PayOnUs API Documentation

Welcome to the PayOnUs API documentation. This guide will help you integrate with our payment processing platform to enable seamless financial transactions for your business.

API Integration
Get your credentials and make your first API call
Payins
Accept payments via virtual accounts & mobile money
Payouts
Transfer funds from your PayOnUs wallet to bank accounts

API Integration

To set up your integration, follow these steps to get your credentials and connect to the PayOnUs API. All requests must be made over HTTPS; responses are in JSON format.

1
Create an account
Sign up on the Merchant Portal. You'll have access to both Sandbox and Production environments immediately after registration.
2
Get your API credentials
Navigate to Settings → API Credentials to generate your Client ID and Client Secret. These are required to obtain an access token.
3
Set up your Webhook
Configure your Webhook URL and copy your Webhook Verification Key from Settings → API. Both are needed to receive and verify payment notifications.
4
Retrieve your Business ID
Toggle Test Mode to OFF, navigate to the Business section and copy your ID. Required for most API endpoints. You can re-enable Test Mode after.

Base URLs

Sandbox
https://core-sandbox.payonus.com
No real money. Full API parity.
Production
https://core.payonus.com
Live keys. Keep credentials secret.

Authentication

PayOnUs uses OAuth 2.0. Exchange your Client ID and Client Secret for an access token, then pass it in the Authorization header of every request.

Generate Access Token

POST /api/v1/access-token

curl -X POST https://core.payonus.com/api/v1/access-token \ -H "Content-Type: application/json" \ -d '{"apiClientId":"your_client_id","apiClientSecret":"your_client_secret"}'

Response

{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "Bearer", "expires_in": 86399 }

Include the token in every subsequent request:

Authorization: Bearer <access_token>
You can only have one valid token at a time — generating a new one invalidates the previous. Token generation is rate-limited to 3 requests per minute. Cache the token for slightly less than expires_in seconds.

Error Handling

PayOnUs uses conventional HTTP status codes. 2xx indicates success; 4xx a client error; 5xx a server error.

400Bad RequestMissing or invalid parameter
401UnauthorizedMissing or expired access token
403ForbiddenToken doesn't have permission for this action
404Not FoundThe requested resource doesn't exist
409ConflictRequest conflicts with an existing record
429Too Many RequestsRate limit exceeded (e.g. token generation > 3/min)
500Internal Server ErrorSomething went wrong on PayOnUs's servers

Error response format

{ "status": 400, "data": { "statusCode": 400, "errors": [] } }

Payins

PayOnUs provides multiple methods to receive payments: Virtual Accounts (Fixed or Dynamic) and Mobile Money. Virtual Accounts accept bank transfers; Mobile Money supports MTN, M-Pesa, and other networks across Africa.

Fixed Accounts
Permanent accounts assigned to your business for receiving ongoing customer payments.
Dynamic Accounts
Temporary accounts created per transaction — ideal for one-time payments with exact amounts.
Mobile Money
Accept MoMo payments across Ghana (MTN), Kenya (M-Pesa), South Africa, and Francophone Africa.
Card Payments
Accept card payments via the PayOnUs checkout or direct API integration.

Fixed Accounts

Fixed virtual accounts are permanent accounts assigned to your business that can receive payments from customers at any time.

POST /api/v1/virtual-accounts/fixed-account

{ "customer": { "name": "Test Customer", "email": "test@customer.com", "phone": "+2347019098867", "bvn": "22123456789", "address": { "line1": "9 Finance Avenue" } ,} "dob": "1994-01-01", "reference": "unique_ref_001", "businessId": "your_business_id" }

Dynamic Accounts

Dynamic virtual accounts are temporary accounts created for a specific transaction. Ideal for one-time payments and precise transaction tracking.

POST /api/v1/virtual-accounts/dynamic

{ "amount": 5000.00, "reference": "unique_txn_ref", "businessId": "your_business_id", "customer": { "name": "John Doe", "email": "john@example.com", "phone": "08012345678" } }

Response

{ "status": 200, "message": "Success", "data": { "accountName": "John Doe", "accountNumber": "1212334455", "bankName": "Test Bank", "amount": 5000, "onusReference": "ONUS-NUB-xxx-xxx-xxx", "merchantReference": "unique_txn_ref" } }

Payouts

Transfer funds from your PayOnUs wallet to bank accounts, mobile money wallets, or other PayOnUs businesses. Always perform a Name Enquiry before initiating a bank transfer.

GET /api/v1/banks
Fetch Bank List
Returns available banks and institution codes.
POST /api/v1/transfer-requests/name-enquiry
Name Enquiry
Verify account details before initiating a transfer.
POST /api/v1/transfer-requests/bank-transfer
Initiate Transfer
Send funds to a bank account or mobile money wallet.
GET /api/v1/transfer-requests
List Transfers
Retrieve transfer history with optional filters.
GET /api/v1/merchants/{merchantId}/wallets
Fetch Wallets
List all wallets and their current balances.

Bank Transfers

1. Name Enquiry

POST /api/v1/transfer-requests/name-enquiry

{ "institutionCode": "044", "accountNumber": "0123456789", "businessId": "your_business_id", "currency": "NGN" }

2. Initiate Transfer

POST /api/v1/transfer-requests/bank-transfer

{ "reference": "unique_ref_123", "amount": 5000.00, "beneficiaryAccountNumber": "0123456789", "beneficiaryAccountName": "John Doe", "beneficiaryBankCode": "044", "transferType": "WALLET_TO_BANK_ACCOUNT", "countryCode": "NG", "currency": "NGN", "businessId": "your_business_id", "email": "customer@example.com" }

Transfer types: WALLET_TO_WALLET · WALLET_TO_BANK_ACCOUNT · WALLET_TO_MOMO · WALLET_TO_EFT


Wallets

Retrieve all wallets for a merchant and their current balances.

GET /api/v1/merchants/{merchantId}/wallets

{ "status": 200, "message": "Success", "data": [ { "currency": "NGN", "availableBalance": 50000000.00, "status": "ACTIVE", "maxSinglePayout": 1000000.00, "payoutAllowed": true }, { "currency": "KES", "availableBalance": 1000000.00, "status": "ACTIVE", "maxSinglePayout": 10000.00, "payoutAllowed": true } ] }

Webhooks

PayOnUs sends real-time webhook events when a payment is received or a payout completes. Configure a publicly accessible HTTPS endpoint under Merchant Dashboard → Settings → Webhooks. Respond with HTTP 200 within 5 seconds; defer heavy work to background jobs.

Event types

CollectionCOLLECTIONInbound payment confirmed — bank transfer, card, or mobile money
PayoutPAYOUTOutbound transfer settled, failed, or rejected

Every webhook request includes a hash header — a hex-encoded SHA-256 used to verify payload integrity.

Sample payload — Dynamic Account payin

{ "id": "xxx-xxx-xxx", "type": "COLLECTION", "currency": "NGN", "businessId": "xxx-xxx-xxx", "accountNumber": "6012343210", "onusReference": "ONUS-NUB-xxxx-01012026-010101", "paymentStatus": "SUCCESSFUL", "paymentChannel": "BANK_TRANSFER", "transactionAmount": 50000, "merchantReference": "mtx-20260101-010101", "merchantFee": 50, "senderDetails": { "name": "JOHN SMITH", "bankCode": "100004", "bankName": "OPAY", "accountNumber": "8012345678" } }

Verify Signatures

Always verify incoming webhooks to confirm they originate from PayOnUs and were not tampered with.

  1. Retrieve your Verification Key from Merchant Dashboard → Settings → API.
  2. Concatenate in this exact order (no delimiter): accountNumber + onusReference + paymentStatus + verificationKey
  3. Compute the SHA-256 hex digest of the resulting string.
  4. Compare it to the hash header using a constant-time comparison.
import crypto from 'crypto'; function verifyWebhook(body, hashHeader, verificationKey) { const str = body.accountNumber + body.onusReference + body.paymentStatus + verificationKey; const computed = crypto .createHash('sha256') .update(str) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(computed), Buffer.from(hashHeader) ); }

Testing Integration

Use the sandbox environment to simulate transactions before going live. Ensure your Webhook URL and Verification Key are configured in the sandbox dashboard, and that your IP is whitelisted.

Simulate NGN Payin

POST http://core-sandbox.payonus.com/api/v1/fund-ngn-account

{ "onusReference": "ONUS-NUB-{{random}}", "accountNumber": "{{dynamic_account_number}}", "amount": 10000, "paymentStatus": "SUCCESSFUL", "forDynamicAccount": true }

NGN Payout test accounts

9999991111PROCESSING
9999991112SUCCESSFUL
9999991113UNKNOWN_ERROR
9999991114INVALID_ACCOUNT
9999991117INSUFFICIENT_FUNDS
9999991118ACCOUNT_NAME_MISMATCH
9999991119TRANSACTION_NOT_PERMITTED_TO_SENDER
9999991120INVALID_AMOUNT
9999991121INVALID_BANK_CODE

Mobile Money test numbers

GHS — GhanaMTN0240000003
KES — KenyaM-Pesa234700000000
ZAR — South Africa—Automatic on any attempt
Related:Payment API →Security →Developers →