Afripay Merchant API
Use this API to manage customers, attach documents, open currency accounts, issue cards, move money, and receive webhooks when things change.
https://sandbox-business.goafripay.com/api/merchant
Authentication
Afripay APIs require authentication using client credentials generated from Merchant Dashboard → Settings → API Credentials (open API credentials).
Option 1: Custom Headers (Recommended)
X-Client-Id: mch_live_YOUR_CLIENT_ID
X-Client-Secret: sk_live_YOUR_CLIENT_SECRET
Accept: application/json
Content-Type: application/json
Option 2: Bearer Token
Authorization: Bearer sk_live_YOUR_CLIENT_SECRET
Accept: application/json
Content-Type: application/json
Complete End-to-End Architecture
How your app starts a checkout, your customer pays, and Afripay notifies you when the payment succeeds.
1. Third-Party App 2. Afripay Gateway 3. Customer
│ │ │
├─ POST /checkout/initialize ─>│ │
│ (amount, name, email, ref) │ │
│<── Returns checkout_url ────┤ │
│ │ │
│── Redirects customer ───────┼─> Opens checkout page ──────>│ Customer sees pre-filled
│ │ (/link/{token}) │ name, email & locked amount.
│ │ │ Gets bank transfer details.
│ │ │
│ │<── Customer pays transfer ───┤
│ │ │
│ │<── Payment confirmed ────────┤ Customer payment succeeds
│ │ │
│ ├── Credit your wallet │
│<── Afripay Webhook ─────────┤ (charge.success) │
│ (Fulfill the order) │ │
│ │ │
│<── Customer Redirect ───────┼──────────────────────────────┤ Browser redirects back to
│ (to callback_url) │ │ merchant's callback_url
1. Where Merchants Configure Their Webhook
Merchants can configure their webhook URL and view their secret directly in the dashboard:
URL Route:
https://sandbox-business.goafripay.com/merchant/developer/webhooks(Also accessible via a shortcut button on the API Credentials page at
https://sandbox-business.goafripay.com/merchant/developer/api)
What is available on the Webhook page:
| Feature | Description |
|---|---|
| Webhook URL | The merchant pastes their listener endpoint (e.g., https://example.com/api/afripay-webhook). Must be an accessible HTTPS URL. |
| Status Switch | Toggle webhook notifications on or off at any time. |
| Signing Secret | A unique secret key (whsec_...) used to verify signatures with a one-click copy button and a Rotate Secret button. |
| Send Test Webhook | Sends an instant test ping event (webhook.test) to verify that your endpoint responds with HTTP 200. |
| Delivery History Log | Displays every webhook attempt, event name, target URL, HTTP response code, attempt count, timestamp, an Inspect Payload modal, and a Retry button. |
| Automatic Retries | If your server is down or returns an error, Afripay automatically retries delivery (after about 1 minute, then 5 minutes, 15 minutes, 1 hour, 3 hours, and 6 hours). |
2. Third-Party Integration API
Step 1: Initialize Checkout
Creates a checkout session for your customer. You can pass the transaction amount, customer details, custom reference, redirect callback URL, and custom metadata.
Request Body Parameters
| Field | Type | Required | Description |
|---|---|---|---|
amount | numeric | Yes | Transaction amount (e.g. 5000). |
currency | string | No | Currency code (default: NGN). |
reference | string | No | Your unique transaction reference (e.g. ORDER-982134). Auto-generated if omitted. |
title | string | No | Description or payment title displayed to customer. |
customer.name / name | string | No | Customer full name. Pre-fills on the payment screen. |
customer.email / email | string | Yes | Customer email address. Pre-fills on the payment screen. |
customer.phone / phone | string | No | Customer phone number. |
callback_url | string (URL) | No | URL customer will be redirected to upon successful payment completion. |
metadata | object | No | Custom JSON object with key-value pairs stored with this transaction. |
email, name, first_name, last_name, phone) are also accepted at the root level for developer convenience.
{
"amount": 5000,
"currency": "NGN",
"reference": "ORDER-982134",
"title": "Payment for Order #982134",
"customer": {
"name": "Jane Doe",
"email": "jane.doe@example.com",
"phone": "+2348012345678"
},
"callback_url": "https://merchant-store.com/checkout/callback",
"metadata": {
"order_id": 982134,
"user_id": 42
}
}
{
"status": true,
"message": "Checkout link initialized successfully.",
"data": {
"checkout_url": "https://sandbox-business.goafripay.com/link/qtujnmgstm",
"token": "qtujnmgstm",
"reference": "ORDER-982134",
"amount": "5000.00",
"currency": "NGN",
"title": "Payment for Order #982134",
"customer": {
"name": "Jane Doe",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane.doe@example.com",
"phone": "+2348012345678"
},
"callback_url": "https://merchant-store.com/checkout/callback",
"created_at": "2026-10-06T12:00:00.000000Z"
}
}
Step 2: Customer Checkout Experience
- The merchant redirects their customer to
checkout_url(e.g.https://sandbox-business.goafripay.com/link/qtujnmgstm). - When the customer lands on the page:
- Customer Details: First Name, Last Name, and Email are pre-filled automatically.
- Amount Locked: The Amount field is locked and readonly to prevent tampering.
- The customer clicks Pay and receives dedicated virtual bank transfer account details.
- The customer transfers the funds via their bank app. Afripay confirms the incoming transfer.
- Automatic Redirect: If
callback_urlwas provided, the checkout page automatically redirects back with query parameters:https://merchant-store.com/checkout/callback?status=successful&reference=ORDER-982134&trx=PL-64728192
Step 3: Webhook Notification (Server-to-Server)
When the payment is confirmed, Afripay signs and sends an HTTP POST request to your configured Webhook URL. Full event catalog and payloads are under Webhooks.
HTTP Headers Sent
| Header | Description |
|---|---|
Content-Type | application/json |
X-Afripay-Event | Event name, e.g. charge.success or charge.failed |
X-Afripay-Signature | Signature of the request body, signed with your webhook secret — use this to confirm the webhook came from Afripay |
X-Afripay-Delivery | Unique id for this delivery attempt (useful if the same event is retried) |
Identifiers in the payload
| Field | What it means |
|---|---|
id | Afripay’s unique id for this payment. Store this and use it when you need to look the payment up later. |
trx_id | Short transaction code you can show on receipts or support tickets (for example PL-64728192). |
reference | Your own reference from checkout (for example ORDER-982134). |
afripay_reference | Afripay’s payment-link / collection reference for this checkout. |
{
"event": "charge.success",
"data": {
"id": "a1b2c3d4e5f6789012345678abcdef01",
"reference": "ORDER-982134",
"afripay_reference": "PL-QTUJNM-AB12CD",
"trx_id": "PL-64728192",
"status": "successful",
"amount": "5000.00",
"currency": "NGN",
"fee": "75.00",
"amount_settled": "4925.00",
"payment_method": "bank_transfer",
"customer": {
"name": "Jane Doe",
"email": "jane.doe@example.com",
"phone": "+2348012345678"
},
"payment_link": {
"token": "qtujnmgstm",
"title": "Payment for Order #982134"
},
"metadata": {
"order_id": 982134,
"user_id": 42
},
"paid_at": "2026-10-06T12:05:00.000000Z",
"created_at": "2026-10-06T12:00:00.000000Z"
}
}
Step 4: Verifying the Webhook on Third-Party Servers
Always verify the X-Afripay-Signature header before processing any webhook payload to ensure the request originated from Afripay.
PHP Verification Example
<?php
$secret = 'whsec_YOUR_WEBHOOK_SECRET';
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_AFRIPAY_SIGNATURE'] ?? '';
$computedSignature = hash_hmac('sha512', $rawBody, $secret);
if (!hash_equals($computedSignature, $signature)) {
http_response_code(401);
exit('Invalid signature');
}
$payload = json_decode($rawBody, true);
if ($payload['event'] === 'charge.success') {
$reference = $payload['data']['reference'];
$amount = $payload['data']['amount'];
// Update order status to PAID in merchant database
}
http_response_code(200);
echo json_encode(['status' => 'ok']);
?>
Node.js / Express Verification Example
const crypto = require('crypto');
app.post('/api/afripay-webhook', express.raw({ type: 'application/json' }), (req, res) => {
const secret = process.env.AFRIPAY_WEBHOOK_SECRET;
const signature = req.headers['x-afripay-signature'];
const computedSignature = crypto
.createHmac('sha512', secret)
.update(req.body)
.digest('hex');
if (computedSignature !== signature) {
return res.status(401).send('Invalid signature');
}
const payload = JSON.parse(req.body.toString());
if (payload.event === 'charge.success') {
const { reference, amount } = payload.data;
// Mark order as paid in database
}
return res.status(200).json({ received: true });
});
Step 5: Verify Endpoint (Polling Fallback)
If you ever miss a webhook, or want to verify a payment on demand, query the verification endpoint using your reference or Afripay token.
curl -X GET "https://sandbox-business.goafripay.com/api/v1/checkout/verify/ORDER-982134" \
-H "X-Client-Id: mch_live_YOUR_CLIENT_ID" \
-H "X-Client-Secret: sk_live_YOUR_CLIENT_SECRET"
{
"status": true,
"message": "Payment status retrieved.",
"data": {
"reference": "ORDER-982134",
"afripay_reference": "PL-QTUJNM-AB12CD",
"status": "successful",
"amount": "5000.00",
"currency": "NGN",
"fee": "75.00",
"amount_settled": "4925.00",
"paid_at": "2026-10-06T12:05:00.000000Z"
}
}
Customers
A customer is the person you open accounts and cards for. Typical flow:
- Create the customer (identity and address).
- Attach documents when you need foreign accounts.
- Open a currency account.
- Listen for webhooks when the account is ready or if it fails.
1. List Customers
Query parameters
| Parameter | Type | Description |
|---|---|---|
per_page | integer | Items per page (default 20, max 100). |
{
"status": true,
"message": "Customers fetched successfully",
"data": [
{
"id": "1b04b3ffb7f0458db6357e8b152977b9",
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com",
"phone": "+2348012345678",
"date_of_birth": "1990-05-15",
"bvn": "12345678901",
"status": "active",
"address": {
"line1": "12 Broad Street",
"city": "Lagos",
"state": "Lagos",
"country": "NG"
},
"documents": [],
"currency_accounts_count": 0,
"cards_count": 0,
"created_at": "2026-07-10T17:50:55Z"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 20,
"total": 1
}
}
2. Create Customer
If a customer with the same email or BVN already exists for your merchant, we return that customer with HTTP 200 instead of creating a duplicate.
Required fields
| Field | Notes |
|---|---|
first_name | Customer first name. |
email | Unique per merchant. |
phone | Include country code when possible. |
date_of_birth | Must be 18 or older. Format YYYY-MM-DD. |
bvn | Exactly 11 digits. |
address.line1, city, state, country | country is a 2-letter code (for example NG). |
Optional fields
last_namesecondary_id_type/secondary_id_number(nin,drivers_license,passport,voters_card)occupation,source_of_funds, and related profile fields — optional at create; required before USD or EUR (see Occupation & Source of Funds)documents— see Documents
{
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com",
"phone": "+2348012345678",
"date_of_birth": "1990-05-15",
"bvn": "12345678901",
"address": {
"line1": "12 Broad Street",
"city": "Lagos",
"state": "Lagos",
"country": "NG"
},
"occupation": "software_developer",
"source_of_funds": "salary",
"documents": [
{
"type": "passport",
"url": "https://files.example.com/passport.pdf",
"expiry_date": "2030-01-01"
},
{
"type": "selfie",
"url": "https://files.example.com/selfie.jpg"
},
{
"type": "bank_statement",
"url": "https://files.example.com/statement.pdf"
}
]
}
Documents
Pass public file URLs Afripay can download. You can send documents on create or later on update.
Document object
| Field | Required | Notes |
|---|---|---|
type | Yes | One type per customer. See list below. |
url | Yes | Must be reachable over HTTPS. |
expiry_date | No* | Required for passport when opening USD or EUR. |
Supported types
passport— preferred for foreign accountsselfie— required for foreign accountsbank_statementorproof_of_address— proof of addressnin,drivers_license,voters_card,id_photo
For USD and EUR, use passport, selfie, and a proof of address. Other currencies may not need file uploads.
3. Get Customer
Returns the customer profile, documents, currency accounts, and cards.
{
"status": true,
"message": "Customer fetched successfully",
"data": {
"id": "1b04b3ffb7f0458db6357e8b152977b9",
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com",
"phone": "+2348012345678",
"date_of_birth": "1990-05-15",
"bvn": "12345678901",
"nin": null,
"secondary_id_type": null,
"secondary_id_number": null,
"gender": "male",
"occupation": "software_developer",
"source_of_funds": "salary",
"employment_status": "employed",
"primary_purpose": "purchase_goods_and_services",
"monthly_volume": "0_4999",
"status": "active",
"address": {
"line1": "12 Broad Street",
"line2": null,
"city": "Lagos",
"state": "Lagos",
"country": "NG",
"postal_code": null
},
"documents": [
{
"id": "c1d2e3f4a5b6789012345678abcdef01",
"type": "passport",
"url": "https://files.example.com/passport.pdf",
"expiry_date": "2030-01-01",
"verification_status": "verified",
"created_at": "2026-07-10T17:51:00Z"
},
{
"id": "d2e3f4a5b6789012345678abcdef012",
"type": "selfie",
"url": "https://files.example.com/selfie.jpg",
"expiry_date": null,
"verification_status": "verified",
"created_at": "2026-07-10T17:51:00Z"
},
{
"id": "e3f4a5b6789012345678abcdef01234",
"type": "bank_statement",
"url": "https://files.example.com/statement.pdf",
"expiry_date": null,
"verification_status": "verified",
"created_at": "2026-07-10T17:51:00Z"
}
],
"currency_accounts_count": 1,
"cards_count": 0,
"created_at": "2026-07-10T17:50:55Z",
"currency_accounts": [
{
"id": "e2c83407880211f183520e74f6457b17",
"currency_code": "NGN",
"status": "active",
"account_number": "3504939056",
"account_name": "John Doe",
"bank_name": "Partner Bank",
"bank_code": "999",
"routing_number": null,
"bank_address": null,
"label": "Acme/John Doe - NGN",
"created_at": "2026-07-25T09:40:04Z"
}
],
"cards": []
}
}
4. Update Customer
Send only the fields you want to change. To attach or replace documents, send a documents array. Each item needs type and a public url. The same type updates the existing file.
{
"occupation": "software_developer",
"source_of_funds": "salary",
"documents": [
{
"type": "passport",
"url": "https://files.example.com/passport.pdf",
"expiry_date": "2030-01-01"
},
{
"type": "selfie",
"url": "https://files.example.com/selfie.jpg"
},
{
"type": "bank_statement",
"url": "https://files.example.com/statement.pdf"
}
]
}
{
"status": true,
"message": "Customer updated successfully",
"data": {
"id": "1b04b3ffb7f0458db6357e8b152977b9",
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com",
"phone": "+2348012345678",
"date_of_birth": "1990-05-15",
"bvn": "12345678901",
"occupation": "software_developer",
"source_of_funds": "salary",
"status": "active",
"address": {
"line1": "12 Broad Street",
"city": "Lagos",
"state": "Lagos",
"country": "NG"
},
"documents": [
{
"id": "c1d2e3f4a5b6789012345678abcdef01",
"type": "passport",
"url": "https://files.example.com/passport.pdf",
"expiry_date": "2030-01-01",
"verification_status": "verified",
"created_at": "2026-07-10T17:51:00Z"
},
{
"id": "d2e3f4a5b6789012345678abcdef012",
"type": "selfie",
"url": "https://files.example.com/selfie.jpg",
"expiry_date": null,
"verification_status": "verified",
"created_at": "2026-07-10T17:51:00Z"
},
{
"id": "e3f4a5b6789012345678abcdef01234",
"type": "bank_statement",
"url": "https://files.example.com/statement.pdf",
"expiry_date": null,
"verification_status": "verified",
"created_at": "2026-07-10T17:51:00Z"
}
],
"currency_accounts_count": 0,
"cards_count": 0,
"created_at": "2026-07-10T17:50:55Z",
"currency_accounts": [],
"cards": []
}
}
Cards
6. List Cards
Query Params: per_page, currency, label.
7. Create Card
{
"currency": "USD",
"label": "Primary Card",
"extra": {}
}
8. Get Card
9. Fund Card
{
"amount": 10.00,
"extra": {
"note": "Top up"
}
}
Validation: Amount must be numeric, min 0.00000001. Our API enforces min $5.00 and max $10,000.00.
10. Freeze / Unfreeze Card
{
"freeze": true
}
11. Card Secure Settings
{
"payload": {
"contactless_enabled": false
}
}
12. Close Card
Currency Accounts
Currency accounts are virtual bank accounts your customers can receive money into. One account per currency per customer.
Create always returns HTTP 201 with an account id. Bank details may be filled right away, or stay empty while status is pending. When the account is ready, Afripay sends currency_account.ready. That applies to every currency, including NGN.
Requirements by currency
| Currency | What you need | What to expect |
|---|---|---|
| NGN | Customer with BVN (and related identity already on the customer). | Often ready in the create response. You still get a ready webhook. |
| USD / EUR |
Date of birth; occupation and source of funds; passport (with expiry); selfie; proof of address ( bank_statement or proof_of_address).
|
May return pending with null bank fields until review finishes. Then ready webhook (or failed if declined). |
See Occupation & Source of Funds for the allowed values. If something is missing, the create error lists it.
If create is rejected after review, you get currency_account.failed. That account is removed so you can update documents and try again. Customer records stay in place.
Occupation & source of funds
Send these on create or update customer. They are required before USD or EUR accounts. Use the value in your request; the label is what it means.
Source of funds
salary— Salarysavings— Savings
Occupation values
Set occupation to a value from this list:
software_developer— Software developerregistered_nurse— Registered nursephysician— Physiciansocial_worker— Social workerit_specialist— IT specialistcustomer_service— Customer servicefinancial_analyst— Financial analystproject_manager— Project managerweb_developer— Web developermedia_professional— Media professionalmedical_manager— Medical / health services managerlawyer— Lawyerlegal_support— Legal supportgeneral_manager— General managermarket_research— Market research analystmarketing_manager— Marketing managerengineering_technician— Engineering technicianaccountant— Accountantsystems_analyst— Systems analystother— Other
Other optional fields
- employment_status
employed— Employedself_employed— Self-employedretired— Retiredstudent— Studentunemployed— Unemployedhomemaker— Homemaker
- primary_purpose
payments_to_friends_or_family_abroad— Payments to friends or family abroadpurchase_goods_and_services— Purchase goods and servicesinvestment_purposes— Investment
- monthly_volume
0_4999— Under 5,0005000_9999— 5,000 – 9,99910000_49999— 10,000 – 49,999
- gender
male— Malefemale— Female
13. List Currency Accounts
Optional query: per_page, currency, label.
{
"status": true,
"message": "Currency accounts fetched successfully",
"data": [
{
"id": "e2c83407880211f183520e74f6457b17",
"currency_code": "NGN",
"status": "active",
"account_number": "3504939056",
"account_name": "John Doe",
"bank_name": "Partner Bank",
"bank_code": "999",
"routing_number": null,
"bank_address": null,
"rails": ["NIP"],
"balance": "0.00",
"label": "Acme/John Doe - NGN",
"created_at": "2026-07-25T09:40:04Z",
"customer_id": "1b04b3ffb7f0458db6357e8b152977b9"
},
{
"id": "a9b8c7d6e5f432109876543210fedcba",
"currency_code": "USD",
"status": "pending",
"account_number": null,
"account_name": null,
"bank_name": null,
"bank_code": null,
"routing_number": null,
"bank_address": null,
"rails": ["ACH", "FEDWIRE"],
"balance": "0.00",
"label": "Acme/John Doe - USD",
"created_at": "2026-07-25T09:41:00Z",
"customer_id": "1b04b3ffb7f0458db6357e8b152977b9"
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 20,
"total": 2
}
}
14. Create Currency Account
{
"currency": "USD"
}
currency must be 3 letters (for example NGN, USD, EUR). If the account already exists, we return it with HTTP 200.
{
"status": true,
"message": "Currency account created",
"data": {
"id": "e2c83407880211f183520e74f6457b17",
"currency_code": "NGN",
"status": "active",
"account_number": "3504939056",
"account_name": "John Doe",
"bank_name": "Partner Bank",
"bank_code": "999",
"routing_number": null,
"bank_address": null,
"rails": ["NIP"],
"balance": "0.00",
"label": "Acme/John Doe - NGN",
"created_at": "2026-07-25T09:40:04Z",
"customer_id": "1b04b3ffb7f0458db6357e8b152977b9"
}
}
{
"status": true,
"message": "Currency account created",
"data": {
"id": "a9b8c7d6e5f432109876543210fedcba",
"currency_code": "USD",
"status": "pending",
"account_number": null,
"account_name": null,
"bank_name": null,
"bank_code": null,
"routing_number": null,
"bank_address": null,
"rails": ["ACH", "FEDWIRE"],
"balance": "0.00",
"label": "Acme/John Doe - USD",
"created_at": "2026-07-25T09:40:04Z",
"customer_id": "1b04b3ffb7f0458db6357e8b152977b9"
}
}
For USD and EUR, bank_address and routing_number appear when the account becomes active. You can call Get Account, or wait for the webhook.
15. Get Currency Account
{
"status": true,
"message": "Currency account fetched successfully",
"data": {
"id": "a9b8c7d6e5f432109876543210fedcba",
"currency_code": "USD",
"status": "active",
"account_number": "120049583091",
"account_name": "John Doe",
"bank_name": "Partner Bank",
"bank_code": "026009593",
"routing_number": "026009593",
"bank_address": "Example Bank Address",
"rails": ["ACH", "FEDWIRE"],
"balance": "0.00",
"label": "Acme/John Doe - USD",
"created_at": "2026-07-25T09:40:04Z",
"customer_id": "1b04b3ffb7f0458db6357e8b152977b9"
}
}
16. Update Account
Not all account types support updates. When supported, send the fields you need to change.
{
"payload": {
"label": "Updated account label"
}
}
{
"status": true,
"message": "Bank account updated successfully",
"data": {
"id": "a9b8c7d6e5f432109876543210fedcba",
"currency_code": "USD",
"status": "active",
"account_number": "120049583091",
"account_name": "John Doe",
"bank_name": "Partner Bank",
"bank_code": "026009593",
"routing_number": "026009593",
"bank_address": "Example Bank Address",
"rails": ["ACH", "FEDWIRE"],
"balance": "0.00",
"label": "Updated account label",
"created_at": "2026-07-25T09:40:04Z",
"customer_id": "1b04b3ffb7f0458db6357e8b152977b9"
}
}
Stablecoin Deposit Addresses
Create a crypto deposit address for each customer (USDT or USDC). When they send funds to that address, Afripay credits your USD wallet and notifies you by webhook.
18. List Deposit Addresses
Optional query: currency, network, per_page (1–100).
{
"status": true,
"message": "Deposit addresses fetched successfully",
"data": [
{
"id": "a1b2c3d4e5f6789012345678abcdef01",
"currency": "USDT",
"network": "TRC20",
"address": "TXyzExampleDepositAddress123456",
"status": "active",
"settlement_currency": "USD",
"label": null,
"created_at": "2026-10-10T12:00:00.000000Z",
"customer": {
"id": "1b04b3ffb7f0458db6357e8b152977b9",
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com"
}
}
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 20,
"total": 1
}
}
19. Create Deposit Address
If this customer already has an address for that currency and network, Afripay returns the existing one instead of creating another.
{
"currency": "USDT",
"network": "TRC20"
}
Supported currencies: USDT, USDC
Supported networks: TRC20, ERC20, POL, SOL
{
"status": true,
"message": "Deposit address ready",
"data": {
"id": "a1b2c3d4e5f6789012345678abcdef01",
"currency": "USDT",
"network": "TRC20",
"address": "TXyzExampleDepositAddress123456",
"status": "active",
"settlement_currency": "USD",
"label": null,
"created_at": "2026-10-10T12:00:00.000000Z",
"customer": {
"id": "1b04b3ffb7f0458db6357e8b152977b9",
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com"
}
}
}
20. Get Deposit Address
Initiate Stablecoin Payout
Send USDT or USDC from your USD wallet balance to an external address. Afripay debits amount + fee from your USD wallet when the payout is accepted, then sends the transfer on-chain.
Statuses: queued → pending → success or failed.
- A
201withqueuedmeans the payout was accepted and your wallet was debited — not that the transfer is confirmed on-chain. - Treat
success/failedfrom status enquiry or the webhook as final. - On
failed, Afripay refundsamount + chargesto your USD wallet.
{
"currency": "USDT",
"network": "TRC20",
"address": "TXyzExampleDestinationAddress123456",
"amount": 100.00,
"reference": "sc_payout_001",
"remarks": "Vendor payout"
}
Supported currencies: USDT, USDC
Supported networks: TRC20, ERC20, POL, SOL
{
"status": true,
"message": "Stablecoin payout initiated and queued.",
"data": {
"id": "a1b2c3d4e5f6789012345678abcdef01",
"reference": "sc_payout_001",
"trx_id": "SCP1234567890",
"currency": "USDT",
"network": "TRC20",
"address": "TXyzExampleDestinationAddress123456",
"amount": "100.00",
"charges": "1.00",
"status": "queued",
"tx_hash": null,
"remarks": "Vendor payout",
"created_at": "2026-10-16T12:00:00+00:00"
}
}
Stablecoin Payout Status
Lookup by payout id or your client reference.
Statuses: queued (accepted), pending (in progress), success (confirmed), failed (refunded). Prefer webhooks for final state; poll this endpoint to catch up.
GET /api/merchant/baas/stablecoin/payouts/a1b2c3d4e5f6789012345678abcdef01
GET /api/merchant/baas/stablecoin/payouts/sc_payout_001
{
"status": true,
"message": "Stablecoin payout status retrieved successfully",
"data": {
"id": "a1b2c3d4e5f6789012345678abcdef01",
"reference": "sc_payout_001",
"trx_id": "SCP1234567890",
"currency": "USDT",
"network": "TRC20",
"address": "TXyzExampleDestinationAddress123456",
"amount": "100.00",
"charges": "1.00",
"status": "success",
"tx_hash": "0xabc...",
"remarks": "Vendor payout",
"created_at": "2026-10-16T12:00:00+00:00"
}
}
Payouts
Send NGN to a Nigerian bank account from your wallet. Responses look like other BAAS endpoints: status, message, and data. Store each payout id.
21. Fetch Support Banks
{
"status": true,
"message": "Banks fetched successfully",
"data": [
{
"bank_code": "090823",
"bank_name": "Partner Bank"
},
{
"bank_code": "000013",
"bank_name": "Access Bank"
}
]
}
22. Resolve Account Name
{
"account_number": "3504939056",
"bank_code": "090823"
}
{
"status": true,
"message": "Account resolved successfully",
"data": {
"account_number": "3504939056",
"account_name": "Sandbox Individual Account",
"bank_code": "090823",
"bank_name": "Partner Bank"
}
}
23. Initiate Payout
{
"account_number": "3504939056",
"bank_code": "090823",
"amount": 5000.00,
"reference": "merchant_ref_abc123",
"remarks": "Services payment"
}
{
"status": true,
"message": "Payout initiated and queued.",
"data": {
"id": "e2c83407880211f183520e74f6457b17",
"reference": "merchant_ref_abc123",
"trx_id": "PO982461972",
"amount": "5000.00",
"charges": "50.00",
"status": "queued",
"remarks": "Services payment",
"account_number": "3504939056",
"bank_code": "090823",
"bank_name": "Partner Bank",
"account_name": "Sandbox Individual Account",
"created_at": "2026-07-25T14:40:00+00:00"
}
}
24. Payout Status Enquiry
You can look up a payout with the Afripay id returned when you created it, or with your own reference if you sent one.
{
"status": true,
"message": "Payout status retrieved successfully",
"data": {
"id": "e2c83407880211f183520e74f6457b17",
"reference": "merchant_ref_abc123",
"trx_id": "PO982461972",
"amount": "5000.00",
"charges": "50.00",
"status": "success",
"remarks": "Services payment",
"account_number": "3504939056",
"bank_code": "090823",
"bank_name": "Partner Bank",
"account_name": "Sandbox Individual Account",
"created_at": "2026-07-25T14:40:00+00:00"
}
}
Webhooks
All events go to the URL from Webhook Configuration. Check X-Afripay-Signature on every call (see Verify Webhook Signature). Return HTTP 200 within 15 seconds. Missed deliveries are retried.
Events: charge.success, charge.failed, currency_account.ready, currency_account.failed, usd.deposit.success, stablecoin.deposit.success, stablecoin.payout.status_updated, payout.status_updated.
Checkout
Events: charge.success / charge.failed
{
"event": "charge.success",
"data": {
"id": "a1b2c3d4e5f6789012345678abcdef01",
"reference": "ORDER-982134",
"afripay_reference": "PL-QTUJNM-AB12CD",
"trx_id": "PL-64728192",
"status": "successful",
"amount": "5000.00",
"currency": "NGN",
"fee": "75.00",
"amount_settled": "4925.00",
"payment_method": "bank_transfer",
"customer": {
"name": "Jane Doe",
"email": "jane.doe@example.com",
"phone": "+2348012345678"
},
"payment_link": {
"token": "qtujnmgstm",
"title": "Payment for Order #982134"
},
"metadata": {
"order_id": 982134,
"user_id": 42
},
"paid_at": "2026-10-06T12:05:00.000000Z",
"created_at": "2026-10-06T12:00:00.000000Z"
}
}
Currency Account
Sent for every currency (NGN, USD, EUR, and others you open). The data object matches the create and get account response.
Ready — currency_account.ready
Bank details are set and the account can receive funds. Fired even when NGN (or another currency) is already filled in the create response.
{
"event": "currency_account.ready",
"data": {
"id": "a9b8c7d6e5f432109876543210fedcba",
"currency_code": "USD",
"status": "active",
"account_number": "120049583091",
"account_name": "John Doe",
"bank_name": "Partner Bank",
"bank_code": "026009593",
"routing_number": "026009593",
"bank_address": "Example Bank Address",
"rails": ["ACH", "FEDWIRE"],
"balance": "0.00",
"label": "Acme/John Doe - USD",
"created_at": "2026-07-25T09:40:04Z",
"customer_id": "1b04b3ffb7f0458db6357e8b152977b9"
}
}
Failed — currency_account.failed
Review did not pass. We include a short reason, then remove that account so you can fix documents and create again. The customer record is kept.
{
"event": "currency_account.failed",
"data": {
"id": "a9b8c7d6e5f432109876543210fedcba",
"currency_code": "USD",
"status": "failed",
"account_number": null,
"account_name": null,
"bank_name": null,
"bank_code": null,
"routing_number": null,
"bank_address": null,
"rails": ["ACH", "FEDWIRE"],
"balance": "0.00",
"label": "Acme/John Doe - USD",
"created_at": "2026-07-25T09:40:04Z",
"customer_id": "1b04b3ffb7f0458db6357e8b152977b9",
"reason": "Documents require attention. Please update and try again."
}
}
USD Deposit
Event: usd.deposit.success
Fired when a deposit lands in a USD currency account. This is funding, not account creation.
{
"event": "usd.deposit.success",
"data": {
"id": "a1b2c3d4e5f6789012345678abcdef01",
"reference": "DEP-982134",
"trx_id": "TX-64728192",
"status": "successful",
"amount": "250.00",
"amount_settled": "250.00",
"currency": "USD",
"rail": "ACH",
"account_number": "120049583091",
"bank_name": "Partner Bank",
"routing_number": "026009593",
"currency_account_id": "a9b8c7d6e5f432109876543210fedcba",
"customer": {
"id": "1b04b3ffb7f0458db6357e8b152977b9",
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com"
},
"created_at": "2026-10-10T12:05:00.000000Z"
}
}
Stablecoin Deposit
Event: stablecoin.deposit.success
When a crypto deposit is confirmed, Afripay credits your USD wallet. amount is what the customer sent; amount_settled is what was credited (today these match).
{
"event": "stablecoin.deposit.success",
"data": {
"id": "a1b2c3d4e5f6789012345678abcdef01",
"reference": "SCD1234567890",
"trx_id": "SCD1234567890",
"status": "successful",
"amount": "100.00",
"amount_settled": "100.00",
"currency": "USD",
"network": "TRC20",
"address": "TXyzExampleDepositAddress123456",
"deposit_address_id": "a1b2c3d4e5f6789012345678abcdef01",
"hash": "0xabc...",
"customer": {
"id": "1b04b3ffb7f0458db6357e8b152977b9"
},
"created_at": "2026-10-10T12:05:00.000000Z"
}
}
Stablecoin Payout
Event: stablecoin.payout.status_updated
Fired when status moves to pending, success, or failed. On-chain confirmation sets success and usually fills tx_hash. On failed, Afripay refunds amount + charges.
{
"event": "stablecoin.payout.status_updated",
"data": {
"id": "a1b2c3d4e5f6789012345678abcdef01",
"reference": "sc_payout_001",
"trx_id": "SCP1234567890",
"currency": "USDT",
"network": "TRC20",
"address": "TXyzExampleDestinationAddress123456",
"amount": "100.00",
"charges": "1.00",
"status": "success",
"tx_hash": "0xabc...",
"remarks": "Vendor payout",
"created_at": "2026-10-16T12:00:00+00:00"
}
}
NGN Payout
Event: payout.status_updated
Use id to match the payout and status for the outcome. Status values: queued, pending, success, failed.
{
"event": "payout.status_updated",
"data": {
"id": "e2c83407880211f183520e74f6457b17",
"reference": "merchant_ref_abc123",
"trx_id": "PO982461972",
"amount": "5000.00",
"charges": "50.00",
"status": "success",
"remarks": "Services payment",
"account_number": "3504939056",
"bank_code": "090823",
"bank_name": "Partner Bank",
"account_name": "Sandbox Individual Account",
"created_at": "2026-07-25T14:40:00+00:00"
}
}
Transactions
Fetch your payment history. Each item includes an id (use this to identify the transaction in your system) and a short trx code (suitable to show on receipts or to customers).
25. List Transactions
Filter by type (optional slug)
| Slug | What it returns |
|---|---|
money_out | Withdrawals and payouts |
merchant_payment | Payments received by your merchant account |
received_payment | Payments from Afripay wallet users |
pay_link | Payments made through payment links |
{
"id": "e2c83407880211f183520e74f6457b17",
"trx": "PO982461972",
"transaction_type": "MONEY-OUT",
"request_amount": "5000.00 NGN",
"status": "Pending",
"date_time": "2026-07-25T14:40:00.000000Z"
}
Error Responses
401 Unauthorized
{
"status": false,
"message": "Client credentials are required.",
"data": null
}
422 Validation Error
{
"status": false,
"message": "The bvn field is required.",
"data": {
"errors": {
"bvn": ["The bvn field is required."],
"address": ["The address field is required."]
}
}
}