Skip to content

Payouts

Payouts move money out of a workspace wallet. The API exposes three related operations:

  • Transfers — send to any bank account.
  • Bank lookup — list banks and resolve an account name before sending.
  • Withdrawals — send to a saved beneficiary, using the same ledger path as a transfer but recording the outflow with channel: "payout".

Every payout debits the wallet in the requested currency and creates a ledger transaction you can read from Balances & transactions. NGN payouts are live; the wallet catalog marks EUR and GBP as sandbox and USD/USDT/JPY as coming_soon, so a payout in a coming_soon currency returns 501.

Method Path Scope Success
POST /v1/transfers payouts:write 201
GET /v1/banks read 200
POST /v1/banks/resolve read 200
POST /v1/withdrawals payouts:write 201

POST /v1/transfers

Field Type Required Notes
currency string Yes Uppercased by the API. Must exist in the wallet catalog and not be coming_soon
amount number Yes Major units, minimum 1
account_number string Yes Destination account
bank_code string No Required in practice for NGN bank transfers
account_name string No Sent to the rail and recorded on the ledger row
bank_name string No Display/ledger metadata
narration string No Defaults to Telliport transfer at the rail
idempotency_key string No Ledger-level de-duplication key (see Idempotency)
Terminal window
curl -X POST https://api.nuansis.co/v1/transfers \
-H "Authorization: Bearer $NUANSIS_API_KEY" \
-H "Idempotency-Key: payout-2026-10-18-0007" \
-H "Content-Type: application/json" \
-d '{
"currency": "NGN",
"amount": 50000,
"account_number": "0123456789",
"bank_code": "058",
"account_name": "Adaeze Okafor",
"bank_name": "Guaranty Trust Bank",
"narration": "October salary",
"idempotency_key": "payout-2026-10-18-0007"
}'
201 Created
{
"data": {
"id": 4421,
"type": "debit",
"direction": "out",
"category": "send",
"channel": "transfer",
"status": "completed",
"reference": "snd_9f2c1a7b4e6d8c0a1b",
"idempotencyKey": "payout-2026-10-18-0007",
"businessId": 42,
"walletId": 7,
"currency": "NGN",
"amount": "50000.000000",
"fees": "0.000000",
"title": "Send money",
"description": "October salary",
"provider": "novac",
"providerReference": "nv_8c1e2f3a",
"counterpartyName": "Adaeze Okafor",
"counterpartyAccount": "0123456789",
"counterpartyBank": "Guaranty Trust Bank",
"counterpartyEmail": null,
"metadata": { "provider": "novac", "raw": {} },
"createdAt": "2026-10-18T10:26:07.412Z",
"updatedAt": "2026-10-18T10:26:08.118Z"
},
"meta": { "request_id": "3d4e5f6a-7b8c-4d9e-0f1a-2b3c4d5e6f70" }
}

Transfers and withdrawals return the raw ledger transaction, which uses camelCase keys and string decimals:

Field Notes
id Ledger transaction id
type debit for payouts
direction out
category send for transfers, payout for withdrawals
channel transfer for transfers, payout for withdrawals
status processing, completed, failed, or reversed
reference Nuansis-generated snd_… reference
amount, fees Decimal strings with 6 fractional digits
providerReference The rail’s reference, when one was returned
counterparty* Name, account, bank, and email as supplied or resolved

The transaction row is created with status processing before the rail is called. If the rail throws, the row is marked failed and the API returns the provider error; if it succeeds, the row becomes completed (or failed if the rail reports a failure). This means a failed payout is recorded — it is not erased.

Before calling the rail, the API:

  1. Uppercases and validates the currency against the wallet catalog.
  2. Loads the wallet for that currency. A missing wallet returns an error rather than creating one.
  3. Compares amount with the available balance and fails with 400 invalid_request / Exceeds balance: <available> <currency> if insufficient.
  4. De-duplicates on idempotency_key (or the generated snd_… reference if none was supplied).

Available balance excludes pending holds; see Balances & transactions.

GET /v1/banks?country=NG

{
"data": [
{ "bankCode": "058", "bankName": "Guaranty Trust Bank" },
{ "bankCode": "033", "bankName": "United Bank for Africa" },
{ "bankCode": "011", "bankName": "First Bank of Nigeria" }
],
"meta": { "request_id": "4e5f6a7b-8c9d-4e0f-9a1b-2c3d4e5f6a7b" }
}

country is accepted but the current NGN lookup rail always returns Nigerian banks; it defaults to NG when omitted. Bank fields are camelCase (bankCode, bankName).

POST /v1/banks/resolve

Field Type Required Notes
account_number string Yes Account to resolve
bank_code string Yes Bank from the list above
currency string No Defaults to NGN
Terminal window
curl -X POST https://api.nuansis.co/v1/banks/resolve \
-H "Authorization: Bearer $NUANSIS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "account_number": "0123456789", "bank_code": "058", "currency": "NGN" }'
{
"data": {
"accountName": "ADAEZE OKAFOR",
"accountNumber": "0123456789",
"bankCode": "058",
"bankName": "Guaranty Trust Bank"
},
"meta": { "request_id": "5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c" }
}

Resolve before every payout: the returned accountName is the name to pass as account_name, and it catches typos in the account number before money moves.

POST /v1/withdrawals

Field Type Required Notes
currency string Yes Uppercased; the beneficiary also has a currency
amount number Yes Minimum 1
beneficiary_id integer Yes Id from GET /v1/beneficiaries
narration string No Defaults to Withdrawal to <beneficiary name>
idempotency_key string No Ledger-level de-duplication key
Terminal window
curl -X POST https://api.nuansis.co/v1/withdrawals \
-H "Authorization: Bearer $NUANSIS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"currency": "NGN",
"amount": 25000,
"beneficiary_id": 17,
"narration": "Vendor settlement"
}'

The response is a ledger transaction with category: "payout" and channel: "payout". An unknown or inactive beneficiary returns 404 not_found with message Beneficiary not found.

Status Code Cause
400 invalid_request Insufficient balance, unsupported currency, missing field
401 unauthorized Missing or invalid API key
403 insufficient_scope Key lacks payouts:write
404 not_found Unknown beneficiary, or no wallet exists for the requested currency
429 rate_limited Request budget exhausted
501 coming_soon Currency marked coming_soon in the wallet catalog
503 provider_error Payout rail not configured or unavailable
502 / 504 provider_error Rail failure or timeout