Capay Global

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

GET/v1/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
curl https://api.capayglobal.com/v1/balances \
  -H "Authorization: Bearer sk_test_8fK2q0Zr41Xa"

Example response

200 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:

FieldTypeDescription
currencystring3-letter ISO currency of this balance row.
availablestringReleased money you can pay out or refund from, a decimal string.
pendingstringCaptured but not yet settled money, a decimal string.
lockedstringMoney held by in-flight reserves, a decimal string.
livemodebooleanfalse in sandbox, true in production.

List balance transactions

GET/v1/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

ParameterTypeDescription
limitoptionalintegerRows per page, 1 to 100. Defaults to 25.
currencyoptionalstringOptional 3-letter ISO filter: only movements in this currency.
journal_kindoptionalstringOptional 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
curl "https://api.capayglobal.com/v1/balance_transactions?currency=XAF&limit=3" \
  -H "Authorization: Bearer sk_test_8fK2q0Zr41Xa"

Example response

200 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:

FieldTypeDescription
idstringBalance transaction id, prefixed txn_.
objectstringAlways "balance_transaction".
amountstringSigned decimal string in the row currency. Positive means the bucket went up, negative means it went down.
currencystring3-letter ISO currency of the movement.
accountstringThe bucket this movement touched: "available", "pending" or "locked".
journal_referencestringReference 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_kindstringWhat kind of movement posted this. See journal kinds below.
posted_atstringISO 8601 timestamp of when the journal posted.
livemodebooleanfalse in sandbox, true in production.
settlement_payoutobject | nullSet only for journals posted by a manual settlement payout (reference spo:...): the payout id (spo_...), status, method and masked destination. null otherwise.
fxobject | nullSet 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.

CodeHTTPWhen it is returned
invalid_api_key401The API key is missing, malformed, revoked, expired, or not valid for this environment.
insufficient_scope403The API key lacks the balances:read scope.
rate_limited429The app exceeded its request quota; see the X-RateLimit-* headers.
internal_error500Something failed on our side. Safe to retry: both endpoints are read-only.