Reseller API
Run your clients from your own product: create them, set what they can do, fund them with credits, verify them, and buy them numbers.
A reseller account owns client accounts. Each client is a separate organization with its own balance, its own agents and its own numbers, and you decide what each one may do and what it pays per minute.
This API is that control panel, so your clients never have to see ours. Every call names one of
your clients by user_id or child_organization_id, and only ever one of your own: any other
id is rejected.
Available to reseller accounts. Request access if you do not have it yet.
The shape of it
Authentication
Send your reseller key as a bearer token on every request. See Authentication.
Authorization: Bearer YOUR_API_KEYThe key must belong to the reseller account owner or an admin on it. A teammate with viewer access is refused.
Your clients
List everything under your account, with each organization's balance, rate, concurrency limit and users:
GET /api/v1/reseller/organizationsCreate a client. This makes the user and their organization together, and links it to you:
POST /api/v1/reseller/users/add
{
"name": "Demo User",
"email": "demo@example.com",
"phone": "+15551234567",
"password": "...",
"cost_per_min": 0.20,
"concurrent_call_limit": 2,
"expiry_date": "2026-12-31",
"user_currency": "USD"
}Only the first four are required. The rest are the terms you are selling on, and you can change
them later. cost_per_min sets both the basic and the premium model rate for that client.
You cannot price a client below your own rate.
cost_per_min has to be at least what you pay, for both rates. A lower value is rejected
rather than quietly costing you money on every call the client makes. welcome_minutes_to_credit
is capped the same way, at the minutes your own balance can cover.
What a client can do
Three things are yours to set, and a client cannot change any of them itself.
Dashboard access. Turn menu areas on and off. Only the flags you pass change, and flags outside your own permissions are ignored rather than escalating.
POST /api/v1/reseller/users/access-control
{ "user_id": 1234, "dashboard_menu_access": { "is_bots_menu_access": true } }Expiry. Set a date the account stops working, or clear it.
POST /api/v1/reseller/users/expiry
{ "user_id": 1234, "expiry_date": "2026-12-31" }Pass null to remove the expiry.
Concurrency. How many calls a client can run at once.
POST /api/v1/reseller/concurrency
{ "child_organization_id": 5678, "new_limit": 5 }new_limit is the absolute number you want, not a change to the current one. Assigning is
free: any figure is accepted, nothing is deducted from you, and the call never fails for lack
of capacity.
Assigning does not create capacity.
Your own concurrent call limit is the ceiling for your whole family at dial time. What you set per client is a cap; your own limit is the capacity those caps compete for. Give five clients ten each while you hold twenty, and the twenty-first simultaneous call is refused no matter whose it is.
Credits
You hold a balance. You move minutes from it to a client at a rate you choose, and you can take unused minutes back.
Two different amounts move on every transfer: your balance is debited at your rate, and the client is credited at the rate you set. The gap is your margin. A revert is the exact mirror.
Preview either direction before committing. Nothing moves on this call:
POST /api/v1/reseller/credits/calculate
{ "minutes": 100, "cost_per_min": 0.20 }Then transfer. Your balance is debited immediately, and a cost_per_min below your own rate is
refused as a loss:
POST /api/v1/reseller/credits/transfer
{ "to_organization_id": 5678, "minutes": 100, "cost_per_min": 0.20 }To take unused minutes back, pass only how many. No rate: the client is deducted at their current rate and you are refunded at yours:
POST /api/v1/reseller/credits/revert
{ "from_organization_id": 5678, "minutes": 40 }Every move is recorded:
GET /api/v1/reseller/credits/logsVerify a client
Where a region requires it, a client has to be identity-verified before it can buy a number. For India that is an Aadhaar eKYC, so each check clears in minutes.
You do not hardcode the order of the checks. You ask which step is next, run it, and the response tells you the one after it. That way the sequence can change without breaking your integration.
Read the status once to get the first step, then follow next_step until the status is completed.
completed, verification is done and the client can buy. That is the same state the
purchase endpoint checks, so there is nothing else to poll.GET /api/v1/reseller/kyc/status?user_id=CLIENT_IDThe response has a regions array, one entry per region, each with that region's status,
its next_step, and whether the client can_purchase yet.
status = GET /kyc/status?user_id=CLIENT_ID
next = status.regions.find(r => r.region == "IN").next_step
while next:
step = POST /kyc/steps/{next} { user_id: CLIENT_ID, region: "IN", ...fields }
next = step.next_step
# status is completed. The client can now buy a number.The steps
One endpoint runs every step: the step name is the last part of the path, and the fields it needs go in the body. India is the region with step-by-step verification today.
Each step's body fields, the next_step it answers with, and the message it returns are listed
in the step reference. You can also read
the same thing as data, so your integration never hardcodes it:
GET /api/v1/reseller/kyc/requirements?region=INTwo things the sequence does not show. verify-gst and skip-gst are a choice, run one or the
other. And if the client says they did not get their code, call resend-otp while they are on
the OTP step: it is never returned as a next_step, so it sits outside the loop.
The Aadhaar steps have a cooldown.
Roughly 30 seconds between attempts. A failed verify can burn the reference and a rapid
resend trips the per-Aadhaar limit, which locks your client out, so a retry loop has to wait.
Calling too soon returns 429.
Your obligation on the data
You are relaying what your client types into your interface, nothing more. Do not store the values you send in these requests, and redact them from your logs. That covers the PAN, the Aadhaar number, and every one-time code.
Buy them a number
Once a client is verified, buying is not a reseller endpoint: it is the ordinary
phone number API with your client's user_id added. The number lands
on their account and the rental comes out of their balance, not yours.
GET /api/v1/phone_number/search?region=IN&user_id=CLIENT_ID
POST /api/v1/phone_number/purchase { "region": "IN", "phone_number": "+91...", "user_id": CLIENT_ID }A client who completed verification in the dashboard instead of through this API passes the same gate, so there is nothing to repeat.
Reference
Full request and response schemas, with a playground, are in the API reference.
