REST API v1

DogeSMS Developer API

Automate OTP number allocation, SMS retrieval, and order management via the REST API at api.dogesms.com. Authenticate with an API Key. POST /v1/orders accepts an X-Idempotency-Key header to prevent duplicate charges on retries.

Quickstart workflow

A typical integration goes from credentials to SMS delivery in four steps.

1

Create an API Key

Generate an API Key in the dashboard under Settings → API Keys. Copy the key immediately — it is shown only once.

Use one key per environment (development, staging, production) so key rotation and audit logs stay clean.

2

Check your balance

Call GET /v1/balance before ordering to confirm the account has sufficient funds.

Balance is returned in cents (integer). Divide by 100 to display USD. Top up via the dashboard if balance is low.

3

Create an order

POST /v1/orders with service_code and country_code. Include an X-Idempotency-Key UUID to prevent duplicate orders on retries.

Store the returned order id immediately. You will use it to poll status and, if needed, cancel the order.

4

Receive number & SMS

Recommended: register a webhook to receive order.number_allocated (phone_number ready) and order.completed (sms_code ready) in real time — no polling. Alternatively, poll GET /v1/orders/{id}: active means phone_number is ready; completed means sms_code is available.

Treat expired, cancelled, and failed as terminal states. Cancel with POST /v1/orders/{id}/cancel if the SMS does not arrive within the expected window.

Authentication

Pass your API Key in the Authorization header: Authorization: Bearer sk_live_…

Keys start with sk_live_ — never share or commit them to source control.

Rotate keys any time from the dashboard; old keys are invalidated immediately.

Rate Limiting & Idempotency

Include X-Idempotency-Key: <uuid> on POST /v1/orders (CreateOrder) to make retries safe. The cancel endpoint does not use this header.

A key is permanently bound to the original creation attempt — reusing it after the order ends silently returns the original response, not a new order. Always generate a fresh UUID for each new order.

On HTTP 429, back off exponentially before retrying.

Integration model

Treat the API as an order workflow rather than a single request-response call.

Safe writes with Idempotency-Key

Generate a fresh UUID for each POST /v1/orders call and store it with the order. Retrying the same key returns the original result without charging again.

Track the order lifecycle

Orders move through pending → active (phone_number ready) → completed (sms_code ready), or reach expired / cancelled / failed. Subscribe to webhooks (order.number_allocated, order.completed) to react in real time, or poll GET /v1/orders/{id} as a fallback.

Handle transient failures

HTTP 429 and 422 SERVICE_NOT_AVAILABLE (no inventory) are expected operational conditions. Back off and retry rather than surfacing raw error codes to end users.

Webhooks (recommended)

Receive order events via HTTP POST the moment they happen — no polling required. Register one endpoint and we push the full lifecycle (number allocation, SMS delivery, and terminal failure/expiry) to you in real time.

Register your endpoint

Configure your webhook URL in the dashboard under Settings → Webhooks (your API Key and webhook endpoint share the same account). The signing secret is shown once at creation — store it safely.

Events

order.number_allocatedorder.number_allocated — fires when the number is allocated (status becomes active). The payload includes phone_number so you can enter it on the target platform. sms_code is not available yet.

order.completedorder.completed — fires when the SMS arrives (status becomes completed). The payload includes sms_code and sms_content.

order.failedorder.failed — fires when the order fails (no number available or allocation error). The payload includes a sanitized error_code; no charge is retained.

order.expiredorder.expired — fires when the number was allocated but no SMS arrived within the window (status becomes expired). The charge is automatically refunded.

Example payloads

POST <your webhook url>
X-Webhook-Signature: sha256=<hex>
Content-Type: application/json

{
  "event_type": "order.number_allocated",
  "order_id": "01960a9b-…",
  "service_code": "whatsapp",
  "country_code": "US",
  "phone_number": "+12015550123",
  "status": "active",
  "allocated_at": "2024-11-01T09:00:05Z"
}
{
  "event_type": "order.completed",
  "order_id": "01960a9b-…",
  "service_code": "whatsapp",
  "country_code": "US",
  "phone_number": "+12015550123",
  "sms_code": "123456",
  "sms_content": "Your code is 123456",
  "completed_at": "2024-11-01T09:01:30Z"
}
{
  "event_type": "order.failed",
  "order_id": "01960a9b-…",
  "service_code": "whatsapp",
  "country_code": "US",
  "status": "failed",
  "error_code": "NO_NUMBERS_AVAILABLE",
  "failed_at": "2024-11-01T09:00:30Z"
}

Verify the signature

Every request carries an X-Webhook-Signature: sha256=<hex> header. Compute HMAC-SHA256 over the raw request body with your secret and compare in constant time before trusting the payload.

import crypto from 'node:crypto'

function verify(rawBody, header, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)              // raw bytes, not parsed JSON
    .digest('hex')
  const sig = Buffer.from(header || '', 'utf8')
  const exp = Buffer.from(expected, 'utf8')
  // length check first — timingSafeEqual throws on unequal lengths
  return sig.length === exp.length &&
    crypto.timingSafeEqual(sig, exp)
}

Delivery & retries

Respond with 2xx quickly (within 10s). Failed deliveries are retried up to 5 times with backoff (30s → 5m → 30m → 2h). Delivery is best-effort.

Reconciliation fallback

Because delivery is best-effort, keep GET /v1/orders/{id} as a fallback to reconcile any missed events. With both events wired, polling becomes a safety net rather than your primary path.

API Endpoints

Base URL: https://api.dogesms.com — all paths below are relative to this base.

MethodPathDescriptionCommon Responses
GET/v1/balanceGet account balance200 Returns { data: { balance_cents: number, currency: string }, request_id: string }. / 401 API Key missing, invalid, or revoked.
POST/v1/ordersCreate an order201 Order created. Returns the order object with id and status. phone_number is available when status becomes active; sms_code is available when status becomes completed. Poll GET /v1/orders/{id} to track transitions. / 400 Missing or invalid parameters (service_code or country_code absent, bad Idempotency-Key format, etc.). / 412 Actual price exceeds the spend cap (max_price_cents). Error code: PRICE_CHANGED. / 422 Insufficient balance (INSUFFICIENT_BALANCE), wallet not initialized (WALLET_NOT_INITIALIZED), or no numbers available for this service/country combination (SERVICE_NOT_AVAILABLE).
GET/v1/ordersList orders200 Returns { data: { items: Order[], total: number, limit: number, offset: number }, request_id: string }. / 401 API Key missing, invalid, or revoked.
GET/v1/orders/{id}Get a single order200 Returns the order object. / 404 Order not found or does not belong to this account.
POST/v1/orders/{id}/cancelCancel an order200 Cancellation accepted. If the order is already in a terminal state (completed, expired, failed, or cancelled) and its state can be read back, the current order object is returned idempotently with no side effects. / 404 Order not found or does not belong to this account. / 409 The order is in a terminal state but its state could not be retrieved — a rare defensive fallback (error code: ORDER_ALREADY_TERMINAL). Fetch the current state with GET /v1/orders/{id}. / 422 Cancellation not allowed within 2 minutes of order creation (cooldown). Error code: CANCEL_TOO_EARLY.
GET/v1/catalog/servicesList available services200 Success. Returns an array of service objects, each with code and name. / 401 Authentication failed.
GET/v1/catalog/countriesList available countries200 Success. Returns an array of country objects with code, name, phone_prefix, and service_count. / 401 Authentication failed.
GET/v1/catalog/pricesList prices for a country200 Success. Returns an array with service_code, service_name, price_cents, and available_count. / 400 Missing country_code parameter. / 401 Authentication failed. / 404 Country not found or no price data available.
GET/v1/balance

Get account balance

Returns the current balance in cents. Check this before placing orders to avoid INSUFFICIENT_BALANCE (422) errors.

Parameters

No request parameters required for this endpoint.

Response codes

200

Returns { data: { balance_cents: number, currency: string }, request_id: string }.

401

API Key missing, invalid, or revoked.

POST/v1/orders

Create an order

Creates an order for the requested service and country. Returns the order object with id and status. Poll GET /v1/orders/{id}: phone_number is available when status becomes active; sms_code is available when status becomes completed. Include X-Idempotency-Key to prevent duplicate charges on retries.

Parameters

service_codestringRequired

Service to activate, e.g. whatsapp or telegram.

country_codestringRequired

Two-letter country code for number allocation, e.g. US or GB.

tierstringOptional

Price tier: standard (default) or premium.

max_price_centsintegerOptional

Optional spend cap in cents. If the actual price exceeds this (with 5% tolerance), the order returns 412 PRICE_CHANGED.

Response codes

201

Order created. Returns the order object with id and status. phone_number is available when status becomes active; sms_code is available when status becomes completed. Poll GET /v1/orders/{id} to track transitions.

400

Missing or invalid parameters (service_code or country_code absent, bad Idempotency-Key format, etc.).

412

Actual price exceeds the spend cap (max_price_cents). Error code: PRICE_CHANGED.

422

Insufficient balance (INSUFFICIENT_BALANCE), wallet not initialized (WALLET_NOT_INITIALIZED), or no numbers available for this service/country combination (SERVICE_NOT_AVAILABLE).

GET/v1/orders

List orders

Returns a paginated list of orders belonging to the authenticated API Key. Filter by status to find pending or completed orders.

Parameters

limitintegerOptional

Page size, default 20, max 100.

offsetintegerOptional

Pagination offset, default 0.

statusstringOptional

Comma-separated status filter, e.g. pending,completed.

Response codes

200

Returns { data: { items: Order[], total: number, limit: number, offset: number }, request_id: string }.

401

API Key missing, invalid, or revoked.

GET/v1/orders/{id}

Get a single order

Fetch full order details by UUID, including current status, phone_number, sms_code, sms_content, and lifecycle timestamps.

Parameters

idstringRequired

Order UUID returned by POST /v1/orders.

Response codes

200

Returns the order object.

404

Order not found or does not belong to this account.

POST/v1/orders/{id}/cancel

Cancel an order

Cancels an order that is still in progress — either pending (awaiting number allocation) or active (number assigned, waiting for the SMS). Releases the reserved number and refunds the charge. Completed, failed, or already-cancelled orders cannot be cancelled.

Parameters

idstringRequired

Order UUID to cancel.

Response codes

200

Cancellation accepted. If the order is already in a terminal state (completed, expired, failed, or cancelled) and its state can be read back, the current order object is returned idempotently with no side effects.

404

Order not found or does not belong to this account.

409

The order is in a terminal state but its state could not be retrieved — a rare defensive fallback (error code: ORDER_ALREADY_TERMINAL). Fetch the current state with GET /v1/orders/{id}.

422

Cancellation not allowed within 2 minutes of order creation (cooldown). Error code: CANCEL_TOO_EARLY.

GET/v1/catalog/services

List available services

Returns all supported services (e.g. WhatsApp, Telegram, Google). Use service codes from this list as service_code when creating orders.

Parameters

No request parameters required for this endpoint.

Response codes

200

Success. Returns an array of service objects, each with code and name.

401

Authentication failed.

GET/v1/catalog/countries

List available countries

Returns all supported countries, sorted by number of available services (most popular first). Use country codes from this list as country_code when creating orders.

Parameters

No request parameters required for this endpoint.

Response codes

200

Success. Returns an array of country objects with code, name, phone_prefix, and service_count.

401

Authentication failed.

GET/v1/catalog/prices

List prices for a country

Returns all available services and their reference prices for the specified country. Prices are in cents (USD) and reflect the displayed price including markup. The server applies authoritative catalog pricing at order time — use max_price_cents on POST /v1/orders if you need a price cap.

Parameters

country_codestringRequired

ISO country code (e.g. US, GB). Case-insensitive.

Response codes

200

Success. Returns an array with service_code, service_name, price_cents, and available_count.

400

Missing country_code parameter.

401

Authentication failed.

404

Country not found or no price data available.

Create Order Example

Request body and response JSON when calling POST /v1/orders.

POST https://api.dogesms.com/v1/orders
Authorization: Bearer sk_live_…
X-Idempotency-Key: 73b7f4a2-1c3e-4d5f-8e9a-0b1c2d3e4f52
Content-Type: application/json

{
  "service_code": "whatsapp",
  "country_code": "US"
}
HTTP 201 Created
X-Request-Id: req_abc123…

{
  "data": {
    "id": "01960a9b-…",
    "order_no": "ORD-20241101-0042",
    "service_code": "whatsapp",
    "country_code": "US",
    "status": "pending",
    "amount_cents": 0,
    "currency": "USD",
    "created_at": "2024-11-01T09:00:00Z"
  },
  "request_id": "req_abc123…"
}
// Poll GET /v1/orders/{id}:
//   "pending"  → waiting for number allocation
//   "active"   → phone_number now available; enter it on the target platform
//   "completed"→ sms_code ready; order done
//   "expired" / "cancelled" / "failed" → terminal error states

Supported Services & Countries

Currently 6 services and 9 countries/regions available.

📱 WhatsApp✈️ Telegram🔍 Google🎵 TikTok🛒 Amazon🧧 淘宝

Availability varies in real time. Always check your order response rather than assuming a fixed catalog.

Common error codes

Handle these status codes explicitly in your integration rather than relying on generic error handling.

401

Unauthorized

The API Key is missing, malformed, or revoked.

Verify the Authorization header format (Bearer sk_live_…) and confirm the key is active in the dashboard.

403

Forbidden

The API Key is valid but the account is not allowed to make requests: banned (ACCOUNT_BANNED), email not verified (EMAIL_NOT_VERIFIED), or not active (ACCOUNT_NOT_ACTIVE).

Check your account status in the dashboard. Verify your email if prompted, or contact support if your account is banned.

409

State conflict

The order is already in a terminal state (completed, cancelled, or failed) and cannot be modified.

Fetch the latest order state with GET /v1/orders/{id} before retrying a write operation.

412

Price changed

The actual price for the requested number exceeds the max_price_cents cap you supplied.

Remove max_price_cents to place the order at the current market price, or raise the cap and retry.

422

Unprocessable request

Balance is insufficient (INSUFFICIENT_BALANCE) or no numbers are available for the requested service/country (SERVICE_NOT_AVAILABLE).

For INSUFFICIENT_BALANCE: top up via the dashboard. For SERVICE_NOT_AVAILABLE: retry after a short delay or try a different country.

429

Rate limit exceeded

Too many requests in the current time window.

Back off exponentially, preserve your Idempotency-Key, and retry after the window resets.

Technical Support

Telegram Support

@dogesms_official

Hours: Daily 09:00-21:00 UTC+8 for live follow-up

Email Support

support@dogesms.com

Hours: 24/7 ticket intake with business-hour review

For high-volume or enterprise access, contact the sales team.