Bill payments overview
Pay electricity, cable TV, and betting wallet funding using the same API as Airtime and Airtime Async. Authentication headers are unchanged. Response field rules: Field conventions.
| Product | Guide | Use msisdn as |
|---|---|---|
ELECTRICITY | Electricity | Meter number (prepaid or postpaid) |
CABLE | Cable TV | Smartcard / IUC number |
BETTING | Betting | Customer ID on the betting platform |
POST https://api.subthingy.io/v1/transactionsSync on the external API — the create response includes the final outcome in one response for electricity, cable, and betting. Airtime and data on the same route are async — see Airtime Async.
Contact your account manager to enable bill products and betting on your merchant profile.
Plan catalogs
Names and amounts (no plan IDs): Catalog — electricity, cable, betting.
Each bill product is identified by a numeric plan_code (catalog plan id). Provider routing fields are resolved server-side — you never send raw service_id or variation_id.
GET /v1/plans/bills/{product}{product} is electricity, cable, or betting. Optional filter: ?network=Ikeja Electric (electricity) or ?network=DSTV (cable).
curl "https://api.subthingy.io/v1/plans/bills/electricity?network=Ikeja Electric" \
-H "X-Merchant-Key: mk_your_key_id" \
-H "X-Merchant-Secret: your_issued_secret"HTTP 200:
{
"status": "2000",
"description": "Operation Successful-BILLS-ELECTRICITY",
"data": [
{
"id": 1001,
"name": "Ikeja Electric Prepaid",
"network": "Ikeja Electric",
"product": "ELECTRICITY",
"amount_type": "variable"
},
{
"id": 1002,
"name": "Ikeja Electric Postpaid",
"network": "Ikeja Electric",
"product": "ELECTRICITY",
"amount_type": "variable"
}
]
}| Field | Description |
|---|---|
id | Pass as plan_code on verify and purchase |
amount_type | variable (you set amount) or fixed (amount is set from catalog) |
price | Present on fixed cable bouquets |
Pass the returned plan id as plan_code. Product-specific network lists: Electricity, Cable TV, Betting.
Betting is opt-in per merchant. Contact support if your profile is not enabled.
Verify customer (optional)
Customer verification also runs automatically before purchase. To validate a meter or smartcard without creating a transaction, use the same merchant credentials as purchase:
POST /v1/transactions/verifycurl -X POST "https://api.subthingy.io/v1/transactions/verify" \
-H "Content-Type: application/json" \
-H "X-Merchant-Key: mk_your_key_id" \
-H "X-Merchant-Secret: your_issued_secret" \
-d '{
"merchant": "YOUR_MERCHANT",
"msisdn": "10000000001",
"network": "Ikeja Electric",
"product": "ELECTRICITY",
"plan_code": "1001"
}'| Field | Required | Description |
|---|---|---|
merchant or merchant_code | Yes | Your merchant profile |
msisdn or customer_msisdn | Yes | Meter, smartcard, or betting ID |
network | Yes | Biller name (e.g. Ikeja Electric) |
product | Yes | ELECTRICITY, CABLE, or BETTING |
plan_code | Yes* | Catalog id from Plan catalogs |
service_id | Yes* | Legacy service code — omit when using plan_code |
* Send plan_code (recommended) or service_id, not both.
HTTP 200 (Subthingy envelope):
{
"state": "ok",
"code": "00",
"message": "customer verified",
"result": {
"product": "ELECTRICITY",
"network": "Ikeja Electric",
"msisdn": "10000000001",
"customer_name": "Test Customer",
"customer_address": "Ikeja Electric - Test Address",
"min_amount": "1000",
"max_amount": "50000"
}
}Electricity verify may also include disco details when the biller returns them: meter_number, arrears, account_type, meter_type, district, business_unit, district_reference, and tariff. Empty values are omitted. min_amount / max_amount are purchase limits (catalog floor when the meter reports 0), not arrears.
No request_ref on verify. You may skip verify and proceed directly to purchase.
HTTP 400 — the meter, smartcard, or betting ID was rejected, or the request was invalid. This is not an outage. Do not retry as if the API is down. message is the biller’s reason when available:
{
"status": "error",
"code": 400,
"message": "This meter is not correct or is not a valid Ibadan Electric prepaid meter. Please check and try again"
}Verify errors use status, code, and message (not the success state / result envelope).
HTTP 502 — verify could not be completed. Retry with backoff. message is generic.
Product-specific guides: Electricity, Cable TV, Betting.
Check status (optional)
Bill purchases return the final outcome on create. To re-fetch a transaction later, use the same status endpoint as airtime and data:
GET /v1/transactions/status?request_ref={request_ref}curl "https://api.subthingy.io/v1/transactions/status?request_ref=subthingy-bill-ikeja-001" \
-H "X-Merchant-Key: mk_your_key_id" \
-H "X-Merchant-Secret: your_issued_secret"Status poll uses state, code, message, payload (different from create). Full shape: Airtime Async — Check status.
List transactions
Bill payments appear in the standard transaction list:
GET /v1/transactions?page=1&limit=20Filter by product in your integration layer using the product field on each row (ELECTRICITY, CABLE, BETTING).