Saving and synchronizing transactions
Read every page, store transactions by account, and keep your copy aligned with changes and deletions.
Use this guide after creating a connection. The goal is to keep your stored transactions aligned with the current data returned by Open Finance, including updates and records that disappear.
Read the full account history → finish every page → replace that account's local Open Finance records.
Before reading data, use a token issued for the same userId used to create the connection, and wait for the connection to be ACTIVE or COMPLETED. These are the normal successful-collection states. After additional account-owner approvals, ACTIVE can appear before the initial collection; also confirm collection progress and freshness as described in Reading account data.
1. Understand the identifiers
A user can have several connections. Each connection contains accounts, and each account contains transactions. The same physical card connected through two providers has separate source accounts; see Cards connected through two sources.
| API field | What it identifies |
|---|---|
Connection id | The connection. Accounts and transactions refer to it as connectionId. |
Account id | The source account. Transactions refer to it as accountId. |
Transaction id | The transaction record identifier assigned by Open Finance. Use this field to identify the record returned by the API. |
Store the returned account and transaction IDs with your user identity and a source label such as open-finance. Open Finance can preserve id when it matches an incoming record to an existing one, but does not guarantee the same id across every correction or replacement.
Keep one user identity throughout: token userId → connection owner userId → data-access token userId. When renewing a token, use that same userId; do not create a new identity for each connection or data request.
2. Read and save the initial history
- Wait for
ACTIVEorCOMPLETEDand check collection progress and freshness. Do not treat browser return or a collection error as a completed collection. - Read all pages of accounts. For a complete inventory of source accounts, use
GET /data/accounts?includeDuplicates=1. - For each returned account
id, readGET /data/transactions?accountId=...&includeDuplicates=1. OmitdateFromanddateToso you do not restrict the history. - Follow
nextPageuntil it isnull, including after an empty page. Keep the user, account and filters unchanged. - Only after every page succeeds, save that account's full result.
When a new consent is approved, Open Finance starts an initial collection with a one-year historical lookback. The actual start date is approximately one year ago, and the returned history depends on the information available from the provider. Subsequent daily refreshes revisit an overlapping week, as explained below.
For your integration, “full history” means all transactions currently available for the account in Open Finance. Read the stored history from the API, not just the last refresh window. A page-size limit controls each response, not how much history you should keep.
Request and response
Use the same user's access token. Set ACCOUNT_ID to an id returned by the accounts endpoint.
curl --get "$API_BASE/data/transactions" \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--data-urlencode "accountId=$ACCOUNT_ID" \
--data-urlencode "includeDuplicates=1" \
--data-urlencode "limit=100"Illustrative response, showing selected fields and anonymous IDs:
{
"items": [
{
"id": "of-record-a",
"accountId": "account-card-a",
"connectionId": "connection-a",
"status": "BOOKED",
"amount": { "chargedAmount": { "amount": "120.00", "currency": "ILS" } },
"date": {
"transactionDate": "2026-09-01",
"bookingDate": "2026-09-02",
"valueDate": "2026-10-10"
},
"isDuplicate": false
}
],
"count": 1,
"nextPage": null
}If nextPage contains a cursor, repeat the request with that exact value URL-encoded. count is the number of records in the current page.
Understand the transaction dates
The API returns these fields inside date:
| Field | Meaning |
|---|---|
transactionDate | When the transaction or purchase took place. |
bookingDate | When the bank or card issuer recorded the transaction in its books. This can differ from the purchase date. |
valueDate | The value date. For a credit-card transaction, this is the card billing date when that transaction's amount is due to be debited. |
In the example above, the purchase took place on September 1, was recorded on September 2, and is due to be charged on October 10. Use transactionDate to describe when the purchase happened and the card transaction's valueDate to describe when it is billed. Dates are supplied by the provider and may differ or be missing.
The API's dateFrom/dateTo filters use transactionDate, falling back to bookingDate and then valueDate when the preceding field is unavailable. They do not specifically filter card billing dates when transactionDate is present.
3. Replace the account snapshot on each synchronization
An integration that only inserts unknown IDs misses both changes and deletions. Instead, repeat the full read above, then replace the local transactions whose source is Open Finance for that same user and account with the new result. Keep other accounts and data from other sources intact. Keep the previous copy until the replacement is fully saved; if saving fails, retain the previous copy.
If any page fails, keep the previous local data and retry the read. Never replace a full account history with one page or a date-filtered result. A complete, successful empty result can replace the account's copy with an empty set; first confirm the connection and collection state, because ended consent also affects data visibility.
Pagination is not a frozen snapshot across requests. Start after collection finishes, record its status and freshness, and recheck them before replacing local data. If collection starts again, fails, or changes during the read, keep the previous copy and repeat the full read after it finishes.
Initial read and later synchronizations
Use this example inside your backend synchronization function. apiBase includes /v2, and token belongs to userId. replaceOpenFinanceAccount is your own storage function: it must check collection state before writing and keep the previous copy if saving fails.
const transactions = [];
const params = new URLSearchParams({ accountId, includeDuplicates: '1', limit: '100' });
let nextPage;
do {
if (nextPage) params.set('nextPage', nextPage);
const response = await fetch(`${apiBase}/data/transactions?${params}`, {
headers: { Authorization: `Bearer ${token}` }
});
if (!response.ok) throw new Error(`Read failed: ${response.status}`);
const page = await response.json();
transactions.push(...page.items);
nextPage = page.nextPage;
} while (nextPage);
// Save only after every page has been read successfully.
await replaceOpenFinanceAccount({ userId, accountId, transactions });Call this only when the connection is ACTIVE or COMPLETED and collection has finished, with a token for the same userId that owns the connection. Run it for each source account you store. Keeping both sources is useful for inspection, but do not sum both copies of the same card's spending; apply the source-selection rules when displaying totals.
4. Why a BOOKED transaction can disappear
A bank can return a provisional card record with status BOOKED. Later, it may stop returning that record or replace it with an updated record for the same purchase. The replacement may appear in the Open Finance API with a different id. BOOKED is not a promise that the record or its id will never change.
Example of a replacement once reflected in the API:
| Read | id | Status | Amount |
|---|---|---|---|
| Initial | of-record-a | BOOKED | ILS 120.00 |
| Later | of-record-b | BOOKED | ILS 120.00 |
The later complete API result contains of-record-b and no longer contains of-record-a. Insert-only storage keeps both and incorrectly totals ILS 240.00. Replacing the account snapshot leaves one record and ILS 120.00. In other cases the record may be updated while retaining its id; updating existing records is still necessary.
The first full replacement also clears stale local records already absent from the API. Repeating it keeps subsequent updates and removals synchronized. It cannot remove an old record that the API itself still returns; report that case to Open Finance rather than guessing matches from amount and date alone.
5. Bank refresh versus your full-history read
| Process | Scope |
|---|---|
| New consent: Open Finance → bank | Initial historical collection uses a one-year lookback, subject to the provider's available history. |
| Daily refresh: Open Finance → bank | Routine refresh is daily by default, subject to configuration and successful collection. It rereads one week before the last data-coverage date onward, updating records and reconciling removals where supported. A delayed collection can cover more than the last seven days. |
| Your application → Open Finance | Read all currently available history and every page for the account, without date filters, before replacing your local copy. |
The overlapping bank refresh captures recent changes. Where missing-record reconciliation is supported, card records no longer returned within the successfully covered range can be removed, including BOOKED records. This removal is not guaranteed for every integration or every fetch result. Confirm the behavior enabled for your integration with Open Finance; incomplete collection is not evidence that all missing bank records were deleted.
Do not copy the internal one-week window into the full-replacement algorithm. Replacing your account history with only that week would discard older transactions.
If you read a limited period
For periodic reads, include at least one week of overlap before your last successful synchronization's data-coverage date, rather than requesting only new transactions since the previous run. This revisits recent records that may have changed. Read every page and update only the matching local period; never replace the full account history with a partial period.
For example, if your previous data coverage ended on September 20, start the next overlapping read on September 13 or earlier. Use the actual previous coverage date after a delay, not automatically today minus seven days.
A weekly overlap is not a guarantee of complete deletion synchronization. Card refresh and reconciliation can use valueDate, while your API date filters normally use transactionDate. A purchase made earlier can be billed or revised later. Use the full-account replacement flow above when your local copy must reflect all records currently returned by the API, including removals.
Frequently asked questions
Why did a BOOKED transaction disappear or return with a different id?
The bank or card issuer can revise or replace a provisional record. A replacement may receive a new id in the Open Finance API. Synchronize the full account result so your local copy reflects both updates and removals.
Why do I have more transactions locally than the API returns?
Check for insert-only synchronization, incomplete pagination and retained records that have disappeared from the API. Perform one successful full account replacement, then continue using that process.
Will includeDuplicates=0 remove my old local records?
No. It controls supported duplicate-source filtering in the API, not your storage. See includeDuplicates and isDuplicate.
Can I delete records missing from one response page?
No. Finish every page of the full account result first. A missing record on one page is not a deletion signal.
Updated 1 day ago
