Skip to main content

Settlement and payouts

When you take a payment for a sub-merchant, the funds land in your treasury. What the merchant is owed is a book entry. This is that book.

What it is

A ledger of obligations, per merchant, per chain, per asset. It holds nothing and can move nothing. Every line is signed: positive increases what you owe the merchant, negative reduces it.

EntryWritten whenSign
captureA payment confirmsPositive
feeBeside a capture, if the merchant has a commissionNegative
payoutYou record a transfer you already madeNegative
adjustmentYou correct somethingEither
reversalA credit is withdrawnNegative

What credits a merchant

A confirmed payment, not a detected one. A detected payment can still be reorganised out of existence, and a merchant credited for one would have to be debited again — which is how a balance nobody can explain comes about.

The amount that arrived, not the amount you asked for. An overpayment is the payer's money and belongs to the merchant.

Nothing at all, if the wrong asset arrived. A payer who sends USDC to an address quoted in USDT has sent real money, and it is swept like any other balance — but it is not credit. Settling an order against an asset the merchant never agreed to price would be worse than leaving it unattributed. Resolve it with an adjustment once you have decided what it was.

Balances are per asset, never one number

curl $CHAINOS/v1/payments/merchants/$MERCHANT/balances -H "X-API-Key: $CHAINOS_KEY"
{ "success": true, "data": [
{ "chain": "bsc", "asset": "USDT", "owed": "1240.50", "decimals": 18 },
{ "chain": "bsc", "asset": "BNB", "owed": "0.42", "decimals": 18 }
]}

USDT on BSC and BNB are not addable, and the rate that would add them is deliberately not on any path in this product. If you settle with merchants in fiat, that conversion is yours and happens after this book.

A balance can be negative. A refund after a payout has gone out leaves a real one, and reporting it as zero would hide the discrepancy in the one place somebody would look.

Recording a payout

curl -X POST $CHAINOS/v1/payments/merchants/$MERCHANT/payouts \
-H "X-API-Key: $CHAINOS_KEY" -H "Content-Type: application/json" \
-d '{ "chain": "bsc", "asset": "USDT", "amount": "1000.00",
"externalReference": "TRF-2026-0142" }'

This does not pay anybody. ChainOS has no payout rail and cannot acquire one without a new signing guard on your Edge — the one control that stops a compromised Cloud redirecting funds. Calling this asserts that you transferred the money on your own rails, and the only thing it changes here is the balance.

externalReference is required and is the idempotency key. Sending it twice records one payout, so a retry after a timeout is safe. Without it a merchant disputing a payout has nothing to quote.

Reconciliation

Two numbers are maintained: the running balance, and the entries it came from. They are written in the same transaction, and an hourly pass compares them.

That redundancy is the whole point. A balance computed on demand is always self-consistent — it is whatever the sum says, including when a writer got it wrong. Two numbers arrived at independently disagree when something is wrong, and nothing else in the system would ever reveal a payment credited to the wrong merchant.

A mismatch is logged at ERROR and never repaired automatically. Overwriting the stored figure with the sum would destroy the only evidence of which of the two writers was wrong, and turn a bug that loses a merchant's money into one that loses it silently. Post an adjustment once you know the cause.

What to reconcile on your side

Join on externalRef, the identifier you gave the merchant when you provisioned them — not on name. Every payment.* webhook carries merchantId, which you stored when you created the merchant.

Daily, the check worth running is: for each merchant and asset, does your own record of what you owe them equal owed here? A drift means one of the two books has a write path the other does not.