Payouts
Send money to recipients across NGN bank accounts, CAD Interac email IDs or EFT bank accounts, and GBP / USD / EUR bank rails — one endpoint, POST /v1/payouts, with a currency-specific recipient block.
Each currency has its own integration page. A page is self-contained: if you only send GBP, you only need the GBP page.
| Currency | Page | How it routes |
|---|---|---|
| NGN | NGN payouts | Nigerian interbank rails (NIP), multi-provider cascade |
| GBP | GBP payouts | International account rail (Faster Payments), backed by your GBP virtual account |
| USD | USD payouts | International account rail (ACH or wire), backed by your USD virtual account |
| EUR | EUR payouts | International account rail (SEPA), backed by your EUR virtual account |
| CAD | CAD payouts | Two rails — Interac e-Transfer (email-routed) or EFT (Canadian bank account), selected per payout with method |
GBP / USD / EUR / CAD require international accounts to be enabled on your account. The GBP/USD/EUR send gate then depends on your flow (individual vs business): an individual sends from the sender’s own virtual account (sender_account_not_provisioned if missing), a business/treasury merchant sends from its own international account (no_active_international_account if missing). If your account is configured for both flows, the gate follows the payout — see sender attribution. This gate applies to GBP, USD and EUR only, so CAD is exempt on both the Interac and EFT rails. NGN works out of the box. Which requirements apply depends on how Technest has configured your account for that currency — ask us if you’re unsure which applies to you.
Two ways to specify the recipient
beneficiary_id— reference a recipient you saved viaPOST /v1/beneficiaries. Swappr resolves the full recipient details server-side. Preferred for FX (GBP / USD / EUR / CAD): the saved beneficiary captures the address details the receiving network needs, so payouts settle reliably.- Inline
recipient— pass the recipient details on the payout itself. Standard for NGN. Still supported for FX, but soft-deprecated.
When beneficiary_id is set, any inline recipient is ignored.
Common fields (all currencies)
| Field | Type | Required | Notes |
|---|---|---|---|
amount_minor | string (BigInt) | yes | Minor units — kobo for NGN, cents for USD/EUR, pence for GBP, cents for CAD. Never a float. |
currency | string | yes | NGN | GBP | USD | EUR | CAD |
recipient | object | conditional | Inline recipient (currency-specific). Required unless beneficiary_id is supplied. |
beneficiary_id | string | conditional | Saved beneficiary reference. Alternative to recipient. Must match the payout currency. |
wallet_id | string | no | Auto-resolved from currency + env if omitted. |
merchant_reference | string | no | Your own ref. Unique per merchant in a 30-day window. |
narration | string | no | Description shown on the recipient’s statement (NGN). |
customer_id | string | no | Sender attribution — your end-user customer id (see Customers). Surfaces in payout.* webhooks, GET responses, and the customer’s transaction history. |
customer_reference | string | no | Opaque attribution string (max 64 chars), echoed in webhooks + GETs. |
sender_customer_id | string | conditional | FX sender id — required on CAD / GBP / USD / EUR in the individual flow (an inline sender block does not substitute); omit it in the business/treasury flow; optional, and it selects the flow per payout, if your account is configured for both. See sender attribution. |
sender | object | conditional | Inline sender identity for compliance. See sender attribution. |
allow_duplicate | boolean | no | Bypass the beneficiary cool-down for an intentional retry. |
The Idempotency-Key header is required on POST /v1/payouts. Same key + same body returns the cached response; same key + different body returns 409. Keys are scoped per merchant, per environment — a key used in sandbox is free to reuse in live, where it creates a real payout. Keys do not expire. See Idempotency.
Key capability required
The API key must be enabled for payout creation. Tick Create payouts on the key in API keys, or the request returns:
{
"error": {
"type": "permission_error",
"code": "permission_denied",
"message": "This API key isn't enabled for creating payouts. Enable payout.create on the key in API keys."
}
}The capability is set per key, so you can hold a read-only key and a payout-enabled key on the same account. Enabling it requires the payout.create permission — a teammate without it can create the key, but someone with it must switch the capability on. Turning it off needs no permission.
Cross-currency payouts require both this capability and payout.fx on the key — see Cross-currency payouts.
Customer attribution
customer_id + customer_reference link a payout to one of your end-user customers. Once set, the values appear in payout.* webhooks, the payout’s GET response, and the unified GET /v1/customers/{id}/transactions feed — so you can rebuild a customer’s history without storing every event yourself. Both are optional and work on every currency.
Sender attribution (FX)
For FX currencies (CAD / GBP / USD / EUR) the sending customer must be identified for compliance. How you supply the sender depends on your flow.
Individual flow — send sender_customer_id on every payout, referencing a customer you created via POST /v1/customers. It is required: an inline sender block does not substitute for it, and omitting it returns missing_field. Swappr resolves that customer’s stored KYC to satisfy the sender-info requirement and uses it for attribution.
Business / treasury flow — omit sender_customer_id. The sender is your merchant treasury customer, resolved automatically. If only this flow is enabled on your account, sending one anyway returns customer_id_mismatch.
Both flows enabled — if Technest has enabled both flows on your account, sender_customer_id is optional, and it selects the flow per payout. Supply it and the payout is attributed to that end customer and leaves the sender’s own account (sender_account_not_provisioned if they have none). Omit it and the payout is attributed to your treasury customer and leaves your merchant international account (no_active_international_account if you have none). customer_id_mismatch does not apply — supplying a sender is a valid instruction for this configuration, not an error. This is a Technest-configured capability, not a per-request option.
The sender block carries the sender’s identity inline (name, ID type/number, date of birth, country). It is how a business/treasury account supplies identity when its account is configured to require sender info, and it may also accompany sender_customer_id in the individual flow — the two are complementary: inline sender fields take precedence per-field and the referenced customer’s stored KYC backfills any you omit.
If you pass customer_id as well, it must match sender_customer_id (both reference the sending customer) or the request is rejected with customer_id_mismatch.
Common error codes
| Code | HTTP | Cause |
|---|---|---|
missing_field | 400 | Required body field absent |
invalid_field | 422 | Wrong type / format |
wallet_not_found | 422 | wallet_id doesn’t match merchant + currency + env |
unsupported_currency | 422 | Currency not active for this merchant |
beneficiary_not_found | 404 | beneficiary_id unknown / not yours / wrong env |
beneficiary_currency_mismatch | 400 | beneficiary_id currency ≠ payout currency |
fx_features_not_enabled | 403 | International accounts not enabled for this merchant (FX currencies) |
no_active_international_account | 403 | (Business/treasury flow) No active merchant international account for this currency |
sender_account_not_provisioned | 422 | (Individual flow) The sending customer has no active virtual account in this currency — issue one via POST /v1/customers/{id}/virtual_accounts |
customer_not_found | 404 | customer_id unknown / not yours |
customer_id_mismatch | 400 | customer_id ≠ sender_customer_id on an FX payout — or a business/treasury-only account sent a per-payout sender_customer_id (omit it; the sender is your treasury customer). The second cause does not apply to an account configured for both flows |
no_treasury_customer | 400 | (Business/treasury flow) Your treasury customer isn’t provisioned yet — contact Technest to set up your international payouts |
env_mismatch | 409 | customer_id belongs to the other environment |
sender_info_required | 422 | Your account is configured to require sender identity and the merged sender details (inline sender block + the referenced customer’s stored KYC) are still incomplete |
idempotency_key_conflict | 409 | Same Idempotency-Key, different body |
provider_error | 502 | Downstream rail unreachable; safe to retry with the same key |
Per-currency pages list the extra codes specific to each rail.
Base URL
All examples use the production base URL — the environment is selected by your secret-key prefix (sk_test_… vs sk_live_…):
https://api.swappr.me/api/v1Manage payouts
POST /v1/payouts creation is documented per currency. The management endpoints below are currency-agnostic:
GET /v1/payouts— list (cursor-paginated; filter by status / currency / date).GET /v1/payouts/{id}— retrieve (accepts the cuid or thepo_…reference).POST /v1/payouts/{id}/requery— force a provider status refresh for a stuckprocessingpayout.POST /v1/payouts/{id}/cancel— cancel adraftorqueuedpayout.
See any per-currency page for the full payout object, status values, and the management-endpoint details.