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).
bank_reference — the bank’s own id for the transfer. On US ACH, wire
and SWIFT it is the CBF reference returned immediately at create (the
payout stays processing until the bank confirms). On other corridors it
stays empty ("") until the payout is completed. It is the value the
beneficiary can use to cross-check the payment with their bank, and it also
appears in the payout_status_changed webhook, the PDF receipt, the payouts
CSV export and the statement. Payout responses, webhooks, CSV and receipts also expose uetr (UETR/SWIFT gpi) and imad (domestic wire/ACH), both empty until reported by the bank; use the public transaction tracker for live status and the same receipt.Paying from another balance (settlement_asset)
The default debit comes from USD; use PUT /v1/settlement to change it. 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/SILVER/PLATINUM 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).
For same-currency BOB, MXN or ARS settlement, see Fiat payout settlement.
Technical screening can be pending
CBPay screens the beneficiary before it debits your balance or calls the core. If the screening service is temporarily unavailable and the technical screening queue is available, the payout is persisted and the create call returns202 Accepted with the payout resource in status: "pending":
receipt_url. Replaying with the same idempotency key returns the same payout
with idempotency_hit: true; it never creates a second operation. When the
screening worker receives process, the existing payout enters the normal debit
and dispatch flow. A valid hold goes to the transactional firewall. A later
rejected result ends the queued payout as failed with no debit.
After process is accepted, the same payout can remain status: "pending"
while the debit and dispatch are reconciled. The API and
payout_status_changed webhook expose these durable transition codes:
compliance_dispatch_pending: screening passed, the debit/hold exists andfunds_debited: true, but the core dispatch has not completed.compliance_dispatching: the dispatch claim is in progress.core_unreachable: the dispatch or local settlement is ambiguous and is waiting for reconciliation; do not create a second payout.
idempotency_key, read the payout, and wait for the next status event. Once the
payout is final, completed consumes the hold and failed refunds the exact
debit. Final status events use the durable event path and include the normal
receipt fields.
The technical compliance_check_unavailable response remains applicable when
the payout cannot be placed in the pending queue, and to flows that do not have
this queue. It does not replace the existing validation, security or
compliance_hold contracts.
Pre-debit failure is not a refund
If screening or a hard compliance gate fails before the debit path starts, the same payout can finish asfailed with predebit_failure: true and
funds_debited: false. The response and its payout_status_changed event omit
receipt_url, because there was no debit, hold or receipt to reverse. This is
different from a post-debit failed payout, where the exact debit is refunded
before the terminal event.
For rate provenance and indicative FIFO quotes, see Payout rate sources and indicative FIFO quotes.
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).
status_message is the optional, normalized detail accompanying
status_code; it can be empty when the core has no additional message.
You can also query at any time:
Payout statuses
Reads and history
Every payout can be read individually and the listing accepts filters:from/to use YYYY-MM-DD (organization timezone, both inclusive); an invalid date
responds 400 invalid_range. method filters by the live payout catalog, while
search performs a case-insensitive partial match on the payout ID, bank reference
or idempotency key.
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
- Argentina
- United States
Bank transfer in CLP. Requires RUT, bank and account: the Chilean peso has no minor unit, so CLP payout amounts must be whole numbers (for example, The banks catalog (
30000 or 30000.00).GET /v1/payouts/banks?country=CL) lists the current bank_code values. CL/CLP/bank_transfer may be dispatched in grouped five-minute windows; POST /v1/payouts still returns 202 with status: "processing" and each payout keeps its final status and payout_status_changed webhook.SEPA Instant in EUR
See the dedicated SEPA Instant payout guide for the single Europe (EU)/EUR/sepa routing row; the beneficiary’s actual country
comes from the IBAN. GB IBANs fail fast with HTTP 400 before dispatch; SEPA Credit
Transfer for GB is future scope.
Mandatory supporting document on USD transfers
EVERY outbound USD transfer over a bank rail (ach, wire or
swift) requires an attached supporting document (invoice or
receipt) — it is a requirement of the processing bank and applies to
any USD corridor, not just the US one (for example, also to an
international SWIFT such as PY/USD/swift).
1
Upload the document
Send the file with
POST /v1/payouts/documents: raw binary body with
a PDF (application/pdf, up to 50 MB) and the filename in the name
query param. The current US/USD payout UI accepts PDF only.2
Pass it when creating the payout
The
document_key travels in options.supporting_document_key. On
ach/wire you can add options.document_reference_number (invoice
or document reference number) — it is sent to the bank.- A USD bank-rail payout created without the document is rejected at
creation with
400 supporting_document_required(no debit). - A key uploaded by another account is rejected with
400 invalid_document_key. - The
document_keystays stored: if the create fails for another reason, you reuse it on the retry — no need to re-upload the file.
QR payout
Paying a collection QR (Bolivia, Brazil PIX) now has its own guide:QR payout
Scan the QR for free, show your user the recipient’s data and confirm the
payment in a second call - charged like a regular payout.
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.FAQ
When is my balance debited?
When is my balance debited?
At creation: the payout debits and holds the funds immediately. If the
payout fails, the exact debited amount (fee included) is refunded
automatically.
Can I cancel a payout in processing?
Can I cancel a payout in processing?
No — once dispatched to the rail it resolves to
completed or failed on
its own. Subscribe to payout_status_changed for the final state.Which FX rate does my payout use?
Which FX rate does my payout use?
The rate quoted at creation (returned as
fx_rate), frozen for that
operation. Your agreed spread is already inside the rate.Can I pay from a balance other than USDT?
Can I pay from a balance other than USDT?
Yes — set a per-account default (
PUT /v1/settlement) or override per
payout with settlement_asset (USDC, BTC, GOLD). Refunds return the exact
settled amount, never re-quoted.What does status: pending with compliance_pending: true mean?
What does status: pending with compliance_pending: true mean?
The beneficiary screening is temporarily unavailable, but CBPay persisted the
payout in its technical screening queue. No balance was debited, no hold or
ledger entry exists, the core was not called and there is no
receipt_url yet.
Replay with the same idempotency key returns the same resource. Wait for the
payout_status_changed event or poll the payout; it will continue after
process, enter the firewall for a valid hold, or become failed without a
debit if the result is rejected.What does compliance_hold (403) mean?
What does compliance_hold (403) mean?
The beneficiary failed the compliance screening: the payout was not
created and your
idempotency_key was not consumed. Review the beneficiary
data or contact your CBPay team.How do I retry safely after a timeout or 5xx?
How do I retry safely after a timeout or 5xx?
Retry with the same
idempotency_key: you get the original payout
back (idempotency_hit: true) — never a duplicate. A new key is a new,
independent payout.Why is my US ACH/wire/SWIFT payout still processing?
Why is my US ACH/wire/SWIFT payout still processing?
US bank-rail payouts are paid by the operator by hand. Create already
returns the CBF in
bank_reference and stays processing until the bank
confirms. Listen to payout_status_changed for the final state; a failure
refunds the debit automatically.422 destination_recently_rejected retry sequence, see
Errors and Destination rejection protection.
US/USD payment purpose and validation
ForUS/USD bank-rail payouts (ach, wire and swift), options.purpose
is mandatory. The API accepts either the visible label or its code,
case-insensitively. The accepted catalog is:
beneficiary.account_type is required for both ach and wire and must be
checking or savings (case-insensitive). swift does not use this field.
For wire and swift, amounts below USD 25 return
422 amount_below_minimum. Annex-B beneficiary or bank countries (CU, IR,
KP, SY) return 422 prohibited_country. These checks happen before any
debit.
The normalized validation errors are invalid_beneficiary,
invalid_amount, amount_below_minimum, prohibited_country,
purpose_required, invalid_options and
supporting_document_required. Read message for the exact field to fix.
Do not retry a rejected request unchanged; after a timeout or 5xx, reuse the
same idempotency key.