Payouts
Payouts send money out to any beneficiary. Naira payouts work out of the box. To send in foreign currencies, your account needs FX capabilities enabled. See Currencies for the supported list. If your account is a licensed money-services or remittance operator, include a sender block on each payout. See Sender information.
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 |
Foreign currencies require FX capabilities enabled on your account (see Currencies). For held currencies, the send gate then depends on which account the payout leaves from: from a customer’s account, funds leave the customer’s own account (sender_account_not_provisioned if missing); from your own account, funds leave your merchant international account (no_active_international_account if missing). If your account can do both, the gate follows the payout: see sender attribution. This gate applies to held currencies 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 | Foreign-currency sender id. Pass it to pay from a customer’s account (required in that case; an inline sender block does not substitute); omit it to pay from your own account; optional, and it selects the source per payout if your account can do both. See sender attribution. |
sender | object | conditional | Inline sender identity for compliance. See Sender information for the full field list and when it applies, and sender attribution for how it interacts with sender_customer_id. |
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.
Recipient receipts
recipient.email is optional on every currency, not only CAD Interac. Pass the recipient’s email address and, when you have turned on recipient receipts, Swappr emails that address a receipt once the payout completes.
Turn it on under Settings → Preferences in the dashboard. It is off by default and applies to live payouts only. The receipt is written in the recipient’s voice (“You’ve received X from your business”), is self-contained (no login, no fee breakdown), and points the recipient back to you for any questions, not to Swappr. A payout with no recipient.email, or one sent while the setting is off, is unaffected.
When present, recipient.email must be a well-formed address of at most 254 characters, on any currency, or the request returns invalid_field (422). CAD Interac still requires it as before.
Sender attribution
For foreign currencies the sending customer must be identified for compliance. How you supply the sender depends on which account the payout leaves from.
From a customer’s account: 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.
From your own account: omit sender_customer_id. The sender is your merchant treasury customer, resolved automatically. If your account can only pay from its own account, sending one anyway returns customer_id_mismatch.
Both: if Technest has enabled both on your account, sender_customer_id is optional, and it selects the source per payout. Supply it and the payout is attributed to that end customer and leaves the customer’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. See Sender information for the full field list and when it applies. It is how an account paying from its own account supplies identity when it is configured to require sender info, and it may also accompany sender_customer_id when you pay from a customer’s account, 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 | (Paying from your own account) No active merchant international account for this currency |
sender_account_not_provisioned | 422 | (Paying from a customer’s account) The sending customer has no active 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 does not match sender_customer_id on a foreign-currency payout, or an account that can only pay from its own 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 that can do both |
no_treasury_customer | 400 | (Paying from your own account) 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.