# Bank account transaction and statement notifications (/guides/transaction-notifications)



## Overview [#overview]

This table summarizes the key differences between the three sources of transaction information.

|                                                               | Transaction notifications                             | Intraday statements                         | End-of-day statements                                                                              |
| ------------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| Timeliness                                                    | Real-time (\< 1 minute) or near real-time (\< 1 hour) | Multiple times a day (e.g. hourly)          | Once a day                                                                                         |
| Retrieve with [/v1/transactions](/reference/get-transactions) | Yes                                                   | Yes                                         | Yes                                                                                                |
| `dataSource` field                                            | DBS: `ICN`, `IDN`<br />Citibank: `CAMT054`            | DBS: `CAMT052`                              | DBS: `CAMT.053`<br />Citibank: `CAMT053`                                                           |
| Number of webhooks                                            | One per transaction                                   | One or more per statement                   | One per statement                                                                                  |
| Webhook event name                                            | `transactions.created`                                | `transactions.created`                      | `statements.created`                                                                               |
| Webhook body                                                  | Single transaction under `object` field               | Array of transactions under `objects` field | Statement ID under `object.id` field.<br /><br />Retrieve transactions with `GET /v1/transactions` |

## Transaction notifications [#transaction-notifications]

Your bank may provide real-time or near real-time transaction notifications when new transactions
land in your bank accounts. If you are subscribed to transaction notifications, you can retrieve the
transaction details with [/v1/transactions](/reference/get-transactions).
New transactions are available in our API immediately after Acme has received and processed the
transaction notifications from your bank.

Transaction notifications are available for the following banks, and can be identified by the
`dataSource` field. These transaction notifications may not cover all transaction types. Reach out
to [Acme](mailto:support@tryacme.com) for more details.

* DBS: `dataSource=ICN` or `dataSource=IDN`
* Citibank: `dataSource=CAMT054`

### Retrieving transaction details [#retrieving-transaction-details]

You can retrieve transaction details with
[/v1/transactions](/reference/get-transactions).

Request example:

```
GET /v1/transactions?order=DESC&limit=10
```

Response example:

```json
{
  "data": [
    {
      "id": "txn_0CQ8BSQEMZKBB",
      "dataSource": "ICN",
      "transactionType": "PAYNOW",
      "bankReference": "20231011OCBCSGSGBRT1801732",
      "description": "Invoice 123",
      "customerReference": "Invoice 123",
      "remittanceInformation": null,
      "additionalInformation": null,
      "amount": 4200,
      "currency": "SGD",
      "direction": "CREDIT",
      "counterparty": {
        "name": "John Doe",
        "bank": "OCBCSGSGXXX",
        "bankAccountNumber": ""
      },
      "bankAccount": {
        "id": "intacc_0DW79C1K6Z2Y9",
        "bank": "DBSSSGSGXXX",
        "bankAccountNumber": "9876543210"
      },
      "virtualAccountNumber": null,
      "transactionDate": "2023-06-19",
      "statementId": null,
      "createdAt": "2023-06-19T07:37:13.845549Z",
      "updatedAt": "2023-06-19T07:37:13.845549Z"
    }
  ],
  "hasMore": false
}
```

Use the `after` query parameter with the last transaction ID to retrieve newer transactions, if
there are any.

Request example:

```
GET /v1/transactions?order=DESC&limit=10&after=txn_0CQ8BSQEMZKBB
```

If a credit transaction was sent to a Virtual Account number that you own, the Virtual Account
number will be set in the `virtualAccountNumber` field.

Response example:

```json
{
  "id": "txn_0CQ8BSQEMZKBB",
  "bankAccount": {
    "id": "intacc_0DW79C1K6Z2Y9",
    "bank": "DBSSSGSGXXX",
    "bankAccountNumber": "9876543210"
  },
  "virtualAccountNumber": "885123000456",
  "...": "..."
}
```

### Transaction webhooks [#transaction-webhooks]

If you have [registered a webhook endpoint](/guides/webhooks#initial-setup) with Acme, you will receive
webhooks for these transactions with the event name `transactions.created`.

Webhook body example:

```json
{
  "id": "wbh_0DW0YCJGWHWH2",
  "mode": "LIVE",
  "event": "transactions.created",
  "createdAt": "2023-10-11T08:41:56.673471Z",
  "object": {
    "id": "txn_0DW0Y1JXMJQR8",
    "dataSource": "ICN",
    "transactionType": "PAYNOW",
    "bankReference": "20230911UOVBSGSGBRT3173409",
    "description": "Invoice 123",
    "customerReference": "Invoice 123",
    "remittanceInformation": null,
    "additionalInformation": null,
    "amount": 2104,
    "currency": "SGD",
    "direction": "CREDIT",
    "counterparty": {
      "name": "John Doe",
      "bank": "UOVBSGSGXXX",
      "bankAccountNumber": ""
    },
    "bankAccount": {
      "id": "intacc_0CSETTM8Y9SJ9",
      "bank": "DBSSSGSGXXX",
      "bankAccountNumber": "9876543210"
    },
    "virtualAccountNumber": null,
    "transactionDate": "2023-10-11",
    "statementId": null,
    "createdAt": "2023-10-11T08:41:56.594852Z",
    "updatedAt": "2023-10-11T08:41:56.594852Z"
  }
}
```

## Intraday statements [#intraday-statements]

Your bank may provide intraday statements. These statements are available multiple times each day,
and each intraday statement covers new transactions since the previous intraday statement.

If you are subscribed to intraday statements, Acme will send webhooks for the **new** transactions
reported in the intraday statements. Transactions that have already arrived via transaction
notifications will not be processed again. These transactions will also be available at
[/v1/transactions](/reference/get-transactions).

Intraday statements are available for the following banks, and can be identified by the
`dataSource` field.

* DBS: `dataSourc=CAMT052`

Intraday statements may contain a large number of transactions. Instead of a single transaction
object in the webhook's `object` field, you will find one or more transaction objects under the
`objects` field. Each webhook will contain up to 10 transactions (this limit is subjected to future
changes). Hence you may receive multiple webhooks for each intraday statement that Acme processes.

Webhook body example:

```json
{
  "id": "wbh_0DW0YCJGWHWH2",
  "mode": "LIVE",
  "event": "transactions.created",
  "createdAt": "2023-10-11T08:41:56.673471Z",
  "objects": [
    {
      "id": "txn_0DW0Y1JXMJQR8",
      "dataSource": "CAMT052",
      "...": "..."
    },
    {
      "id": "txn_0EH0Y1NX8J6R0",
      "dataSource": "CAMT.052",
      "...": "..."
    }
  ]
}
```

## End-of-day statements [#end-of-day-statements]

End-of-day statements (EOD) from your bank cover all new transactions in your bank since the
previous EOD statement. Each EOD statement typically covers one day's worth of transactions.

When Acme processes these EOD statements, only new transactions (not already received through
transaction notification or intraday statements) will be ingested. EOD statements are available for
the following banks, and can be identified by the `dataSource` field.

* DBS: `dataSourc=CAMT.053`
* Citibank: `dataSource=CAMT053`

### Retrieving new transactions [#retrieving-new-transactions]

New transactions from EOD statements can be retrieved at
[/v1/transactions](/reference/get-transactions). You can filter for them
using the `dataSource` query parameter.

Request example:

```
GET /v1/transactions?dataSource=CAMT.053
```

### Statement webhooks [#statement-webhooks]

If you have registered a webhook endpoint with Acme, you will receive a webhook with the event name
`statements.created` for each of your accounts when its statement has been processed. EOD statements
may contain a large number of transactions. Hence Acme does not send `transactions.created` webhooks
for these transactions.

Webhook body example:

```json
{
  "id": "wbh_0DW112C6G4FWG",
  "mode": "LIVE",
  "event": "statements.created",
  "createdAt": "2023-10-11T08:04:50.673471589Z",
  "object": {
    "id": "stmt_0DW113CKX3S0V",
    "statementDate": "2023-10-10",
    "type": "CAMT.053",
    "bankAccount": {
      "id": "intacc_0DW114DZWY7WJ",
      "bank": "DBSSSGSGXXX",
      "bankAccountNumber": "1234567890"
    },
    "createdAt": "2023-10-11T08:04:50.943436Z",
    "updatedAt": "2023-10-11T08:04:50.943436Z"
  }
}
```

In response to this webhook, you can retrieve all the transactions reported in this statement
with [/v1/transactions](/reference/get-transactions) by filtering with the
`statementId` query parameter. Specify the statement ID found in the `object.id` field of the
webhook message.

Request:

```
GET /v1/transactions?statementId=stmt_0DW113CKX3S0V
```

The response will include transactions previously received through transactions notifications and
intraday statements. To exclude them, use the filter `dataSource=CAMT.053` for DBS or
`dataSource=CAMT053` for Citibank.

Request example:

```
GET /v1/transactions?statementId=stmt_0DW113CKX3S0V&dataSource=CAMT.053
```

### Statements API [#statements-api]

Use the [/v1/statements](/reference/get-statements-id) API to look up the
details of a statement.

Request example:

```
GET /v1/statements/stmt_0DW7JWV8A80EM
```

Response example:

```json
{
  "id": "stmt_0DW7JWV8A80EM",
  "statementDate": "2023-10-11",
  "type": "CAMT.053",
  "bankAccount": {
    "id": "intacc_0DW114DZWY7WJ",
    "bank": "DBSSSGSGXXX",
    "bankAccountNumber": "1234567890"
  },
  "createdAt": "2023-10-11T04:19:44.147038Z",
  "updatedAt": "2023-10-11T04:19:44.147038Z"
}
```

You can retrieve the list of statements available to you with the /v1/statements API.

Request example:

```
GET /v1/statements?order=DESC&limit=10
```

Response example:

```json
{
  "data": [
    {
      "id": "stmt_0DW7JWV8A80EM",
      "statementDate": "2023-10-11",
      "type": "CAMT.053",
      "bankAccount": {
        "id": "intacc_0CNMTDA6BS8BC",
        "bank": "DBSSSGSGXXX",
        "bankAccountNumber": "1234567890"
      },
      "createdAt": "2023-10-11T04:19:44.147038Z",
      "updatedAt": "2023-10-11T04:19:44.147038Z"
    }
  ],
  "hasMore": false
}
```

The `statementDateSince` and `statementDateUntil` query parameters can be used to narrow down the
results.

Request example:

```
GET /v1/statements?statementDateSince=2023-09-15
```

## Deduplication across different data sources [#deduplication-across-different-data-sources]

The same transaction may be reported multiple times. For example, once as a transaction
notification, again in the next intraday statement, and finally in the EOD statement. The
transaction will only show up once in /v1/transactions, and Acme will only send one
`transactions.created` webhook for it.

Acme does this by identifying the same transactions across all data sources: transaction
notifications, intraday statements, and EOD statements.

It is not uncommon for transactions in statements to be missing important fields (e.g. customer
reference), or have certain fields altered (e.g. symbols may be replaced with spaces). In such
scenarios we may fail to match them with previously received transactions, and consequently
double-report them in /v1/transactions (as well as through webhooks). Please reach out to
Acme at [support@tryacme](mailto:support@tryacme.com) when you notice such duplicates, and we will
improve the deduplication logic where possible.

## Implementation guides [#implementation-guides]

### Combining multiple data sources [#combining-multiple-data-sources]

#### Transaction notifications and intraday statements [#transaction-notifications-and-intraday-statements]

You will receive one `transactions.created` webhook message for each new transaction from your
bank's transaction notifications.

If you are subscribed to intraday statements, you will also receive `transactions.created` webhooks
for new transactions in the intraday statements. This excludes transactions already reported by
transaction notifications.

1. Implement a webhook handler for `transactions.created` events.
2. Each webhook message contains either exactly one transaction in the `object` field, or multiple
   transactions within an array in the `objects` field.
3. As webhooks are not guaranteed to be delivered once-and-only-once, use the transaction object IDs
   (e.g. `txn_0FD6S8C7YD1H8`) to detect duplicate webhooks.

#### End-of-day statements [#end-of-day-statements-1]

When an end-of-day (EOD) statement has been processed by Acme, you will be notified by a
`statements.created` webhook. Use the /v1/transactions API to retrieve new transactions from this
EOD statement.

1. Implement a webhook handler for `statements.created` events.
2. Each webhook message contains exactly one statement object in the `object` field corresponding to
   one EOD statement.
3. Use the statement object ID (e.g. `stmt_0FD6WQVHFM23Q`) to retrieve transactions in the EOD
   statement via the /v1/transactions API. Use the `dataSource=CAMT.053` query parameter to exclude
   transactions already reported by transaction notifications and intraday statements.
   ```
   GET /v1/transactions?statementId=stmt_0FD6WQVHFM23Q&dataSource=CAMT.053&order=DESC&limit=10
   ```

### Enabling intraday statements [#enabling-intraday-statements]

If you are currently consuming only transaction notifications and EOD statements, and your bank has
enabled intraday statements, you can start consuming intraday statements by following the strategy
below.

1. **New**: Ensure that your code is able to handle a `dataSource` field with the new value
   `CAMT052` (for DBS intraday statements).
2. In the webhook handler for `transactions.created` event:
   1. If the webhook body has an `object` field, process it as a single transaction in the same way
      as before.
   2. **New**: If the webhook body has an `objects` field instead, iterate through the array and
      process each transaction object.
3. In the webhook handler for `statements.created` event:
   1. Retrieve all new transactions (not previously reported through notifications and intraday
      statements) using:
      ```
      GET /v1/transactions?statementId=stmt_...&dataSource=CAMT.053
      ```
   2. **Optional**: Retrieve the transactions previously processed in Step 2b using:
      ```
      GET /v1/transactions?statementId=stmt_...&dataSource=CAMT052
      ```
      1. For each transaction, determine if it has already been successfully processed in Step 2b.
         Otherwise process it.
      2. This step can be removed once you are confident that Step 2b is correctly handling webhooks
         containing multiple transactions.
4. Once the changes above are in place, contact Acme
   ([support@tryacme.com](mailto:support@tryacme.com)) to enable intraday statements for your
   Acme API account.
