Book transfers
Move same-currency fiat between accounts under your partner API key (for example one customer’s EUR account to another customer’s EUR account). Source is always a customer-owned fiataccount_id (pcus_*). Destination may be another customer’s (or legacy entity) account id reachable under the same key.
Prerequisite: Customers quickstart. Banking gate: products.deposit_account.status=ready.
Auth and environments
Gates
Source
account_id must belong to {customer_id}. Destination to_account_id must be under the same API key (may belong to another pcus_* or a legacy entity account).
If banking routes are called before ready, expect 409 with data such as deposit_account_status and reason: banking_profile_incomplete.
Happy path
1
Confirm banking ready
Poll
GET /partner/customers/{customer_id} until deposit_account.status=ready2
List accounts
Resolve source fiat
account_id and destination to_account_id via GET …/accounts3
Preview
POST …/book-transfers/preview with to_account_id + amount → preview_token4
Confirm
POST …/book-transfers with preview_token + idempotency_key (tokens expire quickly; use a fresh preview)5
Re-list balances
GET …/accounts on both sides to confirm balances moved1. Resolve accounts
Use ids returned by your API key (do not hardcode from this guide).2. Preview
Preview and confirm must use the same sourceaccount_id. The preview_token seals source + destination internally.
preview_token. Tokens expire quickly; confirm promptly or re-preview if needed.
3. Confirm
idempotency_key on retries after network errors. Confirm success typically includes status: completed (or submitted), id, amounts, customer_id, and fee status final.
Business rules
- Same currency fiat → fiat only (e.g. EUR → EUR).
- Same partner API key: destination must be reachable under this key (another
pcus_*or legacy entity account). - Preview → confirm on the same source
account_id; use a fresh preview if the token expired. - Partner-neutral JSON: error
message/datado not expose upstream PSP names. - Balance: book transfers require
available >= amount(exact balance is allowed). This differs from stablecoin sends, which need amount strictly belowavailable(1 minor-unit headroom). Insufficient balance still returns 422 withdata.available,data.amount, anddata.currencywhen known.
Legacy routes
The same operations exist under/partner/entities/{entity_id}/accounts/.../book-transfers. Those paths are legacy. New integrations should use pcus_* customer paths only.