Push Transaction
Push (payout) transactions transfer funds to a recipient β to a bank account, wallet, PIX key, UPI address, or card.
Endpoint
POST https://{FQDN}/v2/payment
Headers:
| Header | Value |
|---|---|
Authorization | Bearer {accessToken} |
Content-Type | application/json |
The country schema is the contract
Every payout country has its own draft-07 JSON Schema. It decides which paymentMethod fields exist, which are required, the pattern each must match, the allowed walletOperator values, whether recipient.documents is mandatory, and which currency recipientAmount may carry. These rules differ per corridor and change as corridors are added β a recipient hardcoded for one country will be rejected by the next.
Fetch the schema for the destination country and build the request from it:
GET https://{FQDN}/schema/{countryCode}
countryCode is ISO 3166-1 alpha-3 (BRA, IND, PHL). The response covers recipient, recipientAmount, and additionalData β and, on the corridors that constrain it, sender. See Schemas for the full endpoint reference.
senderis present in some country schemas and not others. Where it appears it tightens the sender-side rules beyond the generic push schema; where it is absent, only the generic rules apply. This is being filled in β treat a missingsenderblock as "not yet constrained here", not as "never constrained". Reading the block at request time rather than caching a copy is what keeps you ahead of it.
This is the schema the gateway validates
POST /v2/paymentagainst, so it is the only one that answers "will this payload be accepted?" for a push.
flowchart TD
A[Choose destination country] --> B["GET /schema/{countryCode}"]
B --> C{"paymentMethod.type<br/>offered by the corridor"}
C -->|BANK_DEPOSIT| D["accountNumber + the<br/>fields the schema requires"]
C -->|WALLET| E["walletId + walletType<br/>+ walletOperator"]
C -->|PIX| F["key + keyType"]
C -->|CARD| G["cardTokenId"]
D --> H["Add recipient.documents and<br/>additionalData.paymentPurpose<br/>if the schema requires them"]
E --> H
F --> H
G --> H
H --> I["POST /v2/foreign-exchange<br/>for a quoted fxId"]
I --> J["POST /v2/payment"]
Request Structure
Root Object
| Field | Type | Required | Description |
|---|---|---|---|
externalPaymentId | string | Yes | Your unique payment identifier (idempotency key) |
ipAddress | string | Yes | Originator's IPv4 or IPv6 address |
paymentType | string | Yes | "PUSH" |
amount | object | Yes | Source amount (what you're sending) |
recipientAmount | object | Yes | Destination amount (what the recipient receives) |
sender | object | Yes | Sender details and address |
recipient | object | Yes | Recipient details, address, and payout method |
fxId | string | Yes | FX quote identifier from the Foreign Exchange endpoint |
dynamicDescriptor | string | No | Text to surface on the recipient's statement |
additionalData | object | No | Corridor extras β see additionalData |
Quote an FX rate and pass its
fxIdon every push. It is what binds the payment to the rate you were quoted; without it the gateway prices the conversion itself and the recipient may receive a different amount than the one you showed your user. The same applies toPULLPUSH.
There is no
capturefield on a push. Capture and pre-authorization are pull-side concepts; a push has no authorization to hold.
amount / recipientAmount
| Field | Type | Required | Description |
|---|---|---|---|
total | number | Yes | Amount, minimum 0 |
currency | string | Yes | ISO 4217 code |
amount is the source side, recipientAmount the destination. The destination currency is fixed per country β each country schema pins recipientAmount.currency to an enum, so BRA accepts only BRL and DEU only EUR. The one exception is Guinea (GIN), which accepts GNF or XOF.
sender Object
| Field | Type | Required | Description |
|---|---|---|---|
firstName | string | Yes | Sender's first name |
lastName | string | Yes | Sender's last name |
address | object | Yes | Sender's address |
birthDate | string | Varies | YYYY-MM-DD. Required by KOR |
birthCountryCode | string | Varies | ISO alpha-3. Required by KOR |
sender.address Object
| Field | Type | Required | Description |
|---|---|---|---|
countryCode | string | Varies | ISO alpha-3 country code (e.g., "USA") |
city | string | Varies | City name |
line1 | string | Varies | Street address line 1 |
stateCode | string | No | State abbreviation (e.g., "MA") |
line2 | string | No | Street address line 2 |
zipCode | string | No | Postal/ZIP code |
The generic push schema requires the address object but marks none of its fields required. A number of destination countries override that and require countryCode, city, and line1:
BGD, CHL, CHN, CIV, GIN, IDN, KEN, LKA, MDG, MLI, MYS, NGA, PAK, PHL, SGP, TUR
South Korea (KOR) is the exception in shape: it adds sender.birthDate and sender.birthCountryCode β both pattern-validated β instead of tightening the address. China (CHN) accepts an optional sender.birthDate on top of the address rule.
Populating
countryCode,city, andline1on every push is the safer default. They cost nothing on the corridors that don't demand them, and they are what the list above will keep growing into.
recipient Object
| Field | Type | Required | Description |
|---|---|---|---|
firstName | string | Yes | Recipient's first name |
lastName | string | Yes | Recipient's last name |
paymentMethod | object | Yes | Payout method and details |
address | object | Varies | Required by many corridors β see Country coverage |
documents | array | Varies | Identity documents β required by BRA, CHL, COL, TUR, and by KEN on BANK_DEPOSIT |
email | string | Varies | Required by COL |
phoneNumber | string | Varies | Required by KOR, MNG |
birthDate | string | Varies | Required by KOR |
birthCountryCode | string | Varies | Required by KOR |
The country schema is authoritative on every "Varies" row above.
recipient.documents Array
Each entry:
| Field | Type | Required | Description |
|---|---|---|---|
document | string | Yes | The document number |
type | string | Yes | Document type β the schema's enum is country-specific |
countryCode | string | No | Country that issued the document |
The field is named document, not value. Accepted types by country:
| Country | type values | Required? |
|---|---|---|
Brazil (BRA) | CPF β 11 digits, or formatted 123.456.789-09 | Yes |
Colombia (COL) | CC | Yes |
Turkey (TUR) | ID, PASSPORT, DRIVER_LICENSE | Yes |
Chile (CHL) | RUT | Yes |
Kenya (KEN) | ID, PASSPORT | On BANK_DEPOSIT |
additionalData
| Field | Type | Description |
|---|---|---|
paymentPurpose | string | Regulatory purpose-of-payment code. Required by a number of corridors, and the accepted codes are a country-specific enum β some run to a handful, others to well over a hundred |
statementNarrative | string | Free text describing the transaction |
paymentPurpose is always required for CAN, CHN, COL, EGY, GMB, IDN, IND, KOR, MYS, NPL, PAK, SGP, THA, VNM, and required on BANK_DEPOSIT for BGD and PHL. Read the accepted codes out of the country schema β they are not a shared list, and a code valid in Thailand is not valid in Malaysia.
Several corridors require the
additionalDataobject to be present even when it carries nothing.BRA,MAR, andMEXare the clearest case: the schema demands the key but requires no property inside it, so a payload without"additionalData": {}is rejected withVE_001 β additionalData can't be emptywhile an empty object passes. Country coverage marks these.
Foreign Exchange
For cross-currency push payments, fetch a rate before submitting and pass the returned fxId β the gateway then applies the rate bound to that quote.
Rates differ by payment method, so quote for the method you are about to pay out on. See Foreign Exchange for the full request/response schemas.
Quick example:
curl -X POST https://{FQDN}/v2/foreign-exchange \
-H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' \
-H 'Content-Type: application/json' \
-d '{
"sourceCurrencyCode": "USD",
"destinationCurrencyCode": "BRL",
"paymentMethod": "BANK_ACCOUNT"
}'
{
"fxId": "b2c3d4e5-...",
"conversionRate": 4.975,
"sourceCurrencyCode": "USD",
"destinationCurrencyCode": "BRL",
"paymentMethod": "BANK_ACCOUNT",
"quoteIdExpiryDateTime": "2025-03-31T15:30:00Z"
}
Note: FX quotes have a limited validity period. Check
quoteIdExpiryDateTimeand refresh if expired.
Response
A push is accepted asynchronously. A 200 with status: "PENDING" means the gateway took the payment, not that the recipient was paid:
{
"paymentId": "bda24392-cac5-4fcf-af80-4a29df3ba0ff",
"parentPaymentId": "bda24392-cac5-4fcf-af80-4a29df3ba0ff",
"externalPaymentId": "push-bgd-001",
"amount": 20.0,
"created": "2026-07-15 21:29:50",
"status": "PENDING",
"approved": false,
"captured": false,
"voided": false,
"responseCode": "01",
"automaticReversed": false
}
Poll GET /v2/payment/{externalId} or wait for the webhook for the settled outcome. Do not treat approved: false on a PENDING push as a decline β it reflects that no authorization has been approved yet, which is the normal state of a push in flight.
What's Next
- Payout methods β Field reference for
BANK_DEPOSIT,WALLET,PIX, andCARD - Country coverage β Every corridor and what each requires
- Examples β Complete request bodies, grouped by region
- Errors β
PAY_001andPAY_271
