GET /v1/rates) and usdt_amount + fee
(the fixed fee, when configured) is debited from your balance.
This is the full lifecycle, including what happens to your balance at each
step:
1. Discover the available corridors
Countries, currencies and methods are defined by CBPay. Always check the catalog:
Availability may vary; the catalog (
GET /v1/payouts/methods) is always
the source of truth. If a country has a single method, method is
optional. Every method is charged the same way: your rate + fixed fee.
For bank transfers you also need the banks catalog (that is where the
beneficiary’s bank_code comes from):
2. Create the payout
Every payout saves the beneficiary as a contact
automatically (
"save_contact": false to skip it). To pay them again
without re-typing their data, send "beneficiary_contact_id" instead of
beneficiary — their most recent saved beneficiary for that country and
method is used (422 no_saved_destination if there is none).202 Accepted:
total_debit moved
from available into held (on the settlement_asset balance).
Paying from another balance (settlement_asset)
By default the debit comes from your default settlement asset (USDT unless
you change it via PUT /v1/settlement). To pay a single operation from
another balance, add settlement_asset to the request. Example: a 100,000
CLP payout paid from the BTC balance goes through four transformations,
all recorded on the response:
- CLP → USDT at your rate:
100000 / 950.25 = 105.235465 USDT. - + fixed fee:
105.235465 + 0.30 = 105.535465 USDT(total_debit). - USDT → BTC at the effective settlement price (
settlement_rate109029.34070000):105.535465 / 109029.3407 = 0.00096795 BTC(rounded up to the satoshi). - Debit and hold in BTC:
settlement_amount0.00096795leaves your BTC balance; the beneficiary receives their 100,000 CLP exactly as always.
settlement_amount is refunded to your BTC
balance — never re-quoted. If the BTC/GOLD execution price is unavailable
at that moment you get 503 pricing_unavailable, and volatile assets have
a per-operation limit (422 settlement_limit_exceeded; check it in
GET /v1/settlement).
3. Receive the final state
Subscribe to thepayout_status_changed event (webhooks):
completed: the money arrived; the hold is consumed.failed: the full debit is refunded automatically (payout_refundin your ledger).
Payout statuses
Reads and history
Every payout can be read individually and the listing accepts filters:from/to use YYYY-MM-DD (UTC, both inclusive); an invalid date
responds 400 invalid_range.
Examples by country
Every corridor with its exactbeneficiary, the full request and the real
response. Rates (fx_rate) are illustrative — your account’s rates from
GET /v1/rates always apply; the debit is usdt_amount + fee (fixed,
when configured; 0.30 here).
Beneficiary fields per corridor
- Chile
- Peru
- Mexico
- Venezuela
- Bolivia
- Brazil
- Ecuador
- Paraguay
Bank transfer in CLP. Requires RUT, bank and account:The banks catalog (
GET /v1/payouts/banks?country=CL) lists the current
bank_code values.QR payout
In Bolivia (the local interoperable QR) and Brazil (PIX QR, including the “copia e cola” code) you can also pay a collection QR in two steps: scan and confirm. Scanning is free; you are only charged on confirm, exactly like a regular payout (your rate + fixed fee). Withoutcountry/currency Bolivia (BOB) is assumed; for Brazil send
country: "BR" and currency: "BRL".
1. Scan the QR (free)
- Bolivia
- Brazil (PIX QR)
2. Confirm the payment (charged here)
- Bolivia
- Brazil (PIX QR)
usdt_amount + fixed feeis debited at your rate, just like abank_transfer.- The result is synchronous: the response already carries the final
state (
completed, orfailedwith an automatic refund) — no waiting. - Retries with the same
idempotency_keyreturn the original payout. In Bolivia the scan reference is single-use (a scanned QR can only be paid once); in Brazil a static PIX QR is reusable and each payment carries its own key.
Common errors
Immediate rejection vs later failure
If the processor rejects the payout at creation, you receive422 with the
object in status: failed and the refund already applied. If it fails later
(e.g. the destination account does not exist), the webhook arrives with
status: failed and the automatic refund happens at that moment.
Reading status_code on a failed payout
In every case the refund is already applied — verify it with the
payout_refund entry in
movements.
A payout in
processing cannot be cancelled through the API: the rail
already has it. Wait for the final state via webhook or GET — it always
arrives, with an automatic refund on failure.