Skip to content

Sandbox keys (sk_test_…) let you build and test your entire integration — sending, status polling, and webhook handling — without sending real SMS or spending money. For the standard API endpoints, switching to production is swapping one environment variable. Integrations follow their platform guide; only guides marked managed require managed activation.

Live key (sk_prod_…) Sandbox key (sk_test_…)
Validation & errors Real Same request validation; production account/cost lookup and blocked-country filtering are skipped
Segments & encoding Real Identical (GSM-7/UCS-2 detection)
Cost preview Real rates Real rates when cached, else 0
SMS delivery Real carrier Simulated — nothing is sent
Balance Deducted Never touched
Delivery webhook Real DLR Synthesized DELIVRD ~5s after send, same HMAC signing, payload carries "sandbox": true, one attempt, no retries
Message storage Normal retained history 24 hours, last 500 messages
Response — Carries "sandbox": true
  1. Create a sandbox key. In the portal under Settings → API Keys, enable Test mode when creating the key. You get an sk_test_… key.

  2. Send a simulated SMS — same request as production:

    Terminal window
    curl -X POST https://restlink23telecom.com/api/v1/sms/send \
    -H "X-API-Key: $API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "to": [
    "+447911123456"
    ],
    "message": "Sandbox hello!",
    "sender_id": "MyApp"
    }'

    The success response has the production fields plus "sandbox": true.

  3. Watch the delivery report arrive. About 5 seconds later your configured delivery_url receives a synthesized DELIVRD webhook with a valid HMAC signature — verify it exactly like a real one (securing webhooks).

  4. Poll status if you prefer: GET /sms/status/{message_id} returns sent, then DELIVRD after the simulated carrier latency.

Synthesized webhooks use an isolated bounded pool with a single attempt. They appear on your webhook-logs page, are never retried, and are never queued with live traffic.

  • Sandbox message IDs are opaque values such as test_<account>_<nanosecond_timestamp>_<hex> and history is kept for 24 hours (last 500 per workspace). Always use the value returned by the send response. It never appears in production statistics or exports. An unknown or expired ID returns 404 NOT_FOUND: “sandbox message not found or expired; sandbox message IDs start with test_ and are retained for 24 hours.” A test_ ID queried with a live sk_prod_ key returns 404 guidance to query it with the sk_test_ key.
  • URL auto-shortening, blocked-country filtering and production account/cost lookup are skipped. Live pricing or balance cache misses are fail-open, not local send gates.
  • GET /sms/messages supports pagination but not SQL-grade filters.
  • If the simulator is unavailable you get 503 SANDBOX_UNAVAILABLE — a test key never falls through to the live pipeline.

From the docs playground (https://docs.23telecom.co.uk/api) the API additionally refuses live keys with 403 LIVE_KEY_FROM_PLAYGROUND and answers preflights for non-sandbox routes with 403 CORS_ROUTE_NOT_ALLOWED.

The daily quota is 10,000 recipients per account per UTC day. Exceeding it returns 429 SANDBOX_QUOTA_EXCEEDED with Retry-After until UTC midnight. The global capacity limit returns 503 SANDBOX_BUSY with Retry-After: 5.

For the standard /sms/send and /sms/send-individual endpoints, replace the key. That’s the whole migration:

Terminal window
# before
export API_KEY=sk_test_abc...
# after
export API_KEY=sk_prod_xyz...

For the self-service /integrations/sms/send path, replace the test key with an eligible live key bound to your workspace with sms.send permission to send immediately. Live requests also require the integration to be enabled, the workspace not to be blocked, and workspace credentials to be available; normal validation and retryable infrastructure errors also apply; see production sending. CRM platform guides marked managed follow their own Production activation section. Do not promote a legacy CRM batch template by changing only its key.