docs/API.md

Public · no sign-in required

Yellow 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

  1. You call POST /recharges. The API saves the request and returns 202.
  2. The worker picks an eligible account automatically and pays the bill.
  3. 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. status stays IN_QUEUE, providerAccount is null, and meta.waitingForAccount is true.
  • Repeating the same payload with the same clientTransactionId returns 200 and meta.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 2xx to 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.