> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useglyde.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Bill Payments

> Buy airtime, data, electricity and cable TV from your naira wallet

Pay bills on behalf of your customers straight from your Glyde wallet. Four bill categories are supported:

| Category | `category` value | Customer identifier | Priced by |
| - | - | - | - |
| Airtime | `airtime` | Phone number | The `amount` you send (min ₦50) |
| Data | `data` | Phone number | The bundle's catalog price |
| Electricity | `electricity` | Meter number | The `amount` you send (min ₦100) |
| Cable TV | `cable` | Smartcard / IUC number | The bouquet's catalog price |

Every purchase debits your **NGN wallet** for the bill amount plus the bill payment fee.

## How Bill Payments Work

**1. Find the Biller**

List the billers in a category to get the biller's `service_code`. For airtime, Network Lookup resolves it from the phone number.

**2. Pick a Product or Validate the Customer**

For data and cable, list the bundles or bouquets to get a product ID and its price. For electricity and cable, validate the meter or smartcard to get the customer's name.

**3. Make the Purchase**

Call the purchase endpoint with your unique `reference`. The response tells you whether the bill is `successful`, `failed` or `pending`.

**4. Handle the Webhook**

Pending purchases settle later. Glyde sends a `bill.successful` or `bill.failed` webhook when they do.

## Prerequisites

Before paying bills:

* **Fund your NGN wallet** — It must cover the bill amount and the fee. Bill payments are only paid from a naira wallet
* **Configure webhooks** — Bill purchases require a webhook URL to be set in your dashboard for the environment you're calling
* **Whitelist your servers** — In production, purchase endpoints are protected by [IP whitelisting](https://docs.useglyde.co/api-reference/ip-whitelisting.md) when it's enabled on your business

## Step 1: Find the Biller

List the billers in a category:

```bash theme={null}
GET /v1/bills/billers?category=electricity
```

```json theme={null}
{
  "status": "success",
  "message": "Billers retrieved",
  "data": [
    { "service_code": "ikeja-electric-prepaid", "name": "Ikeja Electric (Prepaid)" },
    { "service_code": "ikeja-electric-postpaid", "name": "Ikeja Electric (Postpaid)" }
  ]
}
```

For airtime, you can skip the list and resolve the network from the customer's phone number:

```bash theme={null}
GET /v1/bills/network-lookup?phone_number=08031234567
```

```json theme={null}
{
  "status": "success",
  "message": "Network resolved",
  "data": { "network": "mtn", "service_code": "mtn-airtime" }
}
```

> [!TIP]
>
> Biller lists, data bundles and cable bouquets change rarely. Cache them on your side rather than fetching them on every purchase.

## Step 2: Pick a Product or Validate the Customer

### Data bundles and cable bouquets

Data and cable are sold as fixed-price products. List them for the biller's `service_code`:

```bash theme={null}
GET /v1/bills/data-bundles?service_code=mtn-data
```

```json theme={null}
{
  "status": "success",
  "message": "Data bundles retrieved",
  "data": [
    { "bundle_id": "mtn-1gb-30days", "name": "1GB - 30 Days", "amount": "500.00" },
    { "bundle_id": "mtn-2gb-30days", "name": "2GB - 30 Days", "amount": "1000.00" }
  ]
}
```

```bash theme={null}
GET /v1/bills/cable-bouquets?service_code=dstv
```

```json theme={null}
{
  "status": "success",
  "message": "Cable bouquets retrieved",
  "data": [
    { "bouquet_id": "dstv-compact", "name": "Compact", "amount": "19000.00", "duration": "1 Month" }
  ]
}
```

You don't send an amount when buying data or cable. Glyde charges the product's current catalog price, so the price your customer sees should come from these lists.

### Meter and smartcard enquiry

Before buying electricity or cable, validate the customer's meter or smartcard. This confirms it exists and returns the name on the account:

```bash theme={null}
GET /v1/bills/meter-enquiry?service_code=ikeja-electric-prepaid&meter_number=04123456789
```

```json theme={null}
{
  "status": "success",
  "message": "Meter validated",
  "data": {
    "meter_number": "04123456789",
    "customer_name": "John Doe",
    "address": "12 Allen Avenue, Ikeja",
    "access_token": null
  }
}
```

```bash theme={null}
GET /v1/bills/smartcard-enquiry?service_code=dstv&smartcard_number=1234567890
```

```json theme={null}
{
  "status": "success",
  "message": "Smartcard validated",
  "data": {
    "smartcard_number": "1234567890",
    "customer_name": "John Doe",
    "current_bouquet": "Compact"
  }
}
```

Display `customer_name` to your user for confirmation, then send it as `customer_name` on the purchase. If Meter Enquiry returns an `access_token`, send that on the electricity purchase too.

## Step 3: Make the Purchase

Each category has its own purchase endpoint. All of them take your unique `reference` and the biller's `service_code`.

```json Airtime theme={null}
// POST /v1/bills/airtime
{
  "reference": "bill_abc123",
  "service_code": "mtn-airtime",
  "phone_number": "08031234567",
  "amount": 1000
}
```

```json Data theme={null}
// POST /v1/bills/data
{
  "reference": "bill_abc124",
  "service_code": "mtn-data",
  "phone_number": "08031234567",
  "bundle_id": "mtn-2gb-30days"
}
```

```json Electricity theme={null}
// POST /v1/bills/electricity
{
  "reference": "bill_abc125",
  "service_code": "ikeja-electric-prepaid",
  "meter_number": "04123456789",
  "customer_name": "John Doe",
  "amount": 5000,
  "phone_number": "08031234567",
  "email": "john@example.com"
}
```

```json Cable TV theme={null}
// POST /v1/bills/cable
{
  "reference": "bill_abc126",
  "service_code": "dstv",
  "smartcard_number": "1234567890",
  "bouquet_id": "dstv-compact",
  "customer_name": "John Doe"
}
```

**Key fields:**

* `reference` — Your unique identifier for this purchase. A reference can only be used once
* `service_code` — The biller, from List Billers or Network Lookup
* `phone_number` — 11 digit Nigerian mobile number starting with `0` (airtime and data)
* `amount` — Amount in naira, with at most 2 decimal places (airtime and electricity only)
* `bundle_id` / `bouquet_id` — The product to buy (data and cable only)
* `customer_name` — The name returned by Meter or Smartcard Enquiry (electricity and cable)

The response is the bill payment transaction:

```json theme={null}
{
  "status": "success",
  "message": "Bill payment successful",
  "data": {
    "reference": "550e8400-e29b-41d4-a716-446655440000",
    "merchant_reference": "bill_abc125",
    "type": "debit",
    "amount": "5000",
    "status": "successful",
    "fee": "0",
    "bill": {
      "category": "electricity",
      "service_code": "ikeja-electric-prepaid",
      "customer_identifier": "04123456789",
      "customer_name": "John Doe",
      "product_code": null,
      "product_name": null,
      "token": "1234-5678-9012-3456-7890",
      "units": "42.5 kWh"
    },
    "created_at": "2026-10-21T10:30:00.000000Z"
  }
}
```

For prepaid electricity, give the customer the meter token from `bill.token`. For data and cable, `bill.product_code` and `bill.product_name` hold the bundle or bouquet that was bought.

## Bill Payment Lifecycle

A purchase request returns `200` whenever the bill was submitted to the biller. Always check `data.status`:

| Status | Message | Meaning |
| - | - | - |
| `successful` | `Bill payment successful` | The bill was delivered to the customer |
| `failed` | `Bill payment failed` | The biller rejected the bill. The amount and fee are already refunded to your wallet |
| `pending` | `Bill payment is processing` | The biller hasn't confirmed yet. Glyde keeps checking and sends a webhook when it settles |

Most purchases settle immediately. A purchase stays `pending` when the biller is slow to respond; Glyde rechecks pending purchases every minute until they're final. You can also check one at any time with [Fetch Transaction](https://docs.useglyde.co/api-reference/transactions/fetch.md) using Glyde's `reference` — bill payment transactions include the same `bill` object.

## Webhook Notification

When a pending purchase settles, or as soon as a purchase completes, you'll receive a webhook:

```json theme={null}
{
  "event": "bill.successful",
  "data": {
    "reference": "550e8400-e29b-41d4-a716-446655440000",
    "merchant_reference": "bill_abc125",
    "type": "debit",
    "amount": 5000,
    "status": "successful",
    "fee": 0,
    "session_id": null,
    "created_at": "2026-10-21T10:30:00.000000Z",
    "bill": {
      "category": "electricity",
      "service_code": "ikeja-electric-prepaid",
      "customer_identifier": "04123456789",
      "customer_name": "John Doe",
      "product_code": null,
      "product_name": null,
      "token": "1234-5678-9012-3456-7890",
      "units": "42.5 kWh"
    }
  }
}
```

A failed bill sends `bill.failed` with `"status": "failed"`. By the time you receive it, the amount and fee have been refunded to your NGN wallet.

## Common Errors

Purchase errors return `422` and no funds are debited.

| Error | Cause | Solution |
| - | - | - |
| Insufficient funds, can not process this bill payment. | NGN wallet balance doesn't cover the amount plus fee | Fund your wallet before retrying |
| Duplicate transaction reference | Reference already used | Generate a new unique reference |
| Webhook url for this business is not set | No webhook URL for this environment | Configure webhooks in your dashboard |
| Bill payments require a naira wallet. | Your business has no NGN wallet | Contact support to enable an NGN wallet |
| The selected data bundle is not available for this biller. | `bundle_id` isn't in the biller's current list | Refresh the bundles for the `service_code` |
| The selected bouquet is not available for this biller. | `bouquet_id` isn't in the biller's current list | Refresh the bouquets for the `service_code` |
| The phone number must be an 11 digit Nigerian mobile number. | `phone_number` isn't in `0XXXXXXXXXX` format | Send 11 digits starting with `0` |
| Transaction amount cannot be more than … | Amount is above your per-transaction limit | Lower the amount |
| You have reached your daily transaction limit | Today's debits plus this amount exceed your daily limit | Try again tomorrow or contact support |
| Too many bill payment requests. | Concurrent purchases for your business | Retry after a short delay |

Lookups (Network Lookup, Meter Enquiry and Smartcard Enquiry) return `422` with the biller's message when the number can't be validated.

If bill payments are switched off for your business, or the biller can't be reached, endpoints return `503`. Retry shortly.

## Fees

Each purchase incurs a bill payment fee, charged in naira and taken from your NGN wallet together with the bill amount. The fee is shown in `data.fee` on the purchase response and webhook. `data.amount` is the bill amount alone.

## Testing in Sandbox

Sandbox bill payments never reach a real biller, and purchases settle immediately — they're never `pending`. See [Testing](https://docs.useglyde.co/api-reference/testing.md#test-bill-payments) for the test billers, products and failing customer numbers.

## Best Practices

1. **Validate first** — Always run Meter or Smartcard Enquiry and confirm the name with your customer before buying electricity or cable
2. **Price from the catalog** — Show data and cable prices from the bundle and bouquet lists, since that's what Glyde charges
3. **Use unique references** — Generate references you can trace back to the order (e.g., `bill_{order_id}`)
4. **Treat `pending` as in progress** — Don't retry a pending purchase with a new reference; wait for the webhook or fetch the transaction
5. **Deliver tokens reliably** — Store `bill.token` for prepaid electricity so you can resend it if your customer loses it


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.