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.
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.
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.
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.
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_allocated — order.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.completed — order.completed — fires when the SMS arrives (status becomes completed). The payload includes sms_code and sms_content.
• order.failed — order.failed — fires when the order fails (no number available or allocation error). The payload includes a sanitized error_code; no charge is retained.
• order.expired — order.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.
| Method | Path | Description | Common Responses |
|---|---|---|---|
| GET | /v1/balance | Get account balance | 200 Returns { data: { balance_cents: number, currency: string }, request_id: string }. / 401 API Key missing, invalid, or revoked. |
| POST | /v1/orders | Create an order | 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 | 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 | 200 Returns the order object. / 404 Order not found or does not belong to this account. |
| POST | /v1/orders/{id}/cancel | Cancel an order | 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 | 200 Success. Returns an array of service objects, each with code and name. / 401 Authentication failed. |
| GET | /v1/catalog/countries | List available countries | 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 | 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. |
/v1/balanceGet 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
Returns { data: { balance_cents: number, currency: string }, request_id: string }.
API Key missing, invalid, or revoked.
/v1/ordersCreate 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_codestringRequiredService to activate, e.g. whatsapp or telegram.
country_codestringRequiredTwo-letter country code for number allocation, e.g. US or GB.
tierstringOptionalPrice tier: standard (default) or premium.
max_price_centsintegerOptionalOptional spend cap in cents. If the actual price exceeds this (with 5% tolerance), the order returns 412 PRICE_CHANGED.
Response codes
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.
Missing or invalid parameters (service_code or country_code absent, bad Idempotency-Key format, etc.).
Actual price exceeds the spend cap (max_price_cents). Error code: PRICE_CHANGED.
Insufficient balance (INSUFFICIENT_BALANCE), wallet not initialized (WALLET_NOT_INITIALIZED), or no numbers available for this service/country combination (SERVICE_NOT_AVAILABLE).
/v1/ordersList orders
Returns a paginated list of orders belonging to the authenticated API Key. Filter by status to find pending or completed orders.
Parameters
limitintegerOptionalPage size, default 20, max 100.
offsetintegerOptionalPagination offset, default 0.
statusstringOptionalComma-separated status filter, e.g. pending,completed.
Response codes
Returns { data: { items: Order[], total: number, limit: number, offset: number }, request_id: string }.
API Key missing, invalid, or revoked.
/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
idstringRequiredOrder UUID returned by POST /v1/orders.
Response codes
Returns the order object.
Order not found or does not belong to this account.
/v1/orders/{id}/cancelCancel 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
idstringRequiredOrder UUID to cancel.
Response codes
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.
Order not found or does not belong to this account.
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}.
Cancellation not allowed within 2 minutes of order creation (cooldown). Error code: CANCEL_TOO_EARLY.
/v1/catalog/servicesList 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
Success. Returns an array of service objects, each with code and name.
Authentication failed.
/v1/catalog/countriesList 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
Success. Returns an array of country objects with code, name, phone_prefix, and service_count.
Authentication failed.
/v1/catalog/pricesList 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_codestringRequiredISO country code (e.g. US, GB). Case-insensitive.
Response codes
Success. Returns an array with service_code, service_name, price_cents, and available_count.
Missing country_code parameter.
Authentication failed.
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 statesSupported Services & Countries
Currently 6 services and 9 countries/regions available.
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.
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.
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.
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.
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.
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.
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.