Headers
| Header | Required |
|---|---|
x-api-key | yes |
x-timestamp | yes |
x-signature | yes |
authorization | yes — Bearer <JWT> |
content-type | yes |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
idempotency_key | UUID v4 | yes | Reusing the same key returns the original transaction_id instead of creating a duplicate |
country_code | string | yes | ISO 3166-1 alpha-2 (e.g. PH, IN) |
currency_code | string | yes | Destination currency (e.g. PHP, INR) |
payment_method | string | yes | bank_local, imps, etc. — depends on country |
payment_network | string | no | Optional override for which payment network to route through |
source_amount | string | yes | Decimal as a string, e.g. "125.00" |
source_currency | string | no | Defaults to USDT |
recipient_details | object | yes | Required field: name. Other fields depend on payment_method (e.g. account_number, ifsc) |
purpose_of_payment | string | no | Free text |
source_of_funds | string | no | Free text |
Example request
All examples assume you’ve already encrypted the body and signed the request — see the Quickstart for the full helper in Node and Python.
Responses
201 — Success
status is one of:
pending— accepted and queued for processingpending_approval— exceeds your auto-approval limit; awaiting manual approval
400 — validation_error
Schema mismatch — fix and retry.401 — unauthorized / jwt_expired
Bad/missing JWT — re-login.403 — forbidden
KYC required or account suspended.422 — business rule failures
| Code | When |
|---|---|
insufficient_funds | Wallet balance is too low |
daily_limit_exceeded | Today’s volume would exceed your daily cap |
monthly_limit_exceeded | This month’s volume would exceed your monthly cap |
rate_moved | Exchange rate moved more than 0.5% since the quote was issued — retry |
screening_failed | Recipient failed sanctions screening |
429 — rate_limited
Tighten your client-side throttle. Full code list in Errors.Sandbox tip
In sandbox, the trailing cents ofsource_amount decide the eventual status — see Sandbox.