Skip to main content

Accounts and balances

A business holds money in currency accounts. Four currencies — EUR, GBP, USD and CAD — are local accounts with their own bank details (an IBAN, a sort code and account number, a routing number); the others are held over SWIFT. Every account is identified by an opaque id such as mockacct_7d2f19c4e8a04b6f9e1c3a5b7d9f0e21_eur, which you pass as source_account_id on payouts and account_id on conversions. Treat it as a string — its shape differs between the sandbox and production rails.

All routes on this page are on the money API: secret key, master key or dashboard session.

Digital-asset accounts (sandbox)

In the sandbox you can also open accounts in USDC, EURC, USDT, USDB, PYUSD and PATHUSD with the same POST /v1/banking/accounts. They are listed here with kind digital in currency_accounts and offered_currencies, carry on-chain identifiers and digital_actions, and their balance adds by_network. See Accounts and wallets.


List accounts​

HTTP
GET /v1/banking/accounts
JSON
{
"accounts": [
{
"id": "mockacct_7d2f19c4e8a04b6f9e1c3a5b7d9f0e21_eur",
"account_holder_name": "Kettlewick Trading GmbH",
"display_name": "EUR account",
"currency": "EUR",
"status": "active",
"identifiers": [{"scheme": "iban", "iban": "DE39500000002907995833", "bic": "MOCKDEFFXXX"}],
"payin_rails": ["sepa", "sepa_instant"],
"opened_at": "2026-01-15T09:00:00Z",
"bank_details": {"local": {"…": "…"}, "international": [{"…": "…"}]},
"standard_qr": [{"scheme": "epc", "block": "local", "payload": "BCD\n002\n1\nSCT\n…"}]
}
],
"refreshing": false,
"primary_currency": "EUR",
"currency_accounts": [
{"currency": "EUR", "kind": "local", "status": "active", "is_primary": true, "created_at": "2026-10-15T17:14:05Z"}
],
"plan": {"tier": "free", "max_named_accounts": 2, "max_currencies": 5, "named_accounts_used": 1, "currencies_used": 1},
"offered_currencies": [{"currency": "EUR", "kind": "local"}, {"currency": "CHF", "kind": "swift"}]
}
  • accounts — the bank accounts in the currencies you hold, with everything a payer needs: identifiers, bank_details (local and international details, or null if the rail gave none), and for EUR an EPC QR payload. The rail may hold more — in production our partner opens all four local currencies on approval — but an account stays out of this list until you hold its currency. At first you hold one: the local currency for the country the business is registered in. Add another with POST /v1/banking/accounts.
  • refreshing — true when accounts is our last known copy and a fresh read from the rail is under way; ask again in a few seconds. In production the list is read from the partner and kept for up to 30 days, refreshed after five minutes.
  • currency_accounts — the currencies you hold or have asked for. status is active or requested; kind is local, swift or, in the sandbox, digital. An entry in accounts has no kind: match it to its entry here by currency.
  • plan — how many local accounts and currencies your plan allows and how many you use.
  • offered_currencies — what you can open: EUR, GBP, USD and CAD as local; CHF, AUD, JPY, NZD, SEK, DKK, NOK, PLN, RON, HUF, SGD, HKD, CNH, THB, AED, SAR, BHD, ILS, TRY, MXN and ZAR as swift. In the sandbox, the digital assets offered as digital too.
  • own_account_payouts on each account — the currencies it may pay to another of your own accounts in, with destination_account_id: ["EUR"] on a USDC account that can send back to your EUR account, and on an EUR account that can convert into your USDC account; [] everywhere else. See Sending.
  • degraded_partners — present only when a rail could not be reached and we held no copy of its accounts. That rail's accounts are then missing from accounts. reason is for diagnosis, not display. With nothing to report, the key is absent — never null or []. When no rail answers at all, the call fails with 503 instead — with production's single rail, that is what you will see. A rail you have no relationship with is not asked, so it is never listed.
  • closing_accounts — accounts being closed: currency, reason (customer or plan_limit), since, convert_to and last_error. They are no longer in currency_accounts.

status on an account is pending, active, suspended or closed.

In production, an account that has not been approved yet has nothing at the rail, and this call can answer 503 service_unavailable rather than an empty list until verification completes.


Opening an account​

HTTP
POST /v1/banking/accounts
Idempotency-Key: open-eur-2026-10
Content-Type: application/json

{"currency": "EUR"}

201 Created:

JSON
{
"currency_account": {"currency": "EUR", "kind": "local", "status": "active", "is_primary": false, "created_at": "…"},
"account": {"id": "…", "currency": "EUR", "identifiers": ["…"], "…": "…"}
}

What happens depends on the rail:

  • Sandbox: the account is opened on the spot and account carries its bank details.
  • Production: our partner opens EUR, GBP, USD and CAD when it approves the business, so adding one of those returns the existing account at once. For any other currency the answer is status: "requested" with no account — it appears in GET /v1/banking/accounts once the partner has opened it.
ErrorWhen
409 conflict"you already hold a EUR account". Also what a retry with the same Idempotency-Key returns — there is no replay of the original 201 on this route.
403 currency_limit_reachedYour plan's currency limit is used up
403 named_account_limit_reachedYour plan's local-account limit is used up (local currencies only)
400 bad_request"XYZ is not a currency we can open accounts in"

Idempotency-Key is required.

Primary currency​

HTTP
PUT /v1/banking/accounts/primary
Content-Type: application/json

{"currency": "GBP"}

200 {"primary_currency": "GBP"}. Only an active local account can be primary — otherwise 422 primary_not_eligible.

Closing an account​

HTTP
POST /v1/banking/accounts/close
Content-Type: application/json

{"currency": "GBP"}

200 with status closed or closing, a step (closed, converting, waiting_for_pending, retrying) and convert_to — any remaining balance is converted into the primary currency before the account closes. You cannot close the primary account (422 primary_account); an account you do not hold is 404. This route is stopped by a platform lockdown.

GET /v1/banking/accounts/plan-impact?plan=starter shows which accounts a plan change would close: {plan, max_named_accounts, max_currencies, accounts_to_close}. plan is one of free, starter, professional, business, enterprise.


Bank details for incoming payments​

HTTP
GET /v1/banking/accounts/{account_id}/payin-instructions
JSON
{
"instructions": [
{
"rail": "sepa",
"currency": "EUR",
"account_holder_name": "Kettlewick Trading GmbH",
"identifier": {"scheme": "iban", "iban": "DE39500000002907995833", "bic": "MOCKDEFFXXX"},
"bank_name": "Mock Bank AG",
"bank_address": "Sandboxplatz 1, 80331 Munich, DE"
}
]
}

One entry per rail that can pay into the account. This is what you give a customer who pays you. In the sandbox the details are simulated and no real payment can reach them.


Balances​

HTTP
GET /v1/banking/accounts/{account_id}/balance
JSON
{
"balances": [
{
"account_id": "mockacct_7d2f19c4e8a04b6f9e1c3a5b7d9f0e21_eur",
"currency": "EUR",
"available": "60.0000",
"pending": "0",
"as_of": "2026-10-15T20:02:01Z"
}
],
"refreshing": false
}
  • Sandbox: the test ledger, exact at the moment of the call.
  • Production: read from the partner and cached. A figure younger than 15 seconds is served as is. An older one is served at once with its as_of time, refreshing: true, and a fresh read started in the background — ask again a moment later. A copy older than a day is not served: that read waits for the partner. A payout, a conversion or an incoming payment drops our copy, but another of our servers may still answer from its own for up to 15 seconds.

Show as_of to anyone who will act on the number.

GET /v1/accounts/me/balance (developer surface, payments:read) is not a bank balance. It is the dashboard's summary, read from our own ledger: {"totals": [{"currency", "amount", "available", "pending"}]} with an EUR and a USD entry, and one per digital asset our ledger holds, under its own code — never folded into EUR or USD; every other currency, GBP and CAD included, is left out, and pending is always 0. A digital-asset account's balance is read from its wallets, not this ledger. Use the per-account balance above.


Statements​

HTTP
POST /v1/banking/accounts/{account_id}/statements
Content-Type: application/json

{"from": "2026-10-01", "to": "2026-10-15"}

201 with a statement: statement_id, the period, the account and holder, opening_balance, total_credits, total_debits, closing_balance, and lines — each with booked_at, kind (deposit, payment, transfer, conversion, test_funds, fee, reversal, adjustment, other), counterparty, reference, amount and balance_after. Statements carry a checksum and sandbox: true|false.

The period may be at most 366 days and may not end in the future (400 bad_request otherwise).

Anyone holding a printed statement — an auditor, a tax advisor, a bank — can check it against our record without credentials:

HTTP
GET /v1/public/statements/{statement_id}

It returns the statement's summary figures and checksum, never its lines.