Skip to main content

Overview

The Payments API lets your platform move money out of a virtual account to any bank in Nigeria. Transfers are processed through the NIP (Nigeria Instant Payment) network, which is the same inter-bank rails used by every major Nigerian bank. Most transfers complete in under 10 seconds. Key facts:
  • Minimum transfer amount: ₦1 (100 kobo)
  • Maximum per transfer: determined by your tier (see your dashboard)
  • Settlement: instant in sandbox; NIP real-time in production
  • All requests require an idempotency key to prevent duplicate payments

Base URL

EnvironmentBase URL
Sandboxhttps://sandbox.api.unionbank.ng
Productionhttps://api.unionbank.ng

Authentication

All Payments endpoints require an API key:
Authorization: ApiKey <your-api-key>
High-value production transfers additionally require mTLS (mutual TLS). Your server must present a client certificate issued during the go-live process. Contact baas-support@unionbank.ng to obtain your certificate.

Idempotency

Every payment request must include an idempotency key. This is a unique string you generate (a UUID is ideal) that ensures the same payment is never processed twice, even if your server retries after a timeout.
X-Idempotency-Key: 7f3a2b9c-1d4e-4f5a-8b6c-2e9d0f1a3c5e
If you send two requests with the same idempotency key within 24 hours, the second request returns the result of the first instead of creating a new payment. After 24 hours, the key expires and can be reused.

Endpoints

Initiate Transfer

POST /v1/payments/transfer Sends money from one of your virtual accounts to any Nigerian bank account. What you send:
FieldTypeRequiredDescription
source_account_idstringYesThe account_id of your virtual account to debit
amountintegerYesAmount in kobo (₦1,000 = 100000)
currencystringYesAlways NGN
beneficiary_account_numberstringYesDestination 10-digit NUBAN
beneficiary_bank_codestringYes3-digit CBN bank code for the destination bank
beneficiary_namestringYesFull name of the account holder at the destination bank
narrationstringNoDescription that appears on the beneficiary’s bank statement (max 50 characters)
referencestringNoYour internal reference for this payment — returned in webhooks
metadataobjectNoAny key-value pairs for your own records
What you get back:
FieldDescription
payment_idUnique ID for tracking this payment
statuspending, processing, completed, or failed
amountAmount debited in kobo
feeProcessing fee in kobo
referenceYour reference (or system-generated if not provided)
estimated_completionISO 8601 timestamp for expected settlement
Example request:
{
  "source_account_id": "acct_9d7e1c",
  "amount": 500000,
  "currency": "NGN",
  "beneficiary_account_number": "0987654321",
  "beneficiary_bank_code": "044",
  "beneficiary_name": "Chidi Okonkwo",
  "narration": "Invoice payment - INV-2024-042",
  "reference": "PAY-20240115-001"
}
Example response:
{
  "payment_id": "pay_4a8f2c",
  "status": "processing",
  "amount": 500000,
  "fee": 5000,
  "currency": "NGN",
  "reference": "PAY-20240115-001",
  "created_at": "2024-01-15T10:30:00+01:00",
  "estimated_completion": "2024-01-15T10:30:10+01:00"
}

Get Payment Status

GET /v1/payments/{payment_id} Check the current status of a payment. Poll this endpoint if you do not use webhooks, or use it to verify webhook events. Path parameter:
ParameterDescription
payment_idThe payment_id returned when the transfer was initiated
Status values:
StatusMeaning
pendingPayment queued, not yet submitted to NIP
processingSubmitted to the NIP network
completedBeneficiary account credited
failedTransfer unsuccessful — see failure_reason
reversedPayment was reversed after initial completion

List Payments

GET /v1/payments Returns a paginated list of all payments made from your business accounts. Query parameters:
ParameterTypeDefaultDescription
fromstringStart date YYYY-MM-DD
tostringEnd date YYYY-MM-DD
statusstringallFilter by payment status
source_account_idstringReturn only payments from this account
pageinteger1Page number
per_pageinteger20Results per page (max 100)

Reverse Payment

POST /v1/payments/{payment_id}/reverse Request a reversal of a completed payment. Reversals are not guaranteed — if the beneficiary bank has already credited the account and the funds have been spent, reversal may fail. What you send:
FieldTypeRequiredDescription
reasonstringYesReason for reversal (e.g., wrong_account, duplicate_payment)
Reversals are best-effort and depend on the beneficiary bank’s cooperation. Always verify account details before initiating a transfer.

Verify Bank Account

GET /v1/payments/verify-account Confirm that a bank account number and name match before sending money. Prevents failed transfers due to wrong account numbers. Query parameters:
ParameterTypeRequiredDescription
account_numberstringYes10-digit NUBAN to verify
bank_codestringYes3-digit CBN bank code
What you get back:
FieldDescription
account_nameRegistered name of the account holder
account_numberConfirmed account number
bank_nameBank name
verifiedtrue if the account exists and is active

Nigerian Bank Codes (Common)

BankCode
Access Bank044
Zenith Bank057
GTBank058
First Bank011
UBA033
Union Bank032
Kuda Bank090267
OPay100004
Moniepoint50515
The full list of CBN bank codes is available at CBN’s official portal.

Request Signing (Production)

Production payment requests must be signed with your HMAC-SHA256 secret key. This prevents tampering in transit. Signing steps:
  1. Concatenate: timestamp + "." + request_body_json
  2. Compute: HMAC-SHA256(concatenated_string, your_hmac_secret)
  3. Encode: Base64 the result
  4. Send: Include in the X-Signature header along with X-Timestamp
X-Timestamp: 1705312200
X-Signature: Base64(HMAC-SHA256("1705312200.{...body...}", hmac_secret))
The timestamp must be within 5 minutes of the server clock. Requests with older timestamps are rejected to prevent replay attacks.

Error Codes

HTTP StatusError CodeMeaning
400VALIDATION_ERRORMissing or invalid fields
402INSUFFICIENT_BALANCESource account does not have enough funds
404ACCOUNT_NOT_FOUNDSource account ID does not exist
409DUPLICATE_PAYMENTIdempotency key already used — returning original result
422INVALID_BANK_ACCOUNTBeneficiary account not found or inactive
429RATE_LIMIT_EXCEEDEDToo many requests — see Rate Limits
503NIP_UNAVAILABLEInter-bank network temporarily unavailable — retry

Full API Reference

Open API Playground

Try Payments endpoints live in the browser