Build phone verification into your own product.
Order numbers, receive codes, and run your own storefront on top of CODASMS. Pre-funded balance, 700+ services, 190+ countries — one API key.
Order numbers, receive codes, and run your own storefront on top of CODASMS. Pre-funded balance, 700+ services, 190+ countries — one API key.
The Reseller API is not self-serve. Every reseller is reviewed and approved by us before an API key is issued — this keeps supply predictable for the resellers who are already trading on it.
To apply, contact us with: what you're building, the countries and services you expect to need, and your rough monthly volume. If it's a fit, we'll set up your reseller account, fund it with your first deposit, and send you an API key.
Already approved? Your API key was sent to you directly. It is shown once and stored only as a hash — we cannot look it up or recover it. If you lose it, ask us to revoke it and issue a new one.
Every request carries your API key as a bearer token. Keys look like coda_live_….
Authorization: Bearer coda_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Your key identifies your reseller account. Orders are billed to that account's balance — there is no account or user field in any request body, so a key can only ever spend its own balance and read its own orders.
Treat your key like a password. Anyone holding it can spend your balance. Keep it server-side — never ship it in a browser, mobile app, or public repository. If it leaks, contact us and we'll revoke it immediately; revocation takes effect on the next request.
https://api.codasms.com
All endpoints are under /v1. Requests and responses are JSON. All amounts are integer US cents — never floats. A response always carries an ok boolean; check it before reading anything else.
60 requests per minute, per key. Exceeding it returns 429 with reason: "rate_limited" and a retryAfterSeconds field. Back off and retry — the window is rolling, not fixed.
If you need a higher limit for a specific integration, ask us. It's a per-key setting.
Your current balance. Check it before a batch of orders — an order against an empty balance fails with 402 and costs you a round trip.
# request
curl https://api.codasms.com/v1/balance \
-H "Authorization: Bearer coda_live_..."
# response
{
"ok": true,
"balance_cents": 15000,
"balance_usd": 150.00,
"currency": "USD"
}
balance_cents is authoritative. balance_usd is a convenience float for display — never use it for arithmetic.
The live catalog: what's buyable, where, at what price, with current stock.
Unfiltered, this returns the entire catalog — around 500KB. If you want one cell, ask for one cell.
| Param | Example | Returns |
|---|---|---|
service | ?service=tg | Telegram, every country that has it |
country | ?country=6 | Everything available in Indonesia |
| both | ?service=tg&country=6 | That one cell |
| none | — | The full catalog |
# request
curl "https://api.codasms.com/v1/services?service=tg&country=6" \
-H "Authorization: Bearer coda_live_..."
# response
{
"ok": true,
"currency": "USD",
"filters": { "service": "tg", "country": "6" },
"counts": { "services": 1, "countries": 1 },
"services": [ { "code": "tg", "name": "Telegram", "from_cents": 61, "stock": 2349045 } ],
"countries": [ { "code": "6", "name": "Indonesia", "iso": "ID", "flag": "🇮🇩" } ],
"matrix": {
"6": { "tg": { "standard_cents": 61, "priority_cents": 92, "count": 2349045 } }
},
"generated_at": "2026-07-17T07:20:36.529Z",
"stale": false
}
The matrix is the part that matters. It's keyed matrix[country][service] and holds the real price for that exact combination. The from_cents on a service is its cheapest price anywhere — a headline figure, not a quote.
The catalog refreshes about every 5 minutes and responses carry Cache-Control: max-age=300. stale: true means you're seeing a slightly old snapshot; the prices are still what we'd charge, so it's safe to use.
Prices here are indicative, not a locked quote. Stock and supply move. Every order is priced server-side at the moment you place it, and the response tells you exactly what you were charged.
Buys a number and debits your balance. This is the one endpoint that moves money.
| Field | Required | Notes |
|---|---|---|
service | yes | Service code from the catalog, e.g. tg |
country | yes | Country code from the catalog, e.g. 6 |
band | no | standard (default) or priority |
Priority costs more and buys from higher rungs of supply. It's worth it for services where delivery is hard; for most, standard is fine.
# request
curl -X POST https://api.codasms.com/v1/orders \
-H "Authorization: Bearer coda_live_..." \
-H "Content-Type: application/json" \
-d '{"service":"tg","country":"6","band":"standard"}'
# response
{
"ok": true,
"order_id": 542,
"number": "628xxxxxxxxx",
"service": "tg",
"country": "6",
"band": "standard",
"price_cents": 67,
"status": "active",
"expires_in_seconds": 1200
}
Use number immediately — request the verification code from whatever service you're verifying, then poll order status for the code.
You are never charged for nothing. If we can't issue a number, you're not debited. If we issue one and no code arrives within 20 minutes, the order expires and your balance is refunded automatically — even if you never poll and never cancel.
Poll for the code, or cancel for an early refund.
| Field | Required | Notes |
|---|---|---|
order_id | yes | From the create response |
action | no | poll (default) or cancel |
# poll
curl -X POST https://api.codasms.com/v1/orders/status \
-H "Authorization: Bearer coda_live_..." \
-H "Content-Type: application/json" \
-d '{"order_id":542}'
# waiting
{ "ok": true, "order_id": 542, "status": "active", "waiting": true }
# code arrived
{ "ok": true, "order_id": 542, "status": "delivered", "code": "123456" }
# cancel early — refunds immediately
curl -X POST https://api.codasms.com/v1/orders/status \
-H "Authorization: Bearer coda_live_..." \
-H "Content-Type: application/json" \
-d '{"order_id":542,"action":"cancel"}'
{ "ok": true, "order_id": 542, "status": "refunded" }
Poll every 3–5 seconds. Faster wastes your rate limit; codes typically take ~20 seconds. Polling a delivered order is safe and idempotent — it returns the same code.
Cancelling an order that has already delivered will not refund it: you received what you paid for, and the response returns the code instead.
| Status | Means |
|---|---|
active | Number issued, waiting for a code. Keep polling. |
delivered | Code arrived. Terminal — you were charged. |
refunded | Cancelled or expired. Terminal — balance returned. |
Every order reaches a terminal state on its own. A number that receives no code within 20 minutes is expired and refunded automatically by our system — you don't need to poll or cancel for that to happen.
A minimal integration:
POST /v1/orders → take number and order_idPOST /v1/orders/status every 3–5s until delivered or refundedaction: "cancel" to get your money back soonerErrors return ok: false and a stable reason string. Branch on reason, not on the HTTP status or on any message text — reason strings are the contract; wording is not.
Some responses also carry detail (human-readable, may change) and refunded: true where money was returned.
| Status | reason | What to do |
|---|---|---|
| 400 | bad_request | Malformed JSON or a missing field. See detail. |
| 401 | missing_or_malformed_key | No bearer token, or it isn't a coda_live_ key. |
| 401 | invalid_key | Unknown or revoked key. Contact us. |
| 402 | insufficient_balance | Top up. Nothing was charged. |
| 403 | reseller_not_active | Your account is suspended. Contact us. |
| 403 | forbidden | That order isn't yours. |
| 404 | not_found | No such order. |
| 405 | method_not_allowed | Wrong HTTP verb for that endpoint. |
| 429 | rate_limited | Over 60/min. Back off; see retryAfterSeconds. |
| Status | reason | What to do |
|---|---|---|
| 409 | unavailable | No live supply for that combination. Try another. |
| 409 | out_of_stock | Pool is empty right now. Try later. |
| 409 | concierge_only | Priced above the self-serve range — not sold via the API. |
| 409 | no_provider_ref | Order never finished provisioning. Contact us. |
| Status | reason | What to do |
|---|---|---|
| 409 | no_numbers | Supply vanished mid-purchase. Retry. |
| 409 | price_moved | Price shifted mid-purchase. Retry. |
Both carry refunded: true. Your balance is already back.
| Status | reason | What to do |
|---|---|---|
| 500 | internal_error | Our bug. Retry with backoff; tell us if it persists. |
| 502 | provider_error | Upstream problem. Retry with backoff. |
| 503 | catalog_unavailable | Catalog warming. Retry shortly. |
Retry safely. 500, 502 and 503 are transient — use exponential backoff, not a tight loop. If an order failed after we charged you, the response says refunded: true; if it doesn't, nothing was charged.
Integration questions, a higher rate limit, a lost key, or an order that looks wrong — contact us. Include your order_id and the key prefix (the first ~18 characters, e.g. coda_live_qKcyrcU2) so we can find it. Never send us your full key.
Not a reseller yet? Tell us what you're building.