Balances
Read your money position per currency in real time, and the balance transactions behind every movement. Balances are derived from a double-entry ledger, never from a mutable counter: every figure you see here is the sum of journal entries you can list one by one.
How balances work
Each currency you have ever collected in has three buckets. pending holds captured collections (net of fees) that have not yet been released by settlement. available is money released to you: payouts and refunds draw on it. locked is money held by reserves in flight, for example a refund that has been created but has not yet settled.
Money moves between the buckets only through balanced ledger journals: a completed collection captures its net amount into pending; the settlement engine releases pending into available on your settlement schedule; creating a payout or refund reserves out of available. Because both endpoints read the same ledger, the balance you see always equals the sum of its balance transactions.
Amounts are decimal strings in each row currency. Zero-decimal currencies such as XAF and XOF are whole numbers, so 194,000 XAF reads "194000", never "194000.00" and never 19400000.
Your API key environment selects the ledger you read: sandbox keys see sandbox balances, live keys see production balances. The two never mix.
List balances
One row per currency, covering every currency your account has ever touched in this environment. No parameters.
The balances:read scope is required.
Example request
curl https://api.capayglobal.com/v1/balances \ -H "Authorization: Bearer sk_test_8fK2q0Zr41Xa"
Example response
{
"status": "success",
"data": {
"object": "list",
"data": [
{
"currency": "XAF",
"available": "194000",
"pending": "45000",
"locked": "3000",
"livemode": false
},
{
"currency": "NGN",
"available": "150000.00",
"pending": "0.00",
"locked": "0.00",
"livemode": false
}
],
"has_more": false
},
"request_id": "b1e2c3d4-5f60-4a71-8b92-c0d1e2f3a4b5"
}Each row of the list carries:
| Field | Type | Description |
|---|---|---|
| currency | string | 3-letter ISO currency of this balance row. |
| available | string | Released money you can pay out or refund from, a decimal string. |
| pending | string | Captured but not yet settled money, a decimal string. |
| locked | string | Money held by in-flight reserves, a decimal string. |
| livemode | boolean | false in sandbox, true in production. |
List balance transactions
Every ledger movement that touched one of your balance buckets, newest first. Each row is one leg of a balanced journal: a settlement release, for example, appears twice, once leaving pending and once arriving in available, both sharing the same journal_reference.
The balances:read scope is required.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| limitoptional | integer | Rows per page, 1 to 100. Defaults to 25. |
| currencyoptional | string | Optional 3-letter ISO filter: only movements in this currency. |
| journal_kindoptional | string | Optional exact match on journal_kind, e.g. settlement_payout to list only the manual settlement payouts. Unknown kinds return an empty list; a value outside ^[a-z_]{3,40}$ (a hyphen, a space, capitals) is refused with 400 validation_error, never silently ignored. |
Example request
curl "https://api.capayglobal.com/v1/balance_transactions?currency=XAF&limit=3" \ -H "Authorization: Bearer sk_test_8fK2q0Zr41Xa"
Example response
{
"status": "success",
"data": {
"object": "list",
"data": [
{
"id": "txn_1042",
"object": "balance_transaction",
"amount": "45000",
"currency": "XAF",
"account": "available",
"journal_reference": "stl:0f9a2c34-6b1d-4e8a-9c21-7d2f5a1b3c4d:release",
"journal_kind": "settlement_release",
"posted_at": "2026-08-31T17:00:01.000Z",
"livemode": false,
"settlement_payout": null,
"fx": null
},
{
"id": "txn_1041",
"object": "balance_transaction",
"amount": "-45000",
"currency": "XAF",
"account": "pending",
"journal_reference": "stl:0f9a2c34-6b1d-4e8a-9c21-7d2f5a1b3c4d:release",
"journal_kind": "settlement_release",
"posted_at": "2026-08-31T17:00:01.000Z",
"livemode": false,
"settlement_payout": null,
"fx": null
},
{
"id": "txn_987",
"object": "balance_transaction",
"amount": "4900",
"currency": "XAF",
"account": "pending",
"journal_reference": "col:7d2f5a1b-3c4d-4e8a-9c21-0f9a2c346b1d:capture",
"journal_kind": "collection_capture",
"posted_at": "2026-08-29T12:35:11.000Z",
"livemode": false,
"settlement_payout": null,
"fx": null
}
],
"has_more": false
},
"request_id": "c2d3e4f5-6a71-4b82-9c03-d1e2f3a4b5c6"
}Each balance transaction carries:
| Field | Type | Description |
|---|---|---|
| id | string | Balance transaction id, prefixed txn_. |
| object | string | Always "balance_transaction". |
| amount | string | Signed decimal string in the row currency. Positive means the bucket went up, negative means it went down. |
| currency | string | 3-letter ISO currency of the movement. |
| account | string | The bucket this movement touched: "available", "pending" or "locked". |
| journal_reference | string | Reference of the double-entry journal this leg belongs to, e.g. col:{id}:capture or stl:{id}:release. Legs of one journal share it. |
| journal_kind | string | What kind of movement posted this. See journal kinds below. |
| posted_at | string | ISO 8601 timestamp of when the journal posted. |
| livemode | boolean | false in sandbox, true in production. |
| settlement_payout | object | null | Set only for journals posted by a manual settlement payout (reference spo:...): the payout id (spo_...), status, method and masked destination. null otherwise. |
| fx | object | null | Set only when that settlement payout was paid in a stablecoin: the executed rate (balance currency per 1 asset unit, a decimal string), rate_unit, asset, network, asset_amount, source (operator_derived or fx_quote), the locked quote when one was attached, and conversion: manual_settlement_only. null otherwise. |
has_more is true when the page is full, meaning older movements exist beyond it. This list is paged by limit only, with no cursor yet: to reach further back, narrow with the currency filter, or reconcile against the collection, payout and refund resources whose ids the journal references carry.
Movements posted by a manual settlement payout carry settlement_payout and, when the payout crossed currencies, fx: Capay Global settles by hand and converts nothing automatically, so the fx line reports the executed rate, never a promise. The How you get paid guide walks through it.
Journal kinds
journal_kind tells you why money moved. The vocabulary is additive: new kinds may appear as the platform grows, so treat unknown values as "other movement" rather than an error.
collection_capture- A completed collection captured its net amount into pending.settlement_release- A settlement batch released pending into available.payout_reserve- A payout was created and reserved its amount out of available.payout_settle- A payout completed and its reserve settled.payout_reverse- A payout failed or was reversed and its reserve returned to available.refund_reserve- A refund was created and reserved its amount.refund_settle- A refund completed and its reserve settled back to the payer.refund_reverse- A refund failed and its reserve was released.settlement_payout- A manual settlement payout debited available; the row carries settlement_payout and, for stablecoin payouts, fx.settlement_payout_reverse- A manual settlement payout failed or was cancelled and its amount returned to available.simfund- Sandbox only: test funds added by the sandbox funding endpoint.
Errors
Errors use the standard envelope: a machine-readable code, a human message, and a next_step. Every error carries the request_id to quote to support.
| Code | HTTP | When it is returned |
|---|---|---|
| invalid_api_key | 401 | The API key is missing, malformed, revoked, expired, or not valid for this environment. |
| insufficient_scope | 403 | The API key lacks the balances:read scope. |
| rate_limited | 429 | The app exceeded its request quota; see the X-RateLimit-* headers. |
| internal_error | 500 | Something failed on our side. Safe to retry: both endpoints are read-only. |