Bills is switched off for every business until Platnova enables it. Until then each
/v1/bills request answers 400 with “The bills feature is not enabled for your business”. Contact [email protected] to have it enabled.How a top-up works
- List the billers with
GET /v1/bills/betting/billersand let the payer pick one. Cache the list for up to an hour. - Validate the customer with
POST /v1/bills/betting/validate. The biller answers with the name on the account. - Show that name to the payer and have them confirm it before you go on.
- Create the top-up with
POST /v1/bills/betting. Your NGN wallet is debited the amount plus any fee, and the request answers202withstatus: "processing". - Wait for the outcome: the
bill.success,bill.failedorbill.reversedwebhook, or pollGET /v1/bills/betting/{id}.
Always confirm the customer’s name
A betting account ID is short and easy to mistype, and a mistyped ID is often someone else’s real account: in testing, a random Bet9ja ID resolved to a real person. A top-up sent to the wrong account cannot be pulled back. So before every top-up:- Call
validateand show the payercustomer_nameexactly as returned. - Only continue once the payer confirms it is them.
- Send what they confirmed as
customer_name_confirmationon the purchase. We validate the customer again at purchase time and refuse the top-up with422 customer_name_mismatchif the name no longer matches, so nothing is debited.
skip_validation: true in the biller list) do not return a name. For those, the payer has only the account ID to go on: make them re-enter it rather than confirm a name, and leave customer_name_confirmation out.
Customer ID formats
Whatcustomer_id means depends on the biller, and the biller decides whether it is valid:
When an ID is refused,
validate and purchase answer 422 invalid_customer, and message carries the biller’s own explanation. Show it to the payer.
Amounts
- Amounts are in naira (NGN) and are paid from your NGN wallet.
- Every biller is open-amount: any amount from ₦100 to ₦500,000 per top-up. Some customers’ accounts carry a higher minimum, which
validatereturns asmin_amount. - Amounts outside the range are refused with
422 amount_out_of_range, withmin_amountandmax_amountindata.
Fees
Your wallet is debitedamount + fee. fee is on every bill object and is 0 unless a betting fee is set for your business. The customer receives exactly amount.
References and retries
reference is your identifier for the top-up, up to 100 characters, and must be unique across your business. Use your own order or payment ID.
- A second request with a reference you have already used is refused with
409 duplicate_referenceand debits nothing. The existing top-up is indata.bill. - If a request times out and you do not know whether it arrived, retry it with the same reference. Either it is created, or you get
409 duplicate_referencewith the top-up the first request created. Never retry with a new reference: that can top up the customer twice. - You can fetch a top-up by its
idor by yourreference.
Statuses
A top-up that cannot be confirmed straight away stays
processing while we check it with the biller. That can take longer than usual; do not treat processing as failed, and do not send the top-up again under a new reference.
failure_reason on failed and reversed:
Temporarily unavailable
While betting top-ups are unavailable on our side, new purchases are refused before anything is debited with503 provider_unavailable. Retry after a few minutes.
Errors
Refusals carry a stable code inerror[0] and data.code:
Authentication
Send your API key in theX-API-KEY header. A request made with a dashboard session needs a team member with the permission to send payments, and a completed two-factor check, to create a top-up.
Webhooks
Subscribe your endpoint tobill.success, bill.failed and bill.reversed (or bill.*). Each carries the bill:
bill.failed and bill.reversed, status is failed or reversed and failure_reason is set; amount and fee are already back in your wallet.
Webhooks are retried if your endpoint does not answer 2xx, so the same event can arrive more than once. Use data.id or data.reference to process each top-up once.
Endpoints
- List betting billers:
GET /v1/bills/betting/billers - Validate a customer:
POST /v1/bills/betting/validate - Create a top-up:
POST /v1/bills/betting - Get a top-up:
GET /v1/bills/betting/{id} - List top-ups:
GET /v1/bills/betting

