ZebaPay API v1

Base URL: https://zebapay.com/api/public/v1. All amounts are integers in kobo (₦1 = 100). JSON in, JSON out.

Authentication

Create a secret key in Console → Developer Keys (requires approved KYC). Send it as a bearer token. Sandbox keys start with zbp_test_, live keys with zbp_live_.

curl https://zebapay.com/api/public/v1/balance \
  -H "Authorization: Bearer zbp_test_…"

Errors

401 invalid_api_key, 403 kyc_not_approved, 422 validation_error, 400 business errors (e.g. "Insufficient balance"), 404 not_found.

Balance

GET/balance
{ "data": [{ "id": "…", "currency": "NGN", "balance": 1250000 }] }

Transactions

GET/transactions

Latest 100 transactions, newest first.

Virtual Accounts

GET/virtual-accounts

Dedicated 10-digit NUBAN accounts are issued instantly on ZebaPay's multi-rail infrastructure, with automatic failover between rails. Inbound transfers credit your wallet and fire wallet.funded.

Recipients

GET/recipients

POST/recipients
{ "account_name": "Ada Obi", "account_number": "0123456789",
  "bank_code": "058", "bank_name": "GTBank" }

Transfers

POST/transfers

Send an Idempotency-Key header to safely retry. Minimum ₦100. Fees: ₦20 (≤₦5,000), ₦25 (≤₦50,000), ₦50 above.

{ "recipient_id": "…", "amount": 2500000, "narration": "Vendor payout" }

Identity verification fees

Tier 1 (BVN) verification is free. A Tier 2 upgrade runs a NIN identity check and costs a one-time ₦100, debited from the wallet balance and recorded as a fee transaction. Tier 3 (CAC business) upgrades carry no extra fee. If the balance is below ₦100 the upgrade is rejected with 400 Insufficient balance.

Sandbox

POST/sandbox/fund

Test keys only. Simulates an inbound transfer.

{ "amount": 5000000 }

Webhooks

Events: wallet.funded, transfer.successful, ping. Each request carries x-zebapay-timestamp and x-zebapay-signature = HMAC-SHA256(secret, timestamp + "." + rawBody) in hex. Non-2xx responses are retried with exponential backoff up to 6 attempts.

import crypto from "crypto";
const expected = crypto.createHmac("sha256", SECRET)
  .update(req.headers["x-zebapay-timestamp"] + "." + rawBody).digest("hex");
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers["x-zebapay-signature"]));