Reseller API · v1

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.

Getting access

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.

Authentication

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.

Base URL

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.

Rate limits

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.

Balance

GET/v1/balance

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.

Services & countries

GET/v1/services

The live catalog: what's buyable, where, at what price, with current stock.

Filters

Unfiltered, this returns the entire catalog — around 500KB. If you want one cell, ask for one cell.

ParamExampleReturns
service?service=tgTelegram, every country that has it
country?country=6Everything available in Indonesia
both?service=tg&country=6That one cell
noneThe 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.

Create an order

POST/v1/orders

Buys a number and debits your balance. This is the one endpoint that moves money.

FieldRequiredNotes
serviceyesService code from the catalog, e.g. tg
countryyesCountry code from the catalog, e.g. 6
bandnostandard (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.

Order status

POST/v1/orders/status

Poll for the code, or cancel for an early refund.

FieldRequiredNotes
order_idyesFrom the create response
actionnopoll (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.

Order lifecycle

StatusMeans
activeNumber issued, waiting for a code. Keep polling.
deliveredCode arrived. Terminal — you were charged.
refundedCancelled 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_id
  • Trigger the verification on the target service
  • POST /v1/orders/status every 3–5s until delivered or refunded
  • Give up early? action: "cancel" to get your money back sooner

Errors

Errors 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.

Your request

StatusreasonWhat to do
400bad_requestMalformed JSON or a missing field. See detail.
401missing_or_malformed_keyNo bearer token, or it isn't a coda_live_ key.
401invalid_keyUnknown or revoked key. Contact us.
402insufficient_balanceTop up. Nothing was charged.
403reseller_not_activeYour account is suspended. Contact us.
403forbiddenThat order isn't yours.
404not_foundNo such order.
405method_not_allowedWrong HTTP verb for that endpoint.
429rate_limitedOver 60/min. Back off; see retryAfterSeconds.

Not available not charged

StatusreasonWhat to do
409unavailableNo live supply for that combination. Try another.
409out_of_stockPool is empty right now. Try later.
409concierge_onlyPriced above the self-serve range — not sold via the API.
409no_provider_refOrder never finished provisioning. Contact us.

Bought, then failed refunded

StatusreasonWhat to do
409no_numbersSupply vanished mid-purchase. Retry.
409price_movedPrice shifted mid-purchase. Retry.

Both carry refunded: true. Your balance is already back.

Ours, not yours

StatusreasonWhat to do
500internal_errorOur bug. Retry with backoff; tell us if it persists.
502provider_errorUpstream problem. Retry with backoff.
503catalog_unavailableCatalog 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.

Support

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.