Reading data

What accounts.read lets you call, how money is represented, and what a failed sync tells you.

Kumppanidokumentaatio julkaistaan englanniksi.

#The surface

accounts.read permits:

CallReturns
GET /api/accountsthe user's accounts with balances
GET /api/accounts/{id}/transactions?from&to&limit&offseta page of the statement, newest first
GET /api/connectionsthe bank connections behind the accounts
POST /api/accounts/{id}/syncrefresh one account from the bank; body { "uid": "…" }
POST /api/syncrefresh every account; body { "items": [{ "accountId": "…", "uid": "…" }] }

Everything else answers 403; a revoked or expired key answers 401. Sensitive fields — including the bank-side uid the sync calls need — are encrypted envelopes, so use an SDK: it decrypts locally and posts the uid for you.

js

const accounts = await client.getAccounts();
const page = await client.getTransactions(accounts[0].id, { limit: 50 });
await client.sync(accounts[0].id);

#Money

Inside the envelopes — what the SDKs return — amounts are decimal strings ("1234.56"), never numbers, so no float rounding can creep in. The plaintext amount field on the raw wire is not the value; only the decrypted one is. The sign lives in creditDebitIndicator (CRDT or DBIT), not in the amount; apply it at the presentation edge. Balances carry their ISO 20022 type code (ITBD booked, ITAV available, …).

#Connected but empty

A fresh bank connection starts its statement pull on the server, in the background, and there is no notification when it finishes. Your first read after connecting can legitimately return zero accounts. Render "syncing…" and poll until data lands (cap it — the reference implementation uses 90 seconds), or drive it yourself with syncAll().

#Sync failures

A failed POST /api/accounts/{id}/sync reports the class of failure in the status and a stable reason in a ProblemDetails body, so you branch without parsing prose. POST /api/sync answers 200 and reports the same vocabulary per account in failures[] — except consent_withdrawn: the bulk call leaves withdrawn accounts out silently, so only the per-account call ever answers it.

StatusreasonWhat it meansWhat to do
409reconnect_neededthe consent needs the user againsend them through a reconnect
409consent_withdrawnterminal for this accountremove the connection and authorise afresh
429rate_limitedthe bank is throttlingback off hard; honour Retry-After
502bank_errorthe bank answered, and answered nodo not hammer it
503transientsomething retrying may clearretry with backoff
503partner_app_inactiveyour Enable Banking application is not activatednothing the user can do; activate it

Branch on reason, never on the status — two reasons share 409. Retrying only helps for 429 and 503.

bankErrorCode is present only when the failure originated at the bank and the value it sent is shaped like a code (for example EXPIRED_SESSION); treat it as detail for logs, never as the field you branch on.

#Two kinds of 429

A 429 with a body (reason: "rate_limited") is the bank throttling and carries a deliberately long Retry-After; Enable Banking suspends an application that over-runs, so do not retry sooner. A 429 with no body is the per-IP quota in the reference — slow your loop down.

#Webhooks

There are none yet. Poll GET /api/accounts after a connect and after a sync; keep the interval kind (a page view's worth, not a tight loop).