Payouts

The Payouts API allows you to convert crypto assets (BTC, USDT, USDC) into local fiat currencies (e.g., NGN, KES). The flow involves creating a quote, initializing the payout with beneficiary details, depositing funds, and finalizing the transaction.

Payout Flow

Create quote → Initialize (with beneficiary) → Finalize. Use account-lookup, banks/:countryCode, and supported-countries/:country upstream to build a valid Initialize request.

Payouts API Endpoints

Create Payouts Quote

Creates a quote for converting cryptocurrency to fiat. You can specify either amount (crypto amount) or settlement_amount (target fiat amount).

Create Payouts Quote

Create Payouts Quote Request
1
amountstringRequired if settlement_amount not provided

Crypto amount to convert, expressed as a decimal string in the source asset (e.g. '10' = 10 USDC). Takes priority over settlement_amount when both are provided.

2
settlement_amountstringRequired if amount not provided

Target fiat amount you want the beneficiary to receive. Ignored when amount is also provided.

3
countrystringRequired

Two-letter ISO 3166-1 alpha-2 country code for the destination (e.g. 'NG', 'KE', 'GH'). Must be one of the codes returned by GET /api/payouts/supported-countries.

4
from_assetstringRequired

Crypto asset you're debiting from your wallet: 'BTC', 'USDT', or 'USDC'.

5
to_currencystringRequired

Target fiat currency code the beneficiary will receive (e.g. 'NGN', 'KES', 'GHS'). Must be a currency supported on the chosen country.

6
sourcestringRequired

Source of funds (lowercase). 'offchain' debits your Bitnob wallet balance directly; 'onchain' expects a blockchain deposit to cover the quote.

7
chainstringOptional

Blockchain network used when source is 'onchain' (e.g. 'BITCOIN', 'trc20', 'erc20'). Omit when source is 'offchain'.

8
referencestringRequired

Client-generated unique identifier for this quote. Reused on initialize and finalize for idempotency and reconciliation.

Amount vs. Settlement Amount

Provide either amount (crypto side) or settlement_amount (fiat side) — not both. When both are sent, amount takes priority and settlement_amount is ignored.

Create Payouts Quote

Create Payouts Quote Response
1
idstring (UUID)

A globally unique identifier for the payout. Used to reference this payout in future requests.

2
quote_idstring

An internal reference string for the quote, passed into subsequent payout endpoints.

3
company_idstring (UUID)

The unique identifier of the company that created the quote.

4
statusstring

The current state of the quote (e.g., 'QUOTE'). Changes as the payout progresses through its lifecycle.

5
sourcestring

The source of funds used for the payout (e.g., 'OFFCHAIN' for wallet balance, 'ONCHAIN' for blockchain deposit).

6
from_assetstring

The crypto asset being converted (e.g., 'USDT', 'BTC', 'USDC').

7
chainstring

The blockchain network for the transaction (e.g., 'CHAIN_TYPE_ETHEREUM', 'CHAIN_TYPE_BITCOIN').

8
to_currencystring

The target fiat currency code (e.g., 'GHS', 'NGN').

9
amountstring

The original crypto amount provided in the quote request.

10
settlement_amountstring

The exact amount of fiat the recipient will receive after conversion.

11
sat_amountstring

The equivalent amount in satoshis for the requested crypto amount.

12
btc_amountstring

The equivalent BTC amount for the requested crypto amount.

13
feesstring

The fee charged for this payout in the from_asset currency.

14
exchange_rateobject

Contains the conversion rate details: 'rate' (crypto to fiat), 'btc_rate' (BTC to fiat), and 'currency'.

15
referencestring

Your unique reference string for this quote.

16
countrystring

Two-letter ISO country code for the destination.

17
expires_atstring (ISO 8601)

Timestamp indicating when this quote expires. After this time, the quote must be re-generated.

18
created_atstring (ISO 8601)

Timestamp when the quote was created.

Create Payouts Quote - Response

Initialize Payout

Locks the quote, attaches a beneficiary, and moves the payout to INITIATED. The beneficiary object varies by destination_type and country — call GET /api/payouts/supported-countries/:country (and, for bank rails, GET /api/payouts/banks/:countryCode + Account Lookup) to build a valid shape.

Initialize Payout

Initialize Payout - Path Parameters
1
quoteIdstringRequired

The ID of the quote returned by Create Payouts Quote (e.g. 'QT_100003'). Must not be expired.

Initialize Payout - Request Body
1
quote_idstringRequired

The quote you're initializing. Must match the :quoteId path parameter.

2
referencestringRequired

Client-generated unique identifier for this payout. Reused on Finalize for idempotency and reconciliation.

3
payment_reasonstringRequired

Short reason code for the payment (e.g. 'salary', 'vendor_payment', 'family_support'). Some corridors require a value from a fixed list — check Get Country Details.

4
callback_urlstringOptional

HTTPS URL that will receive payout lifecycle webhooks (completed, failed).

5
beneficiaryobjectRequired

Recipient details. Shape depends on destination_type (bank, mobile_money, swift). For SWIFT, populate the bank fields, a nested beneficiary address, and a nested sender identity.

6
beneficiary.destination_typestringRequired

Rail selector: 'bank', 'mobile_money', or 'swift'.

7
beneficiary.countrystringRequired

Two-letter ISO country code of the destination account.

8
beneficiary.account_namestringRequired

Name on the destination account. Use the value returned by Account Lookup when available.

9
beneficiary.account_numberstringRequired

Bank account number (or IBAN for SWIFT corridors).

10
beneficiary.bank_codestringConditional

Provider code from Get Banks. Required for 'bank' and 'mobile_money' destination types.

11
beneficiary.swift_codestringConditional

BIC/SWIFT code of the destination bank. Required when destination_type is 'swift'.

12
beneficiary.bank_namestringConditional

Display name of the destination bank. Required for SWIFT.

13
beneficiary.bank_address / bank_city / bank_post_code / bank_countrystringConditional

Physical address of the destination bank branch. All required for SWIFT.

14
beneficiary.remittance_purposestringConditional

Regulator-visible reason code (e.g. 'salary_payment', 'family_support'). Required for SWIFT.

15
beneficiary.beneficiaryobjectConditional

Physical address of the recipient (country, city, post_code, address). Required for SWIFT.

16
beneficiary.senderobjectConditional

Identity of the sender. type is 'business' or 'individual'. Business senders provide registration_number; individual senders provide date_of_birth and country_of_birth.

Initialize Payout - Request

Initialize Payout - Response
1
successBoolean

Top-level flag indicating whether the request was processed successfully.

2
messageString

Human-readable status message.

3
data.payoutObject

The initialized payout record.

4
data.payout.idUUID

Unique identifier for this payout. Pass it as :id to GET /api/payouts/:id or as the path param on Finalize is keyed off quote_id (see Finalize).

5
data.payout.quote_idString

The quote that was locked (echoed from the request).

6
data.payout.statusString

Lifecycle state. 'INITIATED' after a successful initialize; transitions through processing states to 'COMPLETED' or 'FAILED' after Finalize.

7
data.payout.sourceString

Source of funds (from the original quote). 'SOURCE_UNSET' when source was not explicitly set.

8
data.payout.from_assetString

Crypto asset being debited (e.g. 'USDT', 'USDC', 'BTC').

9
data.payout.to_currencyString

Destination fiat currency (e.g. 'GHS', 'NGN').

10
data.payout.amountString

Crypto amount being sent, as a decimal string in from_asset units.

11
data.payout.settlement_amountString

Fiat amount the beneficiary will receive after conversion.

12
data.payout.btc_amountString

Equivalent amount expressed in BTC for internal pricing.

13
data.payout.sat_amountString

Equivalent amount expressed in satoshis.

14
data.payout.feesString

Total fees applied to the payout, as a decimal string in the source asset.

15
data.payout.cent_feesString

Total fees expressed in cents / smallest fiat unit.

16
data.payout.exchange_rateObject

Locked FX details: rate (from_asset → to_currency), btc_rate (for BTC pricing), currency (display).

17
data.payout.beneficiaryObject

The full beneficiary object echoed back from the request for auditability.

18
data.payout.payment_reasonString

Reason code provided on the request.

19
data.payout.referenceString

Reference provided on the request.

20
data.payout.expires_atDate

RFC 3339 / ISO 8601 timestamp. Call Finalize before this time or re-request a quote.

21
data.payout.created_at / updated_atDate

Set once the payout is fully persisted downstream. May be null immediately after initialize.

22
metadata.request_idString

Request identifier for log correlation and support.

23
timestampDate

Top-level RFC 3339 / ISO 8601 server timestamp of when the response was generated (UTC).

Finalize Before expires_at

Initialize returns data.payout.expires_at. You must call POST /api/payouts/:quoteId/finalize before that timestamp — if it passes, re-run the quote and initialize flow to lock a fresh rate.

Initialize Payout - Response

Account Lookup

Name-resolves a bank or mobile-money account before initiating a payout. Call this after you've chosen a country and bank_code (see Get Banks) to confirm the account number is valid and pre-fill the account_name on your Initialize request.

Account Lookup

Query Parameters
1
countrystringRequired

Two-letter ISO country code of the destination account (e.g. 'NG', 'KE', 'GH').

2
bank_codestringRequired

Bank or mobile-money provider code from GET /api/payouts/banks/:countryCode.

3
account_numberstringRequired

Account or mobile number to resolve.

Account Lookup - Request

Account Lookup Response
1
data.account_nameString

Name registered on the resolved account. Use this value as account_name when constructing the beneficiary on Initialize.

2
data.account_numberString

Echo of the account number that was resolved.

3
data.bank_codeString

Normalized bank code from the underlying rail (may differ from the input bank_code).

4
data.bank_nameString

Display name of the resolving bank or mobile-money provider.

5
data.countryString

Two-letter ISO country code of the resolved account.

6
data.is_verifiedBoolean

True when the rail successfully resolved a name for the account; false when the account couldn't be verified.

Account Lookup - Response

Finalize Payout

Finalizes a payout after the deposit has been confirmed. No request body is required — simply call this endpoint with the quoteId path parameter.

Finalize Payout

Finalize Payout - Path Parameters
1
quoteIdstringRequired

The ID of the quote to finalize.

Finalize Payout - Request

Finalize Payout Response

The response echoes the same data.payout shape as Initialize, with a few settlement-specific additions once the rail has accepted the transfer.

1
data.payout.statusString

Transitions from 'INITIATED' to 'PENDING' on finalize, then on to 'COMPLETED' or 'FAILED' once the rail settles. Watch the webhook for the terminal state.

2
data.payout.sourceString

Populated from the quote (e.g. 'OFFCHAIN', 'ONCHAIN'). 'SOURCE_UNSET' is replaced with the real source at finalize.

3
data.payout.company_idUUID

The company the payout is debited from (populated at finalize).

4
data.payout.cent_amountString

Settlement amount in cents / smallest fiat unit.

5
data.payout.callback_urlString

Echoed from the initialize request — URL that will receive lifecycle webhooks.

6
data.payout.provider_settlement_idString

Identifier returned by the downstream rail (e.g. SWIFT provider / bank). Use it for reconciliation with the provider.

7
data.payout.countryString

Destination country (populated at finalize, e.g. 'GH').

8
data.payout.tripObject

Timestamps of each milestone in the payout's journey: quote_at, initialized_at, and (on completion) finalized_at.

9
data.payout.trip.quote_atDate

When the quote was created.

10
data.payout.trip.initialized_atDate

When the beneficiary was attached and the payout was initialized.

11
data.payout.created_at / updated_atDate

Now populated (not null) — the payout has been persisted through the finalize pipeline.

Baseline Fields

All other fields (id, quote_id, from_asset, to_currency, amount, settlement_amount, btc_amount, sat_amount, fees, cent_fees, nested exchange_rate, echoed beneficiary, reference, payment_reason, expires_at) use the same shape as the Initialize response — refer to that section for per-field explanations.

Finalize Payout - Response

List Payouts

Retrieve historical payouts for your company — quotes, initialized, pending, completed, and failed records all appear here. Supports offset/limit pagination and returns a total_count so you can drive page numbers on your UI.

List Payouts

Query Parameters
1
limitnumberOptional

Maximum number of payouts to return in a single page. Default 20.

2
offsetnumberOptional

Number of records to skip from the start of the result set. Use with total_count to paginate (e.g. offset=20 for page 2 with limit=20).

Response Fields
1
data.itemsArray<Payout>

Page of payout records. Each entry uses the same shape as the Initialize / Finalize response — refer to those sections for per-field explanations.

2
data.total_countInteger

Total number of payouts for your company across all pages.

3
data.limitInteger

Echo of the limit applied to this page.

4
data.offsetInteger

Echo of the offset applied to this page.

5
data.has_moreBoolean

True when offset + limit < total_count — i.e. more records are available beyond this page.

6
data.items[].statusString

Lifecycle state of the payout: 'QUOTE' (quote created, not initialized), 'INITIATED', 'PENDING' (awaiting settlement), 'COMPLETED', or 'FAILED'.

7
data.items[].tripObject

Milestone timestamps. 'QUOTE'-status rows only carry quote_at; initialized_at and finalized_at populate as the payout progresses.

8
metadata.request_idString

Request identifier for log correlation and support.

9
timestampDate

Top-level RFC 3339 / ISO 8601 server timestamp of when the response was generated (UTC).

Quote-Only Rows

Payouts you created but never initialized still show up here with status: "QUOTE". They expire at expires_at and are never rerun — treat them as abandoned quotes for reconciliation purposes.

List Payouts - Response

Get Payout by ID

Retrieve a single payout by its internal id (the UUID returned on Initialize / Finalize, not the quote_id). Returns the full payout record including its current lifecycle status, settlement details, and the beneficiary object that was attached at initialize.

Get Payout by ID

Path Parameters
1
idUUIDRequired

The payout's internal identifier (from data.payout.id on Initialize / Finalize, or data.items[].id on List Payouts). Not the quote_id.

Response

The response shape is identical to Finalize — the wrapped data.payout object with the same settlement, beneficiary, and trip fields. The trip object grows as the payout progresses:

quote_at — set when the quote was created.

initialized_at — set once Initialize succeeds.

processing_start — set when the rail picks up the payout (appears once status: "PROCESSING").

completion_time — set on the terminal COMPLETED or FAILED state.

Field Reference

For per-field explanations, see the Finalize Payout Response section — data.payout.* uses the same shape.

Get Payout by ID - Response

Get Supported Countries

Returns every country you can send payouts to, along with each country's supported currencies and payout rails (destination_types). Use this to populate country pickers in your UI or to discover which corridors are open for a given destination.

Each corridor lists destination_types — values include bank, swift, mobile_money, sepa_eur, domestic_gbp, wire, ach, alipay, wechatpay, paybill, paytill. Combine the destination type with GET /api/payouts/supported-countries/:country to get the exact field schema.

Get Supported Countries

Response Fields
1
data.countriesArray<Country>

Full list of countries payouts are supported in.

2
data.countries[].codeString

ISO 3166-1 alpha-2 country code (e.g. 'GB', 'NG').

3
data.countries[].nameString

Display name of the country.

4
data.countries[].flagString

Country flag emoji for UI rendering.

5
data.countries[].dial_codeString

International dial prefix. Occasionally a comma-separated list when the country has multiple (e.g. DO → '1809,1829,1849').

6
data.countries[].corridorsArray<Corridor>

Supported payout corridors for this country. Each entry pairs a currency with the destination types available for that currency.

7
data.countries[].corridors[].currencyString

ISO 4217 currency code (e.g. 'USD', 'EUR', 'GHS').

8
data.countries[].corridors[].destination_typesArray<String>

Payout rails available for this (country, currency) pair. See the list above for possible values.

Sandbox vs. Live

The country list you get in sandbox is a subset of the production list. Always call this endpoint against the environment you're integrating with before hard-coding corridor support.

Get Supported Countries - Response (excerpt)

Get Country Details

Returns the full beneficiary field schema for a single country — a machine-readable destination_types object keyed by rail (bank, swift, mobile_money, alipay, wechatpay, etc.). Each entry lists required fields, their type / component / pattern / min_length / max_length validators, plus any enum options and per-rail limits.

This is the source of truth you should consult before constructing an Initialize payload. Use it to drive dynamic forms, client-side validation, or per-country payload builders.

Get Country Details

Path Parameters
1
countrystringRequired

ISO 3166-1 alpha-2 country code — must appear in Get Supported Countries (e.g. 'CN', 'NG', 'GB').

Response Fields
1
data.code / data.name / data.flag / data.dial_codeString

Country identity block — same shape as entries in Get Supported Countries.

2
data.destination_typesObject

Map keyed by rail name (e.g. 'bank', 'swift', 'alipay', 'wechatpay', 'mobile_money'). Each value is the full field schema for that rail.

3
data.destination_types[rail].labelString

Human-readable label for the rail (e.g. 'Bank Transfer', 'International Wire (SWIFT)').

4
data.destination_types[rail].fieldsArray<Field>

Ordered list of fields required on the beneficiary for this rail. Each field carries key/label/type/required/component plus optional pattern/min_length/max_length/placeholder/options. Fields of type 'group' contain nested fields; type 'variant' is a discriminated union keyed by variant_key.

5
data.destination_types[rail].banksArray<Bank>

Populated for rails that require a bank selector (e.g. 'bank'). Same shape as Get Banks.

6
data.destination_types[rail].limitsObject

Per-rail amount limits: min_amount, max_amount, currency.

Dynamic Forms

The response is designed to drive auto-generated forms. Each field's component (text, select, date, country_select, fieldset, variant_fieldset) maps one-to-one to a form control; options_ref points at shared enum lists so you don't re-hardcode them per country.

Get Country Details - CN Response

Get Banks

Returns the bank and mobile-money directory for a country — a flat list of providers with their code and display name. Use the returned code as the bank_code input for Account Lookup and as the beneficiary's bank_code on Initialize.

Get Banks

Path Parameters
1
countryCodestringRequired

Two-letter ISO country code (e.g. 'NG', 'KE', 'GH'). Must be one of the codes returned by Get Supported Countries.

Response Fields
1
data.country_codeString

Echo of the ISO country code you requested.

2
data.country_nameString

Display name of the country (e.g. 'Nigeria').

3
data.totalInteger

Total number of bank / provider entries returned (e.g. Nigeria returns 358).

4
data.banksArray<Bank>

Banks and mobile-money providers supported in the country. Typically sorted alphabetically by bank_name.

5
data.banks[].bank_codeString

Provider code — pass this as bank_code on Account Lookup and as the beneficiary's bank_code on Initialize.

6
data.banks[].bank_nameString

Display name of the bank or mobile-money provider (e.g. 'Access bank', 'PAYSTACK PAYMENTS LIMITED').

Get Banks - Response (excerpt)

Get Limits

Returns the minimum and maximum payout amount for every supported (country, currency) corridor, with pre-computed USD equivalents so you can validate amounts client-side before creating a quote.

Get Limits

Response Fields
1
data.limitsArray<Limit>

One entry per (country, currency) corridor. Countries that support multiple currencies (e.g. GB supports both EUR and GBP) return one limit per currency.

2
data.limits[].countryString

ISO 3166-1 alpha-2 country code (e.g. 'NG', 'GH', 'GB').

3
data.limits[].currencyString

ISO 4217 currency code for this corridor (e.g. 'NGN', 'GHS', 'GBP').

4
data.limits[].min_amountString

Minimum amount for a single payout, expressed as a decimal string in the local currency.

5
data.limits[].max_amountString

Maximum amount for a single payout, expressed as a decimal string in the local currency.

6
data.limits[].min_amount_usdString

min_amount converted to USD using the current exchange rate. Useful for validating USD-denominated amounts client-side without calling a conversion endpoint.

7
data.limits[].max_amount_usdString

max_amount converted to USD using the current exchange rate.

Rates Move

The USD equivalents are computed at request time against the live FX rate. Cache the response sparingly — stale USD bounds may drift from what the quote endpoint will accept.

Get Limits - Response (excerpt)

SWIFT Payouts

Cross-border payouts use the SWIFT destination type when the destination country/currency has no local corridor (ACH, SEPA, domestic GBP, mobile money, etc.). This is the fallback wire rail for USD, EUR, HKD, and similar currencies into most countries.

SWIFT flows use the standard three-step sequence — beneficiary details go on Initialize, not on the quote.

Same Envelope As Other Corridors

SWIFT is not a separate endpoint — the quote, initialize, and finalize responses all use the same data.payout envelope you see for bank, mobile_money, ACH, and SEPA corridors. The only architectural difference is destination_type: "swift" (plus the SWIFT-specific fields like swift_code, remittance_purpose, and the discriminated sender variant) on the Initialize beneficiary. Refer to Create Payouts Quote, Initialize Payout, and Finalize Payout above for full per-field explanations — the SWIFT steps below focus on what's SWIFT-specific.

SWIFT Flow

Step 1 — Create Quote

Describes currency, amount, and destination country. No beneficiary info on the quote. The quote is the same shape used for every payout corridor — see Create Payouts Quote above for the full field reference.

Beneficiary Not Allowed Here

The quote body has no beneficiary_info and no destination_type. Both belong on Initialize.

SWIFT Quote - Sample Request
SWIFT Quote - Sample Response

Step 2 — Initialize (the SWIFT payload)

The SWIFT beneficiary lives inside the top-level beneficiary object on POST /api/payouts/:quoteId/initialize. All API fields are lowercase snake_case; the adapter normalises them to the downstream provider's format.

Top-Level Fields
1
beneficiaryobjectRequired

SWIFT payload — see the three tables below. Validated against the corridor's field schema for (country, destination_type=swift).

2
referencestringOptional

Client-set idempotency / tracking reference. Overrides or supplements the quote's reference.

3
payment_reasonstringOptional

Short reason code for the payment (free text at this level).

4
callback_urlstringOptional

Per-payout webhook URL override.

5
customer_idstringOptional

Optional link to a persisted customer record.

6
client_meta_datastringOptional

Arbitrary client metadata string.

beneficiary — Core SWIFT Fields
1
destination_typestringRequired

Set to 'swift' (lowercase). Selects the SWIFT wire rail.

2
countrystringRequired

ISO 3166-1 alpha-2 of the destination account. Must match the country on the quote.

3
account_namestringRequired

Recipient name. Must have ≥2 space-separated parts, each ≥2 chars. No honorifics (Mr/Mrs/Dr). No single-letter-plus-dot (e.g. 'J. Doe' is rejected; 'John Doe' passes).

4
account_numberstringRequired

For IBAN countries (GB, DE, AT, AD, BE, BG, CH, FR, …): IBAN format, 15–34 chars, ^[A-Z0-9]+$. For non-IBAN countries (AR, BW, CN, …): plain account number, 5–34 chars.

5
swift_codestringRequired

8 or 11 chars, ^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$. Uppercase only. HK uses a stricter pattern that pins chars 5–6 to literal 'HK'.

6
bank_namestringRequired

Display name of the destination bank.

7
bank_addressstringRequired

Street address of the destination bank branch.

8
bank_citystringRequired

City of the destination bank branch.

9
bank_post_codestringRequired

Postal / ZIP code of the destination bank branch.

10
bank_countrystringRequired

ISO 3166-1 alpha-2 of the destination bank's country.

11
remittance_purposestringRequired

Lowercase snake_case value from the remittance purposes enum (see reference below).

12
beneficiaryobjectRequired

Recipient's physical address (nested object — see table below).

13
senderobjectRequired

Sender identity, discriminated by `type` ('business' or 'individual'). See table below.

beneficiary.beneficiary — Recipient Address
1
countrystringRequired

ISO 3166-1 alpha-2 of the recipient.

2
citystringRequired

City.

3
post_codestringRequired

Postal / ZIP code.

4
addressstringRequired

Street address.

RW and SG Only

Rwanda (RW) and Singapore (SG) additionally require type on the nested beneficiary — one of "business" or "individual". No other SWIFT-supporting country requires this.

beneficiary.sender — Payer Identity
1
typestringRequired

'business' or 'individual' (case-insensitive, normalised server-side).

2
account_namestringRequired

Legal name of the sender (same name-validation rules as recipient account_name).

3
countrystringRequired

Sender's ISO 3166-1 alpha-2 country code.

4
citystringRequired

Sender's city.

5
addressstringRequired

Sender's street address.

6
post_codestringRequired

Sender's postal code.

7
registration_numberstringConditional

Business registration / tax ID, 2–30 chars. REQUIRED when type='business'; ignored for individuals.

8
date_of_birthstringConditional

YYYY-MM-DD. REQUIRED when type='individual'.

9
country_of_birthstringConditional

ISO 3166-1 alpha-2. REQUIRED when type='individual'.

SWIFT Initialize - Sample Request
SWIFT Initialize - Sample Response

Country-Specific Extras

A handful of countries add required fields beyond the core SWIFT schema. The corridor config at GET /api/payouts/supported-countries/:country is the source of truth.

1
AU — bsb_numberstringRequired for AU

Australia: 6-digit BSB code, pattern ^\d{6}$. The corridor placeholder shows '062-000' but dashes are NOT accepted — strip them ('062000').

2
IN — IFSCodestringRequired for IN

India: 11-char IFSC code, pattern ^[A-Z]{4}0[A-Z0-9]{6}$. Note the key is `IFSCode` (camelCase) — unlike every other field in the payload. Sending `ifs_code` or `ifsc_code` silently fails validation.

3
HK — swift_code patternstring

Hong Kong uses a stricter SWIFT regex: ^[A-Za-z]{4}HK[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$ — chars 5–6 are pinned to literal 'HK'. Case-insensitive on HK only.

4
RW / SG — beneficiary.typestringRequired for RW / SG

Rwanda and Singapore require `type` ('business' or 'individual') on the nested beneficiary object.

Valid remittance_purpose Values

Step 3 — Finalize

No body. Commit the debit and release the payment to the SWIFT rail.

Empty Body

Any body you send on Finalize is ignored. The server only reads the :quoteId from the path.

SWIFT Finalize - Request
SWIFT Finalize - Sample Response

Discovering SWIFT-Enabled Countries

Before building a SWIFT payload, fetch the corridor config for the destination country and look for destination_types containing "swift". The returned schema for that destination type is the source of truth for that country's required fields.

Discover SWIFT Support
SWIFT Corridor Entry (Example)

Validation Failures

http
code
cause
400
INVALID_BODY
Request body isn't valid JSON or doesn't match the handler shape.
400
INVALID_BENEFICIARY_DATA
`country` missing from beneficiary; or a required SWIFT field is missing / fails regex / exceeds length limits. The error message lists each failing field.
410
QUOTE_EXPIRED
Quote TTL elapsed before Initialize or Finalize was called. Run the quote + initialize flow again.
422
UNSUPPORTED_CORRIDOR
(country, destination_type) pair has no schema — e.g. requesting `swift` for a country that doesn't support it.
422
AMOUNT_OUTSIDE_LIMITS
Quote amount is below or above the corridor's min/max limits.
422
INSUFFICIENT_BALANCE
Company balance can't cover the payout when source is `offchain`.
Common Pitfalls

Beneficiary on the quote. It doesn't go there. Quote has no beneficiary_info, no destination_type. Both live on Initialize.

Casing on enums. API is lowercase snake_case for destination_type, remittance_purpose, sender.type. The adapter uppercases them before dispatch — send lowercase.

Casing on SWIFT/BIC. Must be uppercase (DEUTDEFF, not deutdeff). Length is exactly 8 or 11.

Three address blocks. bank_* = destination bank; nested beneficiary.* = recipient's physical address; sender.* = payer's address. All three are independent.

Finalize body. There is none — sending a body is ignored.

country appears twice. Once at the top of beneficiary (corridor lookup key), once nested inside beneficiary.beneficiary.country (recipient's country — often the same value).

Sender variant mismatch. type: "individual" with registration_numberregistration_number is silently ignored. type: "business" without registration_number → validation fails.

Name format. account_name / sender.account_name must have ≥2 space-separated parts, each ≥2 chars. No Mr/Mrs/Dr. No single-letter-plus-dot. J. Doe rejects; John Doe passes.

IFSCode is the one camelCase field. Every other SWIFT field is snake_case; India's IFSC is literally IFSCode. Sending ifs_code / ifsc_code silently fails as "missing required field".

AU BSB has no dashes. Placeholder shows 062-000; regex ^\d{6}$ rejects the dash. Submit as 062000.

Did you find this page useful?