Developers
VoxioTelecom REST API
One JSON API for your wallet balance, A2P SMS delivery, OTP verification numbers and crypto top-up invoices. Scoped bearer keys, per-key spend caps, idempotent billing calls and predictable error codes.
https://voxiotelecom.com/api/public/v1 Create an API keyStart here
Quickstart
- Sign in to the portal and open API keys.
- Create a key, pick only the scopes your integration needs, and copy the secret — it is shown once.
- Send it as a bearer token on every request. All responses are JSON.
curl -s https://voxiotelecom.com/api/public/v1/balance \ -H "Authorization: Bearer vk_1a2b3c4d.<secret>"
{
"balance": 412.8391,
"currency": "USD",
"account": { "email": "you@example.com", "status": "active" }
}An unauthenticated GET https://voxiotelecom.com/api/public/v1 returns a machine-readable index of every endpoint — handy for autogenerated clients and AI agents.
Security
Authentication
Keys look like vk_<prefix>.<secret>. The prefix identifies the key, the secret is 256 bits of entropy and is stored only as a SHA-256 hash — nobody, including our staff, can read it back. Lost a key? Revoke it and issue a new one.
| Field | Type | Description |
|---|---|---|
| Authorization | header, required | Bearer vk_<prefix>.<secret> |
| Content-Type | header, required on POST | application/json (max 200 KB body) |
| Idempotency-Key | header, optional | Safe retries on billing endpoints (see below) |
Never ship a key in browser JavaScript, a mobile app bundle or a public repository. Calls are billed to your wallet. If a key leaks, revoke it in the portal — revocation takes effect immediately.
Least privilege
Scopes & key limits
Every key carries an explicit scope list. A call without the required scope fails with 403 insufficient_scope — it is never silently allowed.
| Field | Type | Description |
|---|---|---|
| balance:read | read | Wallet balance and account status |
| sms:read | read | Pricing quotes and your message log |
| sms:send | spend | Send SMS — debits your wallet |
| otp:read | read | OTP catalogue and activation status |
| otp:write | spend | Rent and cancel OTP numbers — debits your wallet |
| funds:read | read | Crypto deposit history |
| funds:topup | billing | Create a crypto invoice (cannot credit balance) |
Per key you can also set:
- Expiry — the key stops working automatically on that date.
- IP allowlist — requests from any other address are rejected.
- Daily spend limit — a hard USD ceiling per 24h, enforced before every charge.
Maximum 10 active keys per account. Every authenticated call is logged with IP, status and amount charged, visible in the portal.
Fair use
Rate limits
Limits are per key and enforced both in-process and in the database, so they hold across our whole edge fleet.
| Field | Type | Description |
|---|---|---|
| Read endpoints | 60–120 / minute | balance, quotes, message log, OTP status |
| SMS send | 30 / minute | up to 50 recipients per call |
| OTP request | 20 / minute | rent verification numbers |
| Funds top-up | 10 / hour | invoice creation |
Exceeding a limit returns 429 rate_limited. Back off and retry — no charge is applied.
Reliability
Idempotency
Send an Idempotency-Key header (any unique string, e.g. a UUID) on SMS send, OTP request/cancel and funds top-up. If the same key hits the same endpoint again, we replay the original response instead of charging twice — ideal for timeouts and network retries.
curl -X POST https://voxiotelecom.com/api/public/v1/sms/send \
-H "Authorization: Bearer vk_1a2b3c4d.<secret>" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9f1c2d54-7b2f-4d1a-9f0e-2c1b7d9a4e11" \
-d '{"to":["12025550123"],"message":"Your code is 1234"}'Contract
Errors
Every failure returns the same envelope, with a stable machine-readable code. We never leak provider names, SQL or stack traces.
{ "error": { "code": "insufficient_funds", "message": "Insufficient balance: $2.10 available, $5.00 required." } }| Field | Type | Description |
|---|---|---|
| 400 invalid_request | client | Body or query failed validation |
| 401 unauthorized | client | Missing, malformed, revoked or invalid key |
| 401 key_expired | client | Key passed its expiry date |
| 402 insufficient_funds | client | Wallet balance too low for the call |
| 402 billing_failed | client | Charge failed; the purchase was rolled back |
| 403 insufficient_scope | client | Key lacks the scope for this endpoint |
| 403 ip_not_allowed | client | Caller IP is outside the key allowlist |
| 403 spend_limit_exceeded | client | Daily spend cap for this key reached |
| 404 not_found | client | Resource does not exist or is not yours |
| 409 not_cancellable | client | Activation is past the cancellable state |
| 413 payload_too_large | client | Body exceeds 200 KB |
| 415 unsupported_media_type | client | Content-Type is not application/json |
| 429 rate_limited | client | Slow down and retry |
| 503 unavailable | server | Upstream provider temporarily unavailable |
Endpoints
Balance
/balancebalance:read60 / minuteLive wallet balance straight from the billing platform — the same figure your SIP trunk and SMS traffic draw from.
curl -s https://voxiotelecom.com/api/public/v1/balance -H "Authorization: Bearer $VOXIO_KEY"
{ "balance": 412.8391, "currency": "USD", "account": { "email": "you@example.com", "status": "active" } }Endpoints
SMS
/sms/quote?number=12025550123sms:read60 / minutePrice options for a destination before you commit. Pass an option_id to /sms/send as route_option_id to pin a route.
{
"country": "United States",
"options": [
{ "option_id": "us-direct", "label": "US direct — alpha sender not supported", "price_usd": 0.0091 }
]
}/sms/sendsms:send30 / minutesupports Idempotency-Key| Field | Type | Description |
|---|---|---|
| to | string | string[] | Up to 50 recipients in E.164 without '+'. Comma or newline separated strings are accepted. |
| message | string (1–1600) | Message body. Unicode is supported; long messages are billed per segment. |
| sender_id | string, optional | Alphanumeric sender where the destination allows it. |
| route_option_id | string, optional | Pin a specific route from /sms/quote. |
curl -X POST https://voxiotelecom.com/api/public/v1/sms/send \
-H "Authorization: Bearer $VOXIO_KEY" -H "Content-Type: application/json" \
-d '{"to":["12025550123","447700900123"],"message":"Your code is 1234","sender_id":"Voxio"}'{
"sent": 2,
"failed": 0,
"charged_usd": 0.0284,
"results": [
{ "recipient": "12025550123", "status": "sent", "charged_usd": 0.0091, "error": null }
]
}Each recipient is charged individually; a failed recipient is not billed and is reported in results.
/sms/messages?page=1&page_size=20sms:read60 / minutePaginated delivery log for your account: recipient, status, segments, cost and provider timestamps.
{ "messages": [ … ], "total": 184, "page": 1, "page_size": 20, "pages": 10 }Endpoints
OTP verification numbers
/otp/services?service=waotp:read60 / minuteWithout service you get the full catalogue of services and countries. With it, you get live per-country offers and prices in USD.
{ "service": "wa", "offers": [ { "country": 0, "country_name": "Russia", "price_usd": 0.42, "availability": "good" } ] }/otp/requestotp:write20 / minutesupports Idempotency-Key| Field | Type | Description |
|---|---|---|
| service | string | Service code from /otp/services, e.g. 'wa', 'tg', 'go'. |
| country | number | Country id from the catalogue. |
| max_price_usd | number, optional | Abort instead of renting if the live price is higher. |
{
"activation": {
"id": "0f6f1c0e-4c8b-4b26-b0a2-3b0f1e0b7c11",
"phone": "79991234567",
"service_code": "wa",
"status": "waiting",
"charged_usd": 0.42,
"expires_at": "2026-09-01T22:20:00.000Z"
}
}Returns 201. If billing fails the number is released instantly and you are not charged.
/otp/status?id=<activation uuid>otp:read120 / minutePoll every 3–5 seconds until status becomes code_received. Omit id to list your recent activations.
{ "activation": { "id": "0f6f…", "status": "code_received", "sms_code": "418902", "sms_text": "418902 is your code" } }/otp/cancelotp:write30 / minutesupports Idempotency-KeyCancel a number that never received a code. Eligible activations are refunded to your wallet automatically.
{ "id": "0f6f…", "status": "cancelled", "refunded_usd": 0.42 }Endpoints
Funds & deposits
/funds/topupfunds:topup10 / hoursupports Idempotency-Key| Field | Type | Description |
|---|---|---|
| amount_usd | number (10–5000) | Invoice amount in USD. |
| currency | enum | BTC, ETH, USDT, USDT_TRX, USDT_BSC, LTC or TRX. |
curl -X POST https://voxiotelecom.com/api/public/v1/funds/topup \
-H "Authorization: Bearer $VOXIO_KEY" -H "Content-Type: application/json" \
-d '{"amount_usd":100,"currency":"USDT_TRX"}'{
"invoice_url": "https://plisio.net/invoice/…",
"txn_id": "6603…",
"amount": "100.00",
"currency": "USDT_TRX",
"amount_usd": 100,
"note": "Balance is credited automatically once the payment is confirmed on-chain."
}This endpoint can never credit a balance. It only asks the payment processor for an invoice; funds post exclusively through the signed payment callback once the blockchain confirms the transaction.
/funds/depositsfunds:read60 / minuteYour last 50 crypto deposits with network, transaction hash, confirmations and credit status — use it to reconcile invoices.
{ "deposits": [ { "network": "tron", "tx_hash": "9c2…", "amount_usd": 100, "status": "credited", "credited_at": "2026-09-01T18:04:22Z" } ] }Best practice
Security checklist
Keep keys server-side
Call the API from your backend only. Browser and mobile bundles are readable by anyone.
One key per integration
Separate keys make revocation surgical and the audit log meaningful.
Grant the minimum scopes
A reporting job needs balance:read — not sms:send.
Set a daily spend cap
Caps turn a compromised key into a small, bounded incident.
Pin your server IPs
With an allowlist, a stolen key is useless from anywhere else.
Rotate on staff changes
Create the new key, deploy, then revoke the old one — zero downtime.
Something unclear or missing? Our engineers answer developer tickets directly.
Talk to an engineer