Public reference
API documentation
External API contract: authentication, payments, status, account balance and webhooks.
docs/API.md
Public · no sign-in requiredYellow Fox API — One Page
All in one place: create a payment, check its result, and receive webhooks.
- Base URL:
https://your-domain.com/api/v1 - Auth (POST requests): send
"apiToken": "<API_TOKEN>"in the JSON body - Auth (GET requests):
Authorization: Bearer <API_TOKEN>header - JSON only for POST:
Content-Type: application/json - One service:
UTILITY_PAYMENT(electricity bill or mobile recharge)
How it works
- You call
POST /recharges. The API saves the request and returns202. - The worker picks an eligible account automatically and pays the bill.
- You get the final result on your
WEBHOOK_URL.
The API never blocks while the provider is called.
1. Create a payment
POST /api/v1/recharges
Request
POST /api/v1/recharges
Content-Type: application/json
{
"apiToken": "<API_TOKEN>",
"serviceType": "UTILITY_PAYMENT",
"targetReference": "107540106678",
"amount": "100",
"currency": "INR",
"clientTransactionId": "TEST-1191161198"
}
Ready to run:
curl --location 'http://127.0.0.1:3456/api/v1/recharges' \
--header 'Content-Type: application/json' \
--data '{
"apiToken": "<API_TOKEN>",
"serviceType": "UTILITY_PAYMENT",
"targetReference": "107540106678",
"amount": "100",
"currency": "INR",
"clientTransactionId": "TEST-1191161198"
}'
Fields
| Field | Required | Notes |
|---|---|---|
apiToken |
Yes | Your API token. Removed from the record; never stored with the request. |
targetReference |
Yes | 6–20 digits, optional leading +. consumerNumber / mobile work as aliases. |
clientTransactionId |
Yes | 3–64 chars: letters, numbers, ., :, _, -. Your idempotency key. |
providerAccountId |
No | Omit to let the system choose an account automatically. Once chosen, it never changes. |
providerId |
No | Omit to use any active provider. |
amount |
No | Omit to pay the full current bill. If sent, it must match the bill or be within the admin tolerance. The provider bill amount is paid. |
currency |
No | Only INR. Sent value may be omitted; default INR. |
serviceType |
No | Defaults to UTILITY_PAYMENT. Do not need to send it. |
metadata |
No | JSON object, max 30 keys. |
Response 202 Accepted
{
"data": {
"id": "b2f7c5c4-3b37-4f19-9690-80ae6f2bd5ae",
"clientTransactionId": "TEST-1191161198",
"serviceType": "UTILITY_PAYMENT",
"targetType": "UTILITY_NUMBER",
"targetReference": "******6678",
"mobile": "******6678",
"amount": "100.00",
"currency": "INR",
"bill": null,
"provider": {
"id": 2,
"name": "MSEDCL MahaPowerPay",
"integrationType": "msedcl-mpp"
},
"providerAccount": { "id": 5, "alias": "Account A" },
"status": "IN_QUEUE",
"providerReferenceId": null,
"requestAt": "2026-10-19T06:00:00.000Z",
"processingAt": null,
"completedAt": null,
"failureCategory": null,
"errorDetail": null,
"createdAt": "2026-10-19T06:00:00.000Z",
"updatedAt": "2026-10-19T06:00:00.000Z"
},
"meta": {
"idempotentReplay": false,
"waitingForAccount": false
}
}
- If no account is connected, the request is still accepted.
statusstaysIN_QUEUE,providerAccountisnull, andmeta.waitingForAccountistrue. - Repeating the same payload with the same
clientTransactionIdreturns200andmeta.idempotentReplay: true. No second payment is created. - Reusing the ID with different fields returns
409 CLIENT_TRANSACTION_ID_CONFLICT.
2. Check a payment
By your ID
POST /api/v1/recharges/by-client-id
POST /api/v1/recharges/by-client-id
Content-Type: application/json
{
"apiToken": "<API_TOKEN>",
"clientTransactionId": "UTIL-2026-000123"
}
Returns the full record with the unmasked target reference.
Payment status values
| Status | Meaning |
|---|---|
IN_QUEUE |
Accepted and waiting to be processed. |
PROCESSING |
Being processed with the provider right now. |
SUCCESS |
Payment accepted by the provider. |
FAILED |
Payment failed. See failureCategory. |
UNKNOWN |
Provider result is unclear. Terminal. Do not submit again automatically. |
3. Webhooks
Yellow Fox sends one POST to your WEBHOOK_URL when a payment reaches a final result.
Setup:
WEBHOOK_ENABLED=true
WEBHOOK_URL=https://your-app.com/webhooks/yellow-fox
Rules:
- Reply with any
2xxto mark it delivered. - No signature and no automatic retry.
- A non-2xx response creates an admin alert and a failed delivery log entry.
- Exactly one final event per payment.
Headers
X-Webhook-Event: payment.succeeded
X-Webhook-Event-Id: 3f2504e0-4f89-11d3-9a0c-0305e82c3301
X-Webhook-Timestamp: 2026-10-19T10:30:00.000Z
Event types
| Event | When |
|---|---|
payment.succeeded |
Provider accepted the payment. |
payment.failed |
Payment failed. |
payment.unknown |
Provider result is unclear. Do not resubmit automatically. |
Payload (all events)
The body contains exactly the fields listed below — nothing else.
{
"clientTransactionId": "UTIL-2026-000123",
"consumerName": "Consumer Name",
"accountNumber": "12345678901",
"amount": "1390.00",
"date": "2026-10-19T10:30:00.000Z",
"status": "SUCCESS",
"providerReferenceId": "REC-1001"
}
| Field | Notes |
|---|---|
clientTransactionId |
Your own ID from the create call. Use this to match the record. |
consumerName |
Consumer name, null when the provider did not return one. |
accountNumber |
The account / consumer number the payment was processed on. |
amount |
Amount in INR, decimal string with two decimals ("1390.00"), null when unknown. |
date |
ISO-8601 timestamp of the transaction (completion time, otherwise processing/request time). |
status |
SUCCESS, FAILED, or UNKNOWN. |
providerReferenceId |
Provider receipt number, null unless the payment succeeded. |
Failed / unknown example:
{
"clientTransactionId": "UTIL-2026-000123",
"consumerName": "Consumer Name",
"accountNumber": "12345678901",
"amount": "1390.00",
"date": "2026-10-19T10:31:05.000Z",
"status": "FAILED",
"providerReferenceId": null
}
Full details (bill breakdown, failure reason, provider account) stay on the
GET /api/v1/recharges/:id endpoint — they are not sent in the webhook.
Internal delivery log (admin UI only, not sent to you)
Every delivery is stored with the event type, response status, and response body so support can trace what happened. Delivery is fire-and-forget: a non-2xx response raises an admin alert, it is not retried.
Common failure categories (internal, visible in admin UI)
| Category | Meaning |
|---|---|
INSUFFICIENT_BALANCE |
Provider wallet has insufficient funds. |
SESSION_EXPIRED |
Provider login expired. Reconnect the account. |
NO_DUES |
Nothing payable for this consumer number. |
BILL_CHANGED |
Bill amount changed during processing. |
AMOUNT_OUT_OF_TOLERANCE |
Supplied amount did not match the bill or tolerance. |
PAYMENT_DISABLED |
Real payments are disabled (MPP_PAYMENT_ENABLED=false). |
AMBIGUOUS_TIMEOUT |
Provider did not confirm the result. Status is UNKNOWN. |
NETWORK_ERROR |
Network/provider communication failed. |
Errors
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"requestId": "2b8d40e1-...",
"details": {
"targetReference": "Utility account number must contain 6-20 digits, optionally prefixed with +."
}
}
}
| Status | Code | Meaning |
|---|---|---|
| 401 | INVALID_API_TOKEN |
Missing or wrong apiToken (POST body) or bearer token (GET). |
| 404 | NOT_FOUND |
Payment, account, or resource not found. |
| 409 | CLIENT_TRANSACTION_ID_CONFLICT |
Same client ID used with different data. |
| 415 | UNSUPPORTED_MEDIA_TYPE |
POST body is not JSON. |
| 422 | VALIDATION_ERROR |
Invalid field value. |
| 422 | ACCOUNT_NOT_ELIGIBLE |
Account is not active or not logged in. |
| 422 | PAYMENT_DISABLED |
Real payments are disabled. |
| 429 | RATE_LIMITED |
Too many requests. |
| 500 | INTERNAL_ERROR |
Unexpected server error. Details are hidden. |
Every response includes X-Request-Id. Use it when contacting support.
Provider-side failures (bill, wallet, session) do not make the create call fail. They appear in the final webhook as payment.failed or payment.unknown.