VexaPay API
A single REST API to move money to 40+ countries. Real-time settlement over local rails — PIX, Bre-B, SPEI, ACH, SEPA, and more — with no intermediary banks in the chain.
Quick start
Get from zero to a settled transfer in under 10 minutes. You'll need an API key — request one from your dashboard after KYB verification, or use a sandbox key to start immediately.
1. Install the SDK
2. Initialize the client
3. Create a transfer
Response
Settlement is asynchronous. Listen for the transfer.settled webhook event, or poll GET /transfers/{id} until status is settled.
Authentication
All requests are authenticated with an API key passed in the Authorization header as a Bearer token. Keys are scoped to your organization and available in the dashboard after KYB verification.
Idempotency keys
All mutating requests (POST) accept an Idempotency-Key header. Repeating a request with the same key within 24 hours returns the original response without creating a duplicate transfer — safe to retry on network errors.
Sandbox & testing
The VexaPay sandbox is a fully isolated environment with real API behavior but no actual money movement. All endpoints, webhooks, and settlement flows work identically to production.
Test scenarios
| reference value | outcome |
|---|---|
test-success | Settles in ~3 seconds. Good for happy-path tests. |
test-failure | Fails with invalid_account error. Test your failure handling. |
test-slow | Settles after 60 seconds. Test timeout/polling logic. |
test-fx-shift | FX rate shifts 2% mid-flight. Test FX risk flows. |
test-funds-held | Transfer goes to funds_held for compliance review. |
POST /transfers
Create a new outbound transfer. Returns a processing transfer object. Settlement is asynchronous — monitor status via webhook or polling.
Request parameters
| parameter | type | required | description |
|---|---|---|---|
| amount | integer | required | Amount in the smallest currency unit (cents for USD). |
| currency | string | required | ISO 4217 code. Currently USD only for outbound. |
| destination.country | string | required | ISO 3166-1 alpha-2 destination country code. |
| destination.rail | string | required | Payment rail. See rails reference: SPEI, PIX, BREP, ACH, SEPA_INSTANT, SWIFT… |
| destination.account | string | required | Bank account number, CLABE, IBAN, or PIX key depending on rail. |
| destination.name | string | required | Beneficiary legal name. |
| reference | string | Your internal reference. Passed to the beneficiary bank where supported. | |
| metadata | object | Up to 20 key-value pairs. Returned as-is on the transfer object. |
GET /transfers/{id}
Retrieve a single transfer by its ID. Use this to poll for settlement status if you prefer not to use webhooks.
Transfer status values: processing · settled · failed · funds_held · cancelled
GET /transfers
Returns a paginated list of transfers for your organization, ordered by creation time descending. Use limit (max 100) and starting_after (transfer ID) for cursor-based pagination.
GET /balances
Returns real-time balances across all currencies held in your VexaPay account. available is spendable now; pending is in-flight from incoming transfers.
Webhooks
VexaPay sends HTTPS POST requests to your webhook endpoint when transfer state changes. Configure your endpoint in the dashboard under Settings → Webhooks.
Events
| event | description |
|---|---|
transfer.settled | Transfer settled at the destination bank. settled_at is populated. |
transfer.failed | Transfer failed. Check failure_reason in the payload. |
transfer.funds_held | Transfer paused for compliance review. Contact support. |
balance.low | Account balance dropped below your configured threshold. |
kyb.approved | Your KYB verification was approved — live access unlocked. |
SDK Libraries
Official SDKs are available for all major server-side runtimes. All are open source, maintained by the VexaPay team, and handle authentication, retries with exponential back-off, and webhook signature verification out of the box.
Looking for a runtime not listed? The API is standard REST + JSON — any HTTP client works. Raw integration typically takes under an hour.
Error handling
VexaPay uses standard HTTP status codes. All errors return a JSON body with a machine-readable code and a human-readable message.
| HTTP status | code | description |
|---|---|---|
400 | invalid_params | Request body missing required fields or has invalid values. |
401 | unauthorized | API key missing, malformed, or revoked. |
402 | insufficient_funds | Account balance too low to cover amount + fee. |
422 | invalid_account | Destination account number or CLABE is invalid. |
429 | rate_limited | Too many requests. Back off and retry with exponential delay. |
5xx | server_error | VexaPay-side error. Safe to retry with idempotency key. |