Corporate API
A server-to-server API for reselling recharge and bill payments. Everything is JSON over HTTPS, authenticated with an API token issued to you and restricted to your own IP addresses.
Base URL: https://freedata.in
One surface
| Surface | What you get |
|---|---|
/api/corp/v1/* | Proper HTTP status codes, a machine-readable error string, and a numeric code support can trace. JSON in, JSON out, POST for anything that moves money. |
Authentication
Send your API token on every request. The header form is preferred, because a token in a query string ends up in proxy and web-server access logs:
curl -H "X-Api-Token: <your-token>" \
"https://freedata.in/api/corp/v1/balance"Three transports are accepted, in this order of preference:
X-Api-Token: <token>— recommendedAuthorization: Bearer <token>
IP allowlist
Your account is restricted to the source IP addresses you register with us (individual addresses or CIDR ranges). A call from anywhere else is refused with result code 2428. Tell us before you change egress addresses, or your traffic stops.
Rate limit
Requests are capped per minute per account. Exceeding it returns 429 with result code 123; retry after a short pause.
Errors
A refusal carries a real HTTP status plus a body like:
{"status":"rejected","error":"ip_not_allowed","code":2428,"message":"IP Validation failed."}Retries and idempotency
Every money endpoint is idempotent on your own reference — urid on recharge, request_id everywhere else. This is the single most important rule in this document.
- If you do not receive a response, retry with the SAME reference. We return the original outcome instead of transacting again. Generating a new reference on a retry is how integrations double-charge their customers.
- Reusing a reference for different details (a different number, operator or amount) is rejected with
2555 Duplicate urid. We will never answer you with a transaction that is not the one you asked about. - A reference must be unique across your account and at most 64 characters.
Pending is not failure
PENDING / pending result means the transaction has been accepted and may still complete. Never resend it. Wait for the callback, or poll the status endpoint. Treating pending as a failure and retrying is the classic way to pay twice.Status callbacks
Give us an HTTPS URL and we will call it when a transaction reaches its final state (we do not call for pending). Delivery is retried with a growing back-off until your endpoint answers 2xx.
GET https://your-server.example/callback
?v=2
&status=SUCCESS
&orderid=R2607230429028DB15
&urid=ABC123
&amount=100.0000
&account=9876543210
&ts=1755400000
&nonce=3f1c0a9b2e5d4867b1a0c7d9e2f34856
&hash=<hmac>Verify hash before trusting a callback. It is an HMAC-SHA256, keyed with your API token, over every parameter we send except hash itself — sorted by key and joined as key=value with &. Build the pre-image from what actually arrived rather than from a fixed list of names, so a field we add later is covered automatically:
$params = $_GET;
unset($params['hash']);
ksort($params, SORT_STRING);
$pairs = [];
foreach ($params as $k => $v) {
$pairs[] = $k . '=' . $v;
}
$expected = hash_hmac('sha256', implode('&', $pairs), $yourApiToken);
if (!hash_equals($expected, (string) ($_GET['hash'] ?? ''))) {
http_response_code(403);
exit;
}
// Freshness: refuse anything outside a five-minute window.
if (abs(time() - (int) ($_GET['ts'] ?? 0)) > 300) {
http_response_code(403);
exit;
}
// Replay: refuse a nonce you have already seen inside that window.
if ($youHaveSeen[$_GET['nonce']] ?? false) {
http_response_code(403);
exit;
}
// Duplicate delivery: we retry until you answer 2xx, so the SAME orderid may
// legitimately arrive more than once with different ts/nonce. Book it once,
// keyed on orderid.amount and account. A verifier that hashes only some of them will happily accept a callback whose amount or destination account was rewritten in transit — the signature over the fields it does check still matches.https:// address. Private, loopback and link-local addresses are refused. Callbacks are informational: the status endpoint is always authoritative, so an integration that only polls is still correct.Service status
Read live from the platform as this page was served. Query it yourself at any time with GET /api/corp/v1/services rather than hardcoding a service list.
| Service | Name | Status | Note |
|---|---|---|---|
recharge | Mobile & DTH recharge | live | |
bbps | Bill payments (BBPS) | no vendor active | Endpoints are live but no vendor is currently switched on — calls will return the service's unavailable outcome and nothing is charged. |
"No vendor active" means the endpoints work but nothing is switched on upstream: calls are answered safely and nothing is charged.
Endpoints
Core (v1)
The modern surface for new integrations. JSON in, JSON out, machine-readable string errors alongside a numeric code support can trace.
/api/corp/v1/servicesenabled means the endpoints exist. ready means a vendor is switched on right now — if it is false the calls still answer safely and nothing is charged.{
"status": "success",
"code": 200,
"client": "Acme Pvt Ltd",
"services": [
{
"service": "recharge",
"name": "Mobile & DTH recharge",
"enabled": true,
"ready": true,
"note": null
}
]
}/api/corp/v1/balance{
"status": "success",
"code": 200,
"balance": "5000.0000"
}/api/corp/v1/transactions| Parameter | In | Description | |
|---|---|---|---|
page | query | optional | Default 1. |
page_size | query | optional | Default 20, maximum 100. |
status | query | optional | success | pending | failed. |
service | query | optional | recharge | bbps | money_transfer | travel. |
from | query | optional | yyyy-mm-dd. |
to | query | optional | yyyy-mm-dd. |
q | query | optional | Search by account, our ref, or your reference. |
{
"page": 1,
"page_size": 20,
"total": 4,
"transactions": [
{
"ref": "R2607…",
"request_id": "ABC123",
"service": "recharge",
"account": "9876543219",
"amount": "10.0000",
"status": "failed"
}
]
}Recharge (v1)
Mobile and DTH recharge. Money endpoints are POST; status is GET.
/api/corp/v1/rechargemoney| Parameter | In | Description | |
|---|---|---|---|
account | body | required | Customer number or DTH subscriber id. |
operator | body | required | LIVE operator code (e.g. LIVJIO00003) or our catalog code (e.g. jio). A bare number is an operator id, never an operator code. |
amount | body | required | Face value in rupees. |
urid | body | required | Your unique reference (idempotency key), max 64 characters. |
circle | body | optional | Circle/state hint. |
customer_name | body | optional | For your own records. |
status is success | pending | failed. A rejected request returns status:"rejected" with an error string and the matching numeric code.{
"status": "success",
"code": 200,
"order_id": "R2607…",
"urid": "ABC123",
"operator_ref": "OP998877",
"amount": "100.0000",
"account": "9876543210",
"balance": "4900.0000",
"message": "Recharge successful."
}/api/corp/v1/recharge/{ref}{
"status": "success",
"code": 200,
"order_id": "R2607…",
"urid": "ABC123",
"operator_ref": "OP998877",
"amount": "100.0000",
"account": "9876543210",
"refunded": false
}/api/corp/v1/recharge| Parameter | In | Description | |
|---|---|---|---|
urid | query | required | The reference you sent. |
/api/corp/v1/operators/api/corp/v1/recharge/plans| Parameter | In | Description | |
|---|---|---|---|
operator | query | required | Operator code. |
circle | query | optional | Circle name. |
/api/corp/v1/recharge/circle| Parameter | In | Description | |
|---|---|---|---|
mobile | query | required | Customer number. |
Bill payments — BBPS (v1)
Browse the biller directory, render the biller's input form from its customer_params schema, fetch the live bill, then pay. Billers whose fetch_requirement is MANDATORY must be fetched before payment.
/api/corp/v1/bbps/categories/api/corp/v1/bbps/billers| Parameter | In | Description | |
|---|---|---|---|
category | query | optional | Exact category string from /bbps/categories. |
q | query | optional | Name search. |
page | query | optional | Default 1. |
page_size | query | optional | Default 50, max 100. |
/api/corp/v1/bbps/billers/{id}customer_params[].name must be echoed back verbatim on fetch and pay./api/corp/v1/bbps/quote| Parameter | In | Description | |
|---|---|---|---|
biller_id | query | required | Biller id. |
amount | query | required | Bill amount in rupees. |
{
"ok": true,
"amount": "500.00",
"convenience_fee": "0.00",
"total": "500.00",
"charge": "500.00"
}/api/corp/v1/bbps/fetch| Parameter | In | Description | |
|---|---|---|---|
biller_id | body | required | Biller id. |
params | body | required | The biller's customer parameters, as a name→value object or a [{name,value}] list. |
customer_mobile | body | optional | Customer contact number. |
fetch_ref binds to the bill you fetched and must be sent on pay./api/corp/v1/bbps/paymoney| Parameter | In | Description | |
|---|---|---|---|
biller_id | body | required | Biller id. |
params | body | required | Same customer parameters used on fetch. |
amount | body | required | Amount in rupees. |
request_id | body | required | Your unique reference (idempotency key), max 64 characters. |
fetch_ref | body | optional | Required for billers whose fetch_requirement is MANDATORY. |
/api/corp/v1/bbps/{ref}Operator codes
On /api/corp/v1/* send the code column; on send the Code column. It is accepted on their own surface, so an existing integration keeps working unchanged. Fetch this programmatically from GET /api/corp/v1/operators.
| Operator | Type | Code | Needs circle |
|---|---|---|---|
| Airtel | mobile | LIVAIR00001 | no |
| BSNL | mobile | LIVBSN00002 | no |
| Jio | mobile | LIVJIO00003 | no |
| MTNL | mobile | — | no |
| Vi (Vodafone Idea) | mobile | LIVVIL00004 | no |
| Airtel Digital TV | dth | LIVAIR00005 | no |
| d2h (Videocon) | dth | LIVVID00009 | no |
| Dish TV | dth | LIVDIS00006 | no |
| Sun Direct | dth | LIVSUN00007 | no |
| Tata Play | dth | LIVTAT00008 | no |
Result codes
The same catalog throughout: code on v1.
| Code | Meaning |
|---|---|
101 | Invalid state code |
102 | Invalid opcode value |
103 | Invalid Amount |
104 | Invalid user mobile number |
105 | Invalid pin provided |
106 | Invalid urid value |
107 | Invalid login details |
108 | Operator down time |
111 | Transaction already running |
112 | Insufficient balance |
113 | Internal server error |
115 | Duplicate recharge not allowed within 10 minutes |
116 | Invalid order id |
117 | Order id not found |
120 | Recharge is pending |
121 | Transaction on hold - a support review is in progress |
122 | Recharge failed |
123 | Server is busy |
124 | Service is down |
126 | Invalid data provided |
127 | Max limit for each customer is only 100000 Rs |
200 | SUCCESS |
201 | Recharge is pending |
213 | Invalid Number |
331 | Your account has been suspended, Please contact your administrator |
351 | Recharge amount not in range |
1144 | This operator service has been temporarily unavailable |
2428 | IP Validation failed |
2555 | Duplicate urid |
2666 | Your account has been blocked for wrong credentials, please contact customer support |
200 success · 201 pending (accepted, still in progress — do not resend) · everything else is a refusal or a failure. A failed transaction is refunded to your wallet automatically.