Guide
Pay bills
Sell airtime, data, electricity, cable TV and more. Look up the product, validate the customer, pay from your wallet, and let webhooks tell you how it ended.
Bill payments are paid from your Lint NGN wallet. Fund it first; each order debits the price plus any service fee.
Find the product
List billers, filtered by name or category:
curl "https://staging.lint.finance/api/partner/v1/vas/billers?category_code=utilities&per_page=20" \
-H "Authorization: Bearer $LINT_ACCESS_TOKEN" \
-H "Accept: application/json"Then fetch one biller for its products. Each product has the product_fields the customer must fill in and its prices:
{
"id": 256,
"name": "Ikeja Electric Prepaid",
"amount": null,
"fixed_amount": false,
"minimum_payable_amount": 100000,
"fee": 10000,
"validate": true,
"product_fields": [
{
"id": 344,
"display_name": "Meter number",
"variable_name": "meter_number",
"customer_id_field": true,
"validate_user_entry": true,
"required": true
}
]
}fixed_amount: trueproducts (most data bundles) have a set price; don't send an amount.- Otherwise send
amount_enteredin kobo, at leastminimum_payable_amount. Here that's 100000₦1,000.00. validate: truemeans at least one field should be checked with the biller first.
Validate the customer
For products with validate: true, check the customer identifier before taking money. The biller returns the name and details it holds — show them to your user.
curl -X POST https://staging.lint.finance/api/partner/v1/vas/billers/validate-customer \
-H "Authorization: Bearer $LINT_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{ "product_id": 256, "customer_id": "45071234567" }'Pay
curl -X POST https://staging.lint.finance/api/partner/v1/vas/billers/create-order \
-H "Authorization: Bearer $LINT_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Idempotency-Key: bill-order-77120" \
-d '{
"biller_product_id": 256,
"amount_entered": 500000,
"currency": "NGN",
"customer_name": "Ada Obi",
"product_field_entries": [
{ "field_id": 344, "value": "45071234567" }
]
}'Send one entry in product_field_entries for every required field. Lint creates the order, debits your wallet and sends it to the biller in the same call.
Read the outcome
The response is the order, returned with 201. Its status is one of:
status | Meaning | What to do |
|---|---|---|
SUCCESSFUL | Delivered. For electricity, the token is in response_pin. | Show the result. |
PENDING | The biller hasn't answered yet. Lint keeps retrying. | Wait for the webhook. |
FAILED | Not delivered. The payment is refunded. | Show message; let the user try again. |
A biller rejection during the call itself (for example an invalid meter number) returns 502 bill_order_failed; the message has the order ID and the biller's reason, and the money comes back to your wallet.
Pending orders and webhooks
Some billers take minutes. Lint retries a pending order on its own and sends the final outcome to your registered webhook endpoints:
bill_order.completedwhen it is delivered;bill_order.failedwhen it has failed for good, followed bybill_order.payment_refundedonce the money is back in your wallet.
Each is sent once per order. Subscribe to all three under Integrations → Webhooks, and confirm with GET /vas/billers/orders/{id} whenever you need the current state.
Retries and duplicates
- Always send an
Idempotency-Key. After a timeout, retry with the same key; the order is never paid twice. - Separately, Lint refuses an identical successful order (same product, customer and amount) placed within a minute of the last one, with
422 validation_failedonbiller_product_id. That guards against double taps; use a newIdempotency-Keyonly for a genuinely new purchase.