Pay a bank in Latin America or Asia
Bank payouts to Mexico, Brazil, Colombia, India and Hong Kong are not yet available. Learn what each route will need, and save payees now so they are ready.
Use this recipe to learn what each bank route in Mexico, Brazil, Colombia, India and Hong Kong will need, and to save payees now so they are ready when the route opens. These routes are not yet available.
Who it is for. A marketplace that wants to pay sellers in local currency in Mexico (CLABE), Brazil (PIX), Colombia, India or Hong Kong.
Status. Not yet available. Ask support to enable a country for your business. USD by SWIFT is a different route. See Pay a supplier abroad by SWIFT.
Money example: 100 USDC to Mexico, step by step
- A seller in Guadalajara is owed 100.00 USD of sales. Your customer holds USDC in a Gravv wallet.
- You save the seller as a payee with an 18-digit CLABE and a CURP.
- You send 100.00 from the USDC wallet to that payee. Today the API returns
transfers to MX/MXN are not available at this time, and Gravv debits nothing. - When support enables Mexico for your business, the same payee and the same request pay out in MXN.
Before you start
There is nothing to check in advance. No call tells you whether a country is open, so the transfer's response is the check.
Find out what is required
Each route checks these details when you send, not when you save the payee:
| Country | The payee needs | Fields |
|---|---|---|
| Mexico | An 18-digit CLABE, and a CURP or RFC | payee_type bank_account (always send it, because without it Gravv takes a CLABE for a US account and refuses it for MXN), account_number, national_id, national_id_type (CURP or RFC) |
| Brazil | A PIX key, and a CPF (person) or CNPJ (company) | pix_key (1 to 77 characters, no spaces), national_id, national_id_type |
| Colombia | An account number, the bank, the account type (savings or checking), and a CC or NIT | account_number, institution_id, bank_account_type (checking or savings), national_id, national_id_type |
| India | An account number (9 to 18 digits) and an IFSC | account_number, routing_number set to the IFSC |
| Hong Kong | An account number and the bank | account_number, institution_id. Hong Kong is also missing from GET /v1/transfer/supported-countries |
| All of them | The recipient's address | address.address_line1 (4 to 34 characters), address.city, address.country |
Steps
1. Save the Mexican payee
curl --request POST \
--url https://api.gravv.xyz/v1/external-accounts \
--header 'Api-Key: <API_KEY>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: aa5afdef-78eb-4d44-9ac6-61bd16c95002' \
--data '
{
"customer_id": "30c9ae51-00ce-4616-8eb4-e4d189afa159",
"payee_type": "bank_account",
"currency": "MXN",
"account_name": "Diego Hernandez",
"account_number": "002180000123456789",
"institution_country_iso_code": "MX",
"national_id": "GODE561231HDFRRN09",
"national_id_type": "CURP",
"address": {
"address_line1": "Av Vallarta 1520",
"city": "Guadalajara",
"country": "MX"
}
}
'
const response = await fetch("https://api.gravv.xyz/v1/external-accounts", {
method: "POST",
headers: {
"Api-Key": process.env.GRAVV_API_KEY,
"Content-Type": "application/json",
"Idempotency-Key": "aa5afdef-78eb-4d44-9ac6-61bd16c95002",
},
body: JSON.stringify({
"customer_id": "30c9ae51-00ce-4616-8eb4-e4d189afa159",
"payee_type": "bank_account",
"currency": "MXN",
"account_name": "Diego Hernandez",
"account_number": "002180000123456789",
"institution_country_iso_code": "MX",
"national_id": "GODE561231HDFRRN09",
"national_id_type": "CURP",
"address": {
"address_line1": "Av Vallarta 1520",
"city": "Guadalajara",
"country": "MX"
}
}),
});
console.log(response.status, await response.json());
import os
import requests
response = requests.post(
"https://api.gravv.xyz/v1/external-accounts",
headers={
"Api-Key": os.environ["GRAVV_API_KEY"],
"Content-Type": "application/json",
"Idempotency-Key": "aa5afdef-78eb-4d44-9ac6-61bd16c95002",
},
json={
"customer_id": "30c9ae51-00ce-4616-8eb4-e4d189afa159",
"payee_type": "bank_account",
"currency": "MXN",
"account_name": "Diego Hernandez",
"account_number": "002180000123456789",
"institution_country_iso_code": "MX",
"national_id": "GODE561231HDFRRN09",
"national_id_type": "CURP",
"address": {
"address_line1": "Av Vallarta 1520",
"city": "Guadalajara",
"country": "MX",
},
},
)
print(response.status_code, response.json())
{
"data": {
"id": "2ce0729d-3b00-4b22-8967-58f801966ffc",
"payee_type": "bank_account",
"account_number": "002180000123456789",
"account_name": "DIEGO HERNANDEZ",
"customer_id": "30c9ae51-00ce-4616-8eb4-e4d189afa159",
"currency": "MXN",
"institution_country_iso_code": "MX",
"address": {
"city": "Guadalajara",
"address_line1": "Av Vallarta 1520",
"country": "MX"
},
"status": "active",
"date_created": "2026-10-13T13:01:12.300Z",
"national_id_type": "CURP",
"national_id_masked": "**************RN09"
},
"error": null
}
Gravv never returns the full national_id, only national_id_masked.
2. Add missing details to a payee you already have
To complete a Brazilian payee saved earlier without a PIX key and CPF, use PATCH /v1/external-accounts/{external_account_id}. A field you leave out keeps its value, and null clears a field. This call does not need an Idempotency-Key, and it returns HTTP 202.
curl --request PATCH \
--url https://api.gravv.xyz/v1/external-accounts/8e65cb04-10e2-4faa-b365-e1e03942b846 \
--header 'Api-Key: <API_KEY>' \
--header 'Content-Type: application/json' \
--data '
{
"pix_key": "maria.souza@example.com",
"national_id": "529.982.247-25",
"national_id_type": "CPF",
"address": {
"address_line1": "Rua Augusta 1200",
"city": "Sao Paulo",
"country": "BR"
}
}
'
const response = await fetch("https://api.gravv.xyz/v1/external-accounts/8e65cb04-10e2-4faa-b365-e1e03942b846", {
method: "PATCH",
headers: {
"Api-Key": process.env.GRAVV_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
"pix_key": "maria.souza@example.com",
"national_id": "529.982.247-25",
"national_id_type": "CPF",
"address": {
"address_line1": "Rua Augusta 1200",
"city": "Sao Paulo",
"country": "BR"
}
}),
});
console.log(response.status, await response.json());
import os
import requests
response = requests.patch(
"https://api.gravv.xyz/v1/external-accounts/8e65cb04-10e2-4faa-b365-e1e03942b846",
headers={
"Api-Key": os.environ["GRAVV_API_KEY"],
"Content-Type": "application/json",
},
json={
"pix_key": "maria.souza@example.com",
"national_id": "529.982.247-25",
"national_id_type": "CPF",
"address": {
"address_line1": "Rua Augusta 1200",
"city": "Sao Paulo",
"country": "BR",
},
},
)
print(response.status_code, response.json())
Read the payee back to confirm national_id_type and national_id_masked. Gravv never returns the PIX key.
3. Try the transfer
This example sends from the customer's USDC wallet, as in the money example. You can also skip this step until support confirms the country.
curl --request POST \
--url https://api.gravv.xyz/v1/transfer \
--header 'Api-Key: <API_KEY>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: d440eeab-474f-4c59-9bf5-301c1e3e2ef7' \
--data '
{
"customer_id": "30c9ae51-00ce-4616-8eb4-e4d189afa159",
"source": {
"source_type": "internal_crypto_wallet",
"id": "3a8f1c2d-5b7e-4d9a-8c6f-0e2b4d6f8a15",
"crypto_asset": "USDC"
},
"destination": {
"destination_type": "external_account",
"id": "2ce0729d-3b00-4b22-8967-58f801966ffc"
},
"amount": "100.00",
"description": "Seller payout October",
"client_reference": "mkt-mx-payout-5521"
}
'
const response = await fetch("https://api.gravv.xyz/v1/transfer", {
method: "POST",
headers: {
"Api-Key": process.env.GRAVV_API_KEY,
"Content-Type": "application/json",
"Idempotency-Key": "d440eeab-474f-4c59-9bf5-301c1e3e2ef7",
},
body: JSON.stringify({
"customer_id": "30c9ae51-00ce-4616-8eb4-e4d189afa159",
"source": {
"source_type": "internal_crypto_wallet",
"id": "3a8f1c2d-5b7e-4d9a-8c6f-0e2b4d6f8a15",
"crypto_asset": "USDC"
},
"destination": {
"destination_type": "external_account",
"id": "2ce0729d-3b00-4b22-8967-58f801966ffc"
},
"amount": "100.00",
"description": "Seller payout October",
"client_reference": "mkt-mx-payout-5521"
}),
});
console.log(response.status, await response.json());
import os
import requests
response = requests.post(
"https://api.gravv.xyz/v1/transfer",
headers={
"Api-Key": os.environ["GRAVV_API_KEY"],
"Content-Type": "application/json",
"Idempotency-Key": "d440eeab-474f-4c59-9bf5-301c1e3e2ef7",
},
json={
"customer_id": "30c9ae51-00ce-4616-8eb4-e4d189afa159",
"source": {
"source_type": "internal_crypto_wallet",
"id": "3a8f1c2d-5b7e-4d9a-8c6f-0e2b4d6f8a15",
"crypto_asset": "USDC",
},
"destination": {
"destination_type": "external_account",
"id": "2ce0729d-3b00-4b22-8967-58f801966ffc",
},
"amount": "100.00",
"description": "Seller payout October",
"client_reference": "mkt-mx-payout-5521",
},
)
print(response.status_code, response.json())
{
"data": null,
"error": "transfers to MX/MXN are not available at this time"
}
Brazil, Colombia, India and Hong Kong return the same error, with their own country and currency. Gravv debits nothing.
What can go wrong
| Response | Why | Fix |
|---|---|---|
400 transfers to MX/MXN are not available at this time (or BR/BRL, CO/COP, IN/INR, HK/HKD), or … are not supported at this time |
The route is not yet available | Contact support to enable it, and keep the payee |
400 the payee is missing required information for this transfer type — please update the payee details |
The route cannot pay this payee yet | Contact support with the payee id |
400 MXN not supported for ach/wire external accounts: invalid input |
You sent a CLABE without payee_type, so Gravv took it for a US account |
Send "payee_type": "bank_account" |
400 CLABE checksum invalid (expected check digit X, got Y): invalid input |
The CLABE is mistyped | Enter the 18 digits again |
400 national id type CPF is not accepted for MX payees (expected CURP, RFC) |
The id type is wrong for the country | Send CURP or RFC for Mexico |
400 national id is not a valid CPF (11 digits with valid check digits) |
The CPF is mistyped | Fix it. Gravv removes dots and dashes for you |
400 national id and national id type must be provided together |
You sent only one of the two | Send both |
400 pix key cannot contain whitespace or control characters |
The key has spaces | Remove the spaces |
400 this payee is missing details required for this transfer (<list>); update the payee and retry |
The route is enabled, but a detail from the table is missing | PATCH the payee with the listed fields, then send again with a new key |
… cannot be changed on this payee; create a new payee with the complete details and retry |
The missing field cannot be changed on this payee | Save a new payee |
409 the address of payee … cannot be changed: it is already registered for payouts; create a new payee with the new address |
The address is locked | Save a new payee |
Webhooks you will receive
You receive external_account.created for each payee. You receive no transfer events, because Gravv creates no transfer while the route is closed.
Testing in sandbox
In sandbox, saving payees for Mexico, Brazil, India and Hong Kong works as described, and so does the PATCH.
- Brazil and Hong Kong return
not available at this time. - Mexico and India return
the payee is missing required information for this transfer type — please update the payee detailsinstead.
Either error means you cannot pay the payee yet, and Gravv debits nothing.