# SMSCode > SMSCode is a virtual number service for SMS verification. Users can rent temporary phone numbers from 200+ countries to receive one-time verification codes (OTP) for platforms like WhatsApp, Telegram, Instagram, and hundreds more. ## Overview - Website: https://smscode.gg - API Base URLs: https://api.smscode.gg/v1 (IDR) and https://api.smscode.gg/v2 (USD projection) - Authentication: Bearer token (get your token from Account Settings) - Pricing: Pay-per-number, no subscriptions — v1 prices are integer IDR; v2 prices are USD money objects with exact IDR canonical amounts - Supported countries: 200+ - Supported platforms: 1,000+ ## Company SMSCode is a service of Danver LLC, a Wyoming (USA) limited liability company. - Legal entity: Danver LLC (Wyoming, USA) - Jurisdiction: Wyoming, United States - Founded: 2026-02-11 - Customer support: support@smscode.gg - Privacy contact: privacy@smscode.gg ## Documentation - [API Documentation](https://smscode.gg/docs): Complete REST API reference for v1 IDR and v2 USD APIs, with examples in cURL, JavaScript, and Python - [Full API Reference](https://smscode.gg/llms-full.txt): Complete API documentation in plain text (recommended for LLMs) - [Pricing](https://smscode.gg/pricing): Live pricing table with country/platform breakdown - [FAQ](https://smscode.gg/faq): Frequently asked questions - [Use Cases](https://smscode.gg/use-cases): Real-world scenarios for virtual numbers - [About](https://smscode.gg/about): Company mission, infrastructure, and trust signals - [Compare](https://smscode.gg/compare): Side-by-side comparison with 11 competitors ## Quick Start 1. Sign up at https://smscode.gg/auth/signup 2. Deposit funds (crypto or other methods) 3. Get your API token from Account Settings 4. Install an official SDK: ```bash bun add @smscode/sdk # or: npm i @smscode/sdk pip install smscode ``` 5. Prefer `/v2` for new integrations. `/v2` returns USD money objects with exact IDR canonical amounts; `/v1` is the legacy IDR integer API. 6. Browse catalog products, create with `catalog_product_id`, wait for the verification SMS, submit any detected OTP in your target app, then finish the order. No SMS? Cancel only while the order is still cancelable; an SMS without a detected code still counts as delivered. TypeScript: ```ts import { SmscodeClient } from "@smscode/sdk"; const client = new SmscodeClient({ token: process.env.SMSCODE_TOKEN! }); const { orders } = await client.orders.create({ catalog_product_id: Number(process.env.SMSCODE_CATALOG_PRODUCT_ID), max_price: "0.50", quantity: 1, }); const orderId = orders[0]!.id; const first = await client.orders.waitForOtp(orderId, { timeoutMs: 120_000 }); console.log(first.otpCode); await client.orders.finish(orderId); ``` Python: ```python import os from smscode import SmscodeClient with SmscodeClient(token=os.environ["SMSCODE_TOKEN"]) as client: created = client.orders.create( catalog_product_id=int(os.environ["SMSCODE_CATALOG_PRODUCT_ID"]), max_price="0.50", quantity=1, ) order_id = int(created.orders[0]["id"]) first = client.orders.wait_for_otp(order_id, timeout_ms=120_000) print(first.otp_code) client.orders.finish(order_id) ``` After a resend, use `afterCode` (TypeScript) or `after_code` (Python) so polling ignores the preserved old OTP and waits for a new code. Use `can_resend` and `resend_available_at` for action availability. REST remains available for custom runtimes. Pick one API version per integration: `/v1` for IDR integer money fields, `/v2` for USD money objects. For v2, money-bearing responses return USD money objects such as: ```json { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" } ``` Every v2 money-bearing response includes `meta.fx { pair, rate, rate_as_of }`. `canonical_amount` is the exact IDR ledger value; USD is a render-time projection, not a separate wallet. ## API Endpoints | Method | Path | v1 | v2 | |--------|------|----|----| | GET | /catalog/countries | /v1/catalog/countries | /v2/catalog/countries | | GET | /catalog/services | /v1/catalog/services | /v2/catalog/services | | GET | /catalog/operators | Operators (carriers) w/ stock + synthesized `any` | Same shape | | GET | /catalog/products | IDR `price` integer (+ `operator_id`/`operator_name`, `?operator_id=` filter) | USD `price` money object + `meta.fx` (+ `operator_id`/`operator_name`) | | GET | /catalog/exchange-rate | `{pair, base_currency, quote_currency, rate}` | `{pair, rate, rate_as_of}` | | GET | /balance | IDR balance | USD balance money object + `meta.fx` | | GET | /orders | IDR `amount` integer | USD `amount` money object + `meta.fx` | | GET | /orders/active | Same shape, no money | Same shape, no `meta.fx` | | GET | /orders/{id} | IDR `amount` integer | USD `amount` money object + `meta.fx` | | POST | /orders/create | Same shape | Same shape | | POST | /orders/cancel | IDR refund/balance integers | USD money objects + `meta.fx` | | POST | /orders/finish | Same shape | Same shape | | POST | /orders/resend | Same shape | Same shape | | POST | /orders/reactivate | IDR `max_price` integer + `amount` integer (child order) | USD `max_price` string + `amount` money object + `meta.fx` | | GET | /orders/{id}/reactivate-options | IDR `cost` integer (preview) | USD `cost` money object + `meta.fx` | | GET | /webhook | Same shape | Same shape | | PATCH | /webhook | Same shape | Same shape | | POST | /webhook/test | Same shape | Same shape | ## Browse Virtual Numbers - [All Countries](https://smscode.gg/virtual-number): Browse numbers by country - [SMS Verification](https://smscode.gg/sms-verification): Browse by platform/service - [Platforms](https://smscode.gg/platforms): All supported platforms - [Blog](https://smscode.gg/blog): Guides, tutorials, and comparisons (1,200+ articles in 12 languages) ## Support - Customer support: support@smscode.gg - Privacy & data protection: privacy@smscode.gg - Telegram: https://t.me/smscode_gg ## Legal - [Terms of Service](https://smscode.gg/terms) - [Privacy Policy](https://smscode.gg/privacy) ## For AI Agents — Building on the SMSCode API If you are a coding agent integrating SMSCode, read this section together with the [API reference](https://smscode.gg/docs) and [Full API Reference](https://smscode.gg/llms-full.txt). - **OpenAPI contract:** the [API reference](https://smscode.gg/docs) is rendered from a hand-authored OpenAPI 3.1 document — the single source of truth for every request/response shape. - **Machine-readable contract:** the raw OpenAPI 3.1 document is at . - **Agent integration guide:** — rules, money shapes, idempotency, and a minimal recipe. - **SDK source + examples:** (public TypeScript/JavaScript and Python SDKs). - **Use the public API only.** The supported surfaces are `/v1` and `/v2`. Do not depend on undocumented endpoints — they are outside the public contract and may change without notice. - **Prefer `/v2` (USD-native).** `/v2` returns USD money objects with the exact IDR canonical amount + `meta.fx`. `/v1` is the legacy IDR-only API (integer minor units). Pick one version per integration. - **Auth:** every request sends `Authorization: Bearer ` (token from Account Settings). No cookies, no API key header. - **Idempotency = money safety.** Order create sends an `idempotency-key`. On a FAILED create, RETRY WITH THE SAME KEY — never mint a new one (a new key on retry risks a double charge). In TypeScript, reuse `error.idempotencyKey`; in Python, reuse `err.idempotency_key` and pass it back as `idempotency_key=err.idempotency_key`. - **Errors:** failures return `{ "success": false, "error": { "code", "message", "details"? } }` with a matching HTTP status. There are 16 stable error codes (branch on `error.code`, never on `error.message`) — the full table is in the [API reference](https://smscode.gg/docs). On `429`/`503`, honor the `Retry-After` header (seconds). ### Install an SDK ```bash bun add @smscode/sdk # or: npm i @smscode/sdk pip install smscode ``` ### TypeScript minimal recipe (create → waitForOtp → finish; cancel only when `can_cancel`) ```ts import { SmscodeClient, OtpTimeoutError, OrderTerminalError } from "@smscode/sdk"; const client = new SmscodeClient({ token: process.env.SMSCODE_TOKEN! }); // Create a WhatsApp/Indonesia order (max_price is a USD decimal string on /v2). const { orders, idempotencyKey } = await client.orders.create({ catalog_product_id: Number(process.env.SMSCODE_CATALOG_PRODUCT_ID), max_price: "0.50", quantity: 1, }); const orderId = orders[0]!.id; try { const { otpCode } = await client.orders.waitForOtp(orderId, { timeoutMs: 120_000 }); console.log("OTP:", otpCode); // Use the code, then FINISH. Once any SMS arrives, cancel/refund is closed. await client.orders.finish(orderId); } catch (err) { // A helper error is not proof that no SMS arrived. Re-read capabilities. if (err instanceof OtpTimeoutError || err instanceof OrderTerminalError) { const current = await client.orders.get(orderId); if (current.can_cancel) await client.orders.cancel(orderId); } else throw err; } // `idempotencyKey` is the key to reuse if you ever retry this exact create. ``` ### Python minimal recipe ```py import os from smscode import OtpTimeoutError, OrderTerminalError, SmscodeClient client = SmscodeClient(token=os.environ["SMSCODE_TOKEN"]) with client: created = client.orders.create( catalog_product_id=int(os.environ["SMSCODE_CATALOG_PRODUCT_ID"]), max_price="0.50", quantity=1, ) order_id = int(created.orders[0]["id"]) try: otp = client.orders.wait_for_otp(order_id, timeout_ms=120_000) print("OTP:", otp.otp_code) # Use the code, then FINISH. Once any SMS arrives, cancel/refund is closed. client.orders.finish(order_id) except (OtpTimeoutError, OrderTerminalError): current = client.orders.get(order_id) if current["can_cancel"]: client.orders.cancel(order_id) ``` `waitForOtp` / `wait_for_otp` waits for a classified code. A delivered SMS can instead have `otp_code=null` and a non-null `otp_message` containing the exact SMS text after U+0000 removal; active-order reads and `order.otp_received` webhooks expose that aggregate pair. The retained code may come from an earlier SMS than the latest message. Multiple deliveries are unordered, so reject older pairs by `sms_revision` and do not assume both fields coexisted in one provider message.