Skip to main content
POST
Pay a bill

Statuses

A failed bill payment refunds itself. Sakawave posts a reversal against the same account, so you do not need to claim the money back — but the payment stays in the list as failed with a failureReason.

What comes from where

The enquiry decides most of the payment. customerName is used only when the biller returned no name of its own, and for airtime and data the recipient is the phone number from the enquiry, not the phoneNumber in this request. amount may be omitted when the enquiry or the package already fixes it. Where it is required and disagrees with the fixed price, the payment is refused rather than adjusted.

Tokens

A biller that issues a credential — a prepaid electricity token, most often — returns it in tokenData once the payment is confirmed. Its shape varies by biller. Show it to the customer and store it: it is not re-issued.

Idempotency

externalReference is the key. Reusing it returns the existing payment whatever its status, so a retry after a failure returns the failure — the same contract as transfers, not the re-driving one that cards use. A genuine retry needs a new reference and a new enquiry.

Error codes

Authorizations

Authorization
string
header
required

A secret API key, sent as Authorization: Bearer sk_test_… in sandbox or Bearer sk_live_… in live.

Body

application/json
enquiryId
string
required

From the enquiry. Valid for ten minutes.

Required string length: 26
Pattern: ^[0-7][0-9A-HJKMNP-TV-Z]{25}$
Example:

"01K2Z3AB4C5D6E7F8G9HJKMNPQ"

accountId
string
required

The NGN account to debit.

Required string length: 26
Pattern: ^[0-7][0-9A-HJKMNP-TV-Z]{25}$
Example:

"01K2Z3AB4C5D6E7F8G9HJKMNPQ"

externalReference
string
required

Your idempotency key.

Required string length: 1 - 120
customerName
string
required

Ignored where the biller returned a name of its own.

Required string length: 2 - 200
phoneNumber
string
required

Eight to fifteen digits, optionally with a leading plus. Spaces and hyphens are stripped.

amount
string

A decimal display amount returned by transfer APIs. Preserve it as a string.

Pattern: ^-?\d+(?:\.\d+)?$
Example:

"25000.00"

email
string<email>
Maximum string length: 255

Response

Successful response.

id
string
required

A 26-character ULID.

Required string length: 26
Pattern: ^[0-7][0-9A-HJKMNP-TV-Z]{25}$
Example:

"01K2Z3AB4C5D6E7F8G9HJKMNPQ"

externalReference
string
required
paymentReference
string
required

Sakawave’s own reference for the payment.

providerTransactionId
string | null
required
status
enum<string>
required

confirmed is the success state, not completed.

Available options:
submitted,
pending,
confirmed,
failed
subCategory
string
required

What kind of bill this was: airtime, data, utility, cable_tv, betting, and so on.

amount
string
required

A decimal display amount returned by transfer APIs. Preserve it as a string.

Pattern: ^-?\d+(?:\.\d+)?$
Example:

"25000.00"

charge
string
required

A decimal display amount returned by transfer APIs. Preserve it as a string.

Pattern: ^-?\d+(?:\.\d+)?$
Example:

"25000.00"

commission
string
required

A decimal display amount returned by transfer APIs. Preserve it as a string.

Pattern: ^-?\d+(?:\.\d+)?$
Example:

"25000.00"

totalDebit
string
required

A decimal display amount returned by transfer APIs. Preserve it as a string.

Pattern: ^-?\d+(?:\.\d+)?$
Example:

"25000.00"

currency
enum<string>
required

Bill payments are NGN only.

Available options:
NGN
group
object
required
biller
object
required
package
object
required
customerReferenceLast4
string | null
required

The last four characters of the meter, account, or phone number paid.

customerName
string
required
customerAddress
string | null
required
vendStatus
string | null
required

The biller’s own delivery status, upper-cased.

providerStatusCode
string | null
required
tokenData
object | null
required

The credential the biller issued, where there is one — a prepaid electricity token, for instance. Shape varies by biller.

failureReason
string | null
required
createdAt
string<date-time>
required
Example:

"2026-08-14T09:00:00.000Z"

updatedAt
string<date-time>
required
Example:

"2026-08-14T09:00:00.000Z"