API referencePayoutsOverview

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.

CurrencyPageHow it routes
NGNNGN payoutsNigerian interbank rails (NIP), multi-provider cascade
GBPGBP payoutsInternational account rail (Faster Payments), backed by your GBP virtual account
USDUSD payoutsInternational account rail (ACH or wire), backed by your USD virtual account
EUREUR payoutsInternational account rail (SEPA), backed by your EUR virtual account
CADCAD payoutsTwo 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

  1. beneficiary_id: reference a recipient you saved via POST /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.
  2. 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)

FieldTypeRequiredNotes
amount_minorstring (BigInt)yesMinor units, kobo for NGN, cents for USD/EUR, pence for GBP, cents for CAD. Never a float.
currencystringyesNGN | GBP | USD | EUR | CAD
recipientobjectconditionalInline recipient (currency-specific). Required unless beneficiary_id is supplied.
beneficiary_idstringconditionalSaved beneficiary reference. Alternative to recipient. Must match the payout currency.
wallet_idstringnoAuto-resolved from currency + env if omitted.
merchant_referencestringnoYour own ref. Unique per merchant in a 30-day window.
narrationstringnoDescription shown on the recipient’s statement (NGN).
customer_idstringnoSender attribution, your end-user customer id (see Customers). Surfaces in payout.* webhooks, GET responses, and the customer’s transaction history.
customer_referencestringnoOpaque attribution string (max 64 chars), echoed in webhooks + GETs.
sender_customer_idstringconditionalForeign-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.
senderobjectconditionalInline 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_duplicatebooleannoBypass 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

CodeHTTPCause
missing_field400Required body field absent
invalid_field422Wrong type / format
wallet_not_found422wallet_id doesn’t match merchant + currency + env
unsupported_currency422Currency not active for this merchant
beneficiary_not_found404beneficiary_id unknown / not yours / wrong env
beneficiary_currency_mismatch400beneficiary_id currency ≠ payout currency
fx_features_not_enabled403International accounts not enabled for this merchant (FX currencies)
no_active_international_account403(Paying from your own account) No active merchant international account for this currency
sender_account_not_provisioned422(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_found404customer_id unknown / not yours
customer_id_mismatch400customer_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_customer400(Paying from your own account) Your treasury customer isn’t provisioned yet, contact Technest to set up your international payouts
env_mismatch409customer_id belongs to the other environment
sender_info_required422Your 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_conflict409Same Idempotency-Key, different body
provider_error502Downstream 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/v1

Manage 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 the po_… reference).
  • POST /v1/payouts/{id}/requery: force a provider status refresh for a stuck processing payout.
  • POST /v1/payouts/{id}/cancel: cancel a draft or queued payout.

See any per-currency page for the full payout object, status values, and the management-endpoint details.