API documentation
The UltraPay Pay API follows Flutterwave's v3 conventions, so existing integrations port over with a new base URL and keys. Base URL: https://api.ultrapay.ng/v3
Authentication
Send your secret key as a bearer token. Test keys start UPSECK_TEST-, live keys UPSECK_LIVE-. The key decides the mode. Never put a secret key in a browser or app; use your public key (UPPUBK_…) with inline checkout instead.
Authorization: Bearer UPSECK_TEST-xxxxxxxxxxxxxxxx-X
Create a payment (hosted checkout)
POST https://api.ultrapay.ng/v3/payments
{
"tx_ref": "order-1001",
"amount": 5000,
"currency": "NGN",
"redirect_url": "https://yourshop.com/payment/callback",
"customer": { "email": "ada@example.com", "name": "Ada Obi", "phonenumber": "08030000000" },
"customizations": { "title": "Your Shop", "description": "Order 1001", "logo": "https://yourshop.com/logo.png" },
"meta": { "order_id": 1001 }
}
200 OK
{ "status": "success", "message": "Hosted Link", "data": { "link": "https://api.ultrapay.ng/pay/…" } }Redirect the customer to data.link. When they finish, we send them to your redirect_url with ?status=successful|failed|cancelled&tx_ref=…&transaction_id=…. Always verify the transaction before giving value: never trust the redirect alone.
Verify a transaction
GET https://api.ultrapay.ng/v3/transactions/{transaction_id}/verify
GET https://api.ultrapay.ng/v3/transactions/verify_by_reference?tx_ref=order-1001
{ "status": "success", "data": { "id": 100023, "tx_ref": "order-1001", "flw_ref": "UPAY-…",
"amount": 5000, "currency": "NGN", "charged_amount": 5000, "app_fee": 70, "amount_settled": 4930,
"status": "successful", "payment_type": "ultrapay_account", "customer": { … }, "meta": { … } } }Give value only when data.status is successful, data.amount matches and data.currency is NGN.
List transactions and balances
GET https://api.ultrapay.ng/v3/transactions?page=1&status=successful&from=2026-10-01&to=2026-10-31 GET https://api.ultrapay.ng/v3/balances
Webhooks
Set a webhook URL and a secret hash in the dashboard (per mode). We POST when a payment completes, with your secret hash in the verif-hash header. Reply 200 quickly; we retry failures for about 15 hours.
POST https://yourapp.com/webhooks/ultrapay
verif-hash: <your secret hash>
{ "event": "charge.completed",
"data": { "id": 100023, "tx_ref": "order-1001", "flw_ref": "UPAY-…", "amount": 5000, "currency": "NGN",
"status": "successful", "payment_type": "ultrapay_account", "customer": { … }, "meta": { … } } }Inline checkout (no backend)
<script src="https://api.ultrapay.ng/inline.js"></script>
<script>
UltraPayCheckout({
public_key: "UPPUBK_TEST-xxxx-X",
tx_ref: "order-" + Date.now(),
amount: 5000,
customer: { email: "ada@example.com", name: "Ada Obi" },
redirect_url: "https://yourshop.com/payment/callback"
});
</script>Open accounts
Open real UltraPay MFB accounts. A customer account is opened in your customer's own name: their BVN or NIN is checked with the registry, and the name and date of birth you send must match it. It belongs to your customer, and this API can never debit it. A sub account is an extra account under your own business (for a branch, an outlet or a collection), held by the same customer as your settlement account. Live use needs UltraPay to enable it for your business.
POST https://api.ultrapay.ng/v3/accounts
{
"type": "customer",
"reference": "cust-001",
"first_name": "Amina", "last_name": "Yusuf",
"dob": "1994-03-14", "gender": "female",
"phone_number": "08031234567", "email": "amina@example.com",
"address": "12 Ahmadu Bello Way, Kano",
"bvn": "22200000000" // or "nin": "…", exactly one
}
POST https://api.ultrapay.ng/v3/accounts
{ "type": "sub", "reference": "branch-kano", "label": "Kano branch" }
→ 201
{ "status": "success", "message": "Account created",
"data": { "id": "…", "type": "customer", "reference": "cust-001",
"account_number": "5090903001", "account_name": "AMINA YUSUF",
"bank_name": "ULTRAPAY MFB", "bank_code": "090253", "status": "active", … } }References are unique per business and mode. 409: the reference is taken, or the person already holds an UltraPay account. 422: no record for that BVN/NIN, or the name or date of birth does not match it.
Account balances
GET https://api.ultrapay.ng/v3/accounts?page=1&type=customer // the accounts you opened
GET https://api.ultrapay.ng/v3/accounts/{account_number} // one of them, with its live balance
→ { "data": { "account_number": "5090903001", …,
"balance": { "available_balance": 25000, "ledger_balance": 25000 } } }
GET https://api.ultrapay.ng/v3/balances // your settlement account
→ { "data": [ { "currency": "NGN", "available_balance": 1250400.5, "ledger_balance": 1250400.5,
"collected": 980000, "fees": 13720, "net": 966280, … } ] }You can read only accounts you opened. balance is null when the bank cannot be reached.
Banks and name check
GET https://api.ultrapay.ng/v3/banks
→ { "data": [ { "code": "000013", "name": "GTBANK PLC" }, { "code": "090253", "name": "ULTRAPAY MFB" }, … ] }
POST https://api.ultrapay.ng/v3/accounts/resolve
{ "account_number": "0123456789", "account_bank": "000013" }
→ { "data": { "account_number": "0123456789", "account_name": "ADA OKAFOR" } }Payouts
Send money from your settlement account to any Nigerian bank account, or to another UltraPay account (account_bank 090253 or ultrapay). Payouts only ever debit your own settlement account, within the per-payout and daily limits set for your business. The recipient's name is confirmed with their bank first.
POST https://api.ultrapay.ng/v3/transfers
{
"reference": "payout-2026-10-10-001",
"amount": 25000,
"account_bank": "000013",
"account_number": "0123456789",
"narration": "October commission"
}
→ { "status": "success", "message": "Transfer successful",
"data": { "id": 500001, "status": "SUCCESSFUL", "amount": 25000, "fee": 0,
"full_name": "ADA OKAFOR", "reference": "payout-2026-10-10-001", … } }
GET https://api.ultrapay.ng/v3/transfers?page=1&status=successful
GET https://api.ultrapay.ng/v3/transfers/{id}statusisSUCCESSFUL,FAILED(with the reason incomplete_message; no money moved) orPENDING.- PENDING means the bank has not answered yet. The money may have moved, so do not send it again: check
GET /transfers/{id}or wait for the webhook. - A reference can be used once. Sending the same one again returns
409and pays nothing, so retry with the same reference. - Each final payout sends a
transfer.completedwebhook with the sameverif-hashheader as payments.
Test mode
- UltraPay account
0000000000: payment succeeds. - UltraPay account
0000000001: payment fails with insufficient funds. - The confirmation code is always
123456. No SMS is sent and no money moves. - Accounts opened in test mode get numbers starting
99and a zero balance; no BVN or NIN is checked. - Payouts to
0000000000succeed and to0000000001fail with insufficient funds, at anyaccount_bank; other numbers fail as not found.
Going live
Add your UltraPay MFB settlement account in Settings and request live access. After approval you get live keys. Live payments are debited from the customer's UltraPay account (confirmed with a code sent to the phone registered on that account) and credited instantly to your settlement account. Fee: 1.4% per transaction, capped at ₦2,000.
Errors
{ "status": "error", "message": "Invalid authorization key", "data": null }
401 bad or revoked key · 403 suspended or not live-enabled · 400 invalid request · 404 not found