API documentation

Build payments into your product.

Collect from Rainpay customers, pay out to any Nigerian bank, read your balances and get told when things happen. Amounts are always whole kobo — ₦5,000 is 500000.

Keys and safety

  • Base address: https://web.rainpay.ng/api/public/v1
  • Authorisation: send Authorization: Bearer sk_live_… on every request. Never put a secret key in a browser, an app or a public repository.
  • Test first: keys beginning sk_test_ behave like the real thing but never move real money.
  • Live keys: available once your CAC registration is confirmed. Add your server addresses in the Developer tab and anything else is refused.
  • Safe retries: send your own reference. Reusing one returns duplicate_reference instead of paying twice.

Endpoints

POST/charges/initialize

Start a payment

Creates a payment and gives you a link. Send your customer to that link — they confirm with their Rainpay PIN and the money lands in your business account.

  • amountRequired. Whole kobo. ₦5,000 is 500000.
  • referenceOptional. Your own reference. Must be unique; we make one if you leave it out.
  • customer_emailOptional.
  • customer_nameOptional.
  • descriptionOptional. Shown to the customer.
  • metadataOptional. Any JSON object you want back later.
curl -X POST https://web.rainpay.ng/api/public/v1/charges/initialize \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{ "amount": 500000, "description": "Order 1042" }'

Response

{
  "data": {
    "reference": "RPC-M2XK9P-A7B3C1",
    "amount": 500000,
    "currency": "NGN",
    "status": "pending",
    "expires_at": "2026-01-14T10:31:00.000Z",
    "checkout_url": "https://web.rainpay.ng/pay/RPC-M2XK9P-A7B3C1"
  }
}

GET/charges/{reference}

Check a payment

Ask us whether a payment was completed. Do this before you release goods — never trust the browser alone.

  • referenceThe reference from the payment you created.
curl https://web.rainpay.ng/api/public/v1/charges/RPC-M2XK9P-A7B3C1 \
  -H "Authorization: Bearer sk_test_..."

Response

{
  "data": {
    "reference": "RPC-M2XK9P-A7B3C1",
    "amount": 500000,
    "currency": "NGN",
    "status": "completed",
    "paid_at": "2026-01-14T10:14:22.000Z",
    "description": "Order 1042",
    "metadata": {}
  }
}

GET/balances

Read your balances

Lists your main account and every sub-account, with what is in each.

curl https://web.rainpay.ng/api/public/v1/balances \
  -H "Authorization: Bearer sk_live_..."

Response

{
  "data": {
    "currency": "NGN",
    "total": 4250000,
    "accounts": [
      { "id": "…", "name": "Main account", "type": "main", "balance": 4000000, "status": "active" },
      { "id": "…", "name": "Ads budget", "type": "sub", "balance": 250000, "status": "active" }
    ]
  }
}

POST/transfers

Pay out to a bank

Sends money from a business account to any Nigerian bank account. With a test key we record the payout and return success without moving real money.

  • amountRequired. Whole kobo, at least ₦100.
  • account_numberRequired. 10 digits.
  • bank_codeRequired.
  • account_nameRequired. Check the name first — payouts are final.
  • narrationOptional. Up to 60 characters.
  • referenceOptional. Your own reference; reusing one is refused, so retries are safe.
  • source_account_idOptional. Pay from a sub-account instead of the main account.
curl -X POST https://web.rainpay.ng/api/public/v1/transfers \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 250000,
    "account_number": "0123456789",
    "bank_code": "058",
    "account_name": "Chinedu Okafor",
    "narration": "January payout",
    "reference": "payout-2026-01-14-001"
  }'

Response

{
  "data": {
    "reference": "payout-2026-01-14-001",
    "amount": 250000,
    "currency": "NGN",
    "status": "pending",
    "account_number": "0123456789",
    "account_name": "Chinedu Okafor",
    "balance_after": 3750000
  }
}

Webhooks

Set your address in the Developer tab and pick the events you care about. We sign every message with your webhook secret — check the signature before you act on it. Answer with a 2xx quickly; anything else is retried with growing gaps between tries.

  • charge.completedA customer finished a payment.
  • charge.failedA payment attempt failed.
  • transfer.successA payout reached the bank.
  • transfer.failedA payout failed and the money is back.
  • subwallet.creditedA sub-account received money.
  • dispute.openedA customer disputed a payment.
import crypto from "node:crypto";

app.post("/rainpay/events", express.raw({ type: "*/*" }), (req, res) => {
  const header = req.get("X-Rainpay-Signature") ?? "";           // t=1736847262,v1=9f86d0…
  const parts = new URLSearchParams(header.replace(/,/g, "&"));
  const expected = crypto
    .createHmac("sha256", process.env.RAINPAY_WEBHOOK_SECRET)
    .update(`${parts.get("t")}.${req.body.toString()}`)
    .digest("hex");

  const given = Buffer.from(parts.get("v1") ?? "");
  const mine = Buffer.from(expected);
  if (given.length !== mine.length || !crypto.timingSafeEqual(given, mine)) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(req.body.toString());
  res.sendStatus(200);          // answer fast, then do the work
  handle(event);
});

Errors

Failures come back as { "error": { "code": …, "message": … } }.

  • 401unauthorizedThe key is missing, wrong or revoked.
  • 403ip_not_allowedLive key used from an address that is not on your allowlist.
  • 400invalid_bodyThe body was not JSON.
  • 422invalid_requestA field is missing or out of range.
  • 402insufficient_fundsNot enough money in the source account.
  • 404account_not_foundThat source account does not belong to you.
  • 409duplicate_referenceYou already used that reference.
  • 502payout_failedThe bank refused the payout. Nothing was taken.