NGN payouts

Send NGN to any Nigerian bank account. Routed over the Nigerian interbank rails (NIP) through a multi-provider cascade — if one provider can’t reach a bank, Swappr automatically tries the next. Recipient names are auto-resolved from the account number, so you only send account_number + bank_code.

New to payouts? Start with the Payouts overview for the common fields, customer attribution, and management endpoints shared across every currency.

The payout object

{
  "object": "payout",
  "id": "ckxxxxxxxxxxxxxxxxxx",
  "reference": "po_xxxxxxxxxxxx",
  "status": "paid",
  "currency": "NGN",
  "amount_minor": "500000",
  "fee_minor": "75",
  "tax_minor": "0",
  "total_debit_minor": "500075",
  "recipient_name": "ADAEZE BLESSING NWAFOR",
  "recipient_account": "0690000032",
  "recipient_bank_code": "044",
  "wallet_id": "ckwallet_xxx",
  "provider": "<provider_slug>",
  "provider_ref": "<provider_ref>",
  "nip_reference": "100004250505000123456789",
  "merchant_reference": "ORDER_001",
  "narration": "Payroll April 2026",
  "customer_id": null,
  "customer_reference": null,
  "batch_id": null,
  "failure_code": null,
  "failure_message": null,
  "created_at": "2026-05-05T12:34:50.123Z",
  "queued_at": "2026-05-05T12:34:50.456Z",
  "processing_at": "2026-05-05T12:34:51.789Z",
  "completed_at": "2026-05-05T12:34:56.789Z"
}

Status values

StatusDescription
draftCreated via dual-control flow; awaiting team approval
queuedApproved/auto-approved; awaiting dispatch
processingDispatched to provider; awaiting confirmation
paidFunds reached the recipient
paid_manualManually marked paid by Technest support after evidence review
failedProvider returned failed; if applicable, wallet was auto-reversed
failed_manualManually marked failed by Technest support
reversedPaid payout reversed (refund issued)
cancelledCancelled before dispatch
awaiting_admin_reviewHeld by an internal fraud rule; awaiting Technest review

Maker-checker rules

Single payouts that exceed your account’s per-transaction dual-control threshold land in draft status awaiting team approval (approve via the dashboard or your internal tooling that calls our dashboard API).

Live-merchant Owner-only self-approve: on live-approved merchants, only the team member assigned the Owner role can approve their own draft payouts. All other roles drop to maker-checker on live and need a different teammate to approve, even when they hold both payout_create and payout_approve. Sandbox keeps dual-perm self-approve for development velocity. The Owner role is capped at 3 holders per merchant.

Email-OTP gate (dashboard only)

The dashboard’s New Payout and Approve flows can require an email-OTP before submit and/or approve, configured per (merchant, env, currency).

API integrations bypass the OTP gate entirelyPOST /v1/payouts authenticates via Bearer token + IP allowlist + Idempotency-Key, which is the right machine-to-machine shape. The OTP is a dashboard-only control for human-clicked actions.

POSTCreate an NGN payout

POST/v1/payouts

Send NGN to a Nigerian bank account. The Idempotency-Key header is required.

Body parameters
amount_minorstring (BigInt)Required

Minor units (kobo).

currencystringRequired

NGN

recipientobjectConditional

Inline recipient (below). Required unless beneficiary_id is supplied.

beneficiary_idstringConditional

A saved NGN beneficiary. Alternative to inline recipient.

wallet_idstring

Auto-resolved from currency + env if omitted.

merchant_referencestring

Your own ref. Unique per merchant in a 30-day window.

narrationstring

Shown on the recipient’s bank statement.

customer_idstring

Sender/customer attribution — see Customer attribution.

customer_referencestring

Opaque attribution string (max 64 chars).

allow_duplicateboolean

Bypass the beneficiary cool-down for an intentional retry.

NGN does not use sender / sender_customer_id (those are for FX currencies).

Inline recipient

{
  "account_number": "0690000032",
  "bank_code": "044"
}
FieldRequiredNotes
account_numberyes10-digit NUBAN.
bank_codeyes3-digit CBN code OR 6-digit NIP code — either form is accepted. CBN (e.g. 058 for GTBank) is shorter and matches public references; NIP (e.g. 000013) is supported for integrators already speaking NIBSS NIP. Look both up via GET /v1/banks?currency=NGN.
bank_namenoOptional hint. If omitted, Swappr resolves the canonical name and stores it for dashboards + receipts.
namenoOptional. Auto-resolved via NUBAN against the bank-of-record.

Swappr canonicalizes whichever code form you send to the 3-digit form internally before dispatch. Unknown codes return 422 unknown_bank_code with the input echoed; malformed codes (not 3 or 6 digits) return 422 invalid_field.

Tip: call GET /v1/banks?currency=NGN once at integration time, cache the response (it returns both cbn_code and nip_code per bank), and look up by name when building payloads. Daily refresh is plenty.

Errors

CodeHTTPCause
missing_field400Required body field absent
invalid_field422Wrong type / format (e.g. bank_code not 3 or 6 digits)
unknown_bank_code422bank_code not found in the bank list
wallet_not_found422wallet_id doesn’t match merchant + currency + env
recipient_unresolvable422NUBAN cascade exhausted; account invalid at every provider
beneficiary_cooldown422Same recipient paid recently — pass allow_duplicate: true if intentional
limit_violation422Per-tx / daily / per-beneficiary cap exceeded; see error.detail.kind
fraud_rule_blocked422Internal fraud rule blocked the payout; see error.detail.hits
merchant_blocked422Recipient on your merchant blacklist
globally_blocked422Recipient on the platform-wide blacklist (cannot override)
customer_not_found404customer_id unknown / not yours
env_mismatch409customer_id belongs to the other environment
idempotency_key_conflict409Same Idempotency-Key used with a different body
provider_error502Downstream rail unreachable; safe to retry with the same key
Request
curl https://api.swappr.me/api/v1/payouts \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_minor": "500000",
    "currency": "NGN",
    "recipient": {
      "account_number": "0690000032",
      "bank_code": "044"
    },
    "merchant_reference": "ORDER_001",
    "narration": "Payroll April 2026",
    "customer_id": "cust_cmoji8..."
  }'
Request — by beneficiary
curl https://api.swappr.me/api/v1/payouts \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_minor": "500000",
    "currency": "NGN",
    "beneficiary_id": "ben_cmo8x2p9q0...",
    "merchant_reference": "ORDER_002"
  }'

Response

201 Created with the payout object. Status is typically paid in sandbox, or queued / processing in live (transitions to paid when the provider confirms — listen for the payout.paid webhook).

Webhook events

NGN payouts emit payout.processing, then payout.paid or payout.failed (and payout.reversed if a paid payout is later reversed). If you set customer_id / customer_reference, they’re included in the event payload. See Webhooks.


Manage payouts

These endpoints are currency-agnostic — they work the same for NGN and every FX currency.

GETList payouts

GET/v1/payouts

Cursor-paginated. Filter by status, currency, created_after, created_before, limit (1–100, default 50), starting_after.

Response
{
  "object": "list",
  "has_more": true,
  "data": [{ "object": "payout", "id": "...", "...": "..." }]
}
200 OK

GETRetrieve a payout

GET/v1/payouts/{id}

Accepts the cuid (ckxxx) OR the po_xxx reference.

Request
curl https://api.swappr.me/api/v1/payouts/po_da06226542dc44a9 \
  -H "Authorization: Bearer sk_test_..."

POSTRe-query payout status

POST/v1/payouts/{id}/requery

Forces a status refresh by calling the provider directly. Use it when a payout has been stuck in processing unusually long. If the provider returns failed, the wallet is auto-reversed (principal + fee + tax credited back).

POSTCancel a payout

POST/v1/payouts/{id}/cancel

Cancel a payout in draft (no wallet movement) or queued (wallet auto-reversed) status. Body: { "reason": "..." } (required, 3–500 chars). Cannot cancel processing or terminal-status payouts.