Skip to main content

Your first sandbox round trip

The six operations below are the sandbox control plane; this section is the smallest flow that proves your integration end to end before you spend a cent — one key, one send, one webhook, one reset. Every request body below runs unchanged against live mode later; only the key swaps.

1. Mint a test key

In the console: Settings → API Keys → Create key, mode Test. The same mint over the API is POST /api/v1/settings/api-keys with mode: "test" (documented on the Settings page):
cURL
201
The key value is returned only on this response — store it immediately. From here on, that prefix is the sandbox signal: every request carrying a dv_test_sk_ key runs in test mode with or without the X-Test-Mode header, no carrier is touched, and nothing bills.

2. One canonical send

Send to any E.164 recipient; the trailing digit selects the simulated outcome (§3 below covers the three you will use most; the full catalog is the Magic-numbers playbook). Digit 2 is the happy path:
cURL
200
Assert meta.test_mode: true in your test harness — it is the only reliable marker that the request actually sandboxed. The header you sent and the key prefix you pulled from config can drift between environments; the response marker cannot. The same send in Node:
Node.js

3. Magic numbers — the recipient picks the outcome

The three magic numbers you will reach for most, one send each:
cURL — delivered
200
cURL — DLR failed
200
cURL — carrier error
200
All three sends return test_sent; the simulated receipts never mutate the stored row. They arrive only on the webhook stream (§4 below), replaying the same intermediate → terminal sequence a real carrier produces — the 4 case arrives as a lone failed with no preceding sent ACK, which is exactly the branch your retry logic must not get wrong.

4. The sandbox-tagged DLR webhook

Orbit posts the same message.status envelope it posts in live mode to the sandbox webhook URL you set in the console, tagged so your receiver can tell test traffic apart:
Webhook POST — message.status
Verify the X-Devotel-Signature header, ack within 30 seconds, and dedupe on id — retries are expected. The verifying receiver (Node and Python) is worked in the Sandbox API cookbook; per-digit receipt timings stay canonical at magic numbers.

5. Reset the workspace

When a test run leaves debris, wipe the sandbox back to a clean slate in one call (the same operation worked with its full envelope as §6 below):
cURL
200
Contacts, conversations, messages, and sandbox-provisioned numbers are gone; your API keys, saved test recipients, and the sandbox webhook URL survive — they are org settings, not workspace data. Live-mode requests are refused with 403 SANDBOX_ONLY before a single row changes. Ready to cut over to real traffic? The pre-launch checklist (§2 in the per-operation section below, and its endpoint page) returns readyForLaunch: true once every gate passes, and sandbox and test mode walks the live-key graduation.

Worked sandbox samples

The generated endpoint list documents every operation’s parameters; this section walks a sandbox environment the way a pre-launch integration checklist actually uses it: list the sandbox numbers → read the pre-launch checklist → handle a provisioning reset. Success envelopes are { data, meta }, error envelopes { error, meta } — see How to read a worked sample. A sandbox (dv_test_sk_*) key drives the whole pipeline, and a reset always leaves your tenant with the clean fixture set the checklist expects. Every response carries meta.request_id. Quote the request id when you report a provisioned number that vanishes between calls or a checklist item that flips mid-run.

1. List sandbox numbers

GET /api/v1/sandbox/numbers returns the sandbox phone numbers provisioned for your tenant — the pool the rest of the sandbox surfaces send/receive from. Use this to verify the provision-numbers route below landed before you spawn fixture contacts.
200

2. Read the pre-launch checklist

GET /api/v1/sandbox/pre-launch-checklist returns the readiness items a launch gate evaluates — provisioned numbers, expected fixture contacts, inbound and outbound smoke-paths. Use it to decide when the tenant is a candidate for the live dv_live_sk_* cutover.
200

3. Provision test numbers

POST /api/v1/sandbox/provision-numbers mints deterministic fictional +1(555)01XX numbers for your sandbox tenant — the pool the rest of the surfaces send and receive from. Assert the returned count before you spawn fixture contacts that depend on these numbers.
200
The body is optional — omit it to mint the default batch. Pass { count, country_code, capabilities } to scale the pool deterministically.

4. Inject a simulated inbound message

POST /api/v1/sandbox/inbound pushes a synthetic mobile-originated message into the tenant inbox — the receive half of the Virtual Phone. Use it to exercise auto-responders, STOP keywords, or inbox routing without hand-forging a provider webhook.
200
The response returns the committed row IDs — assert created_conversation confirms the inbox list has a new thread. created_contact flips to true the first time a from appears.

5. Spawn fixture contacts

POST /api/v1/sandbox/spawn-fixture-contacts seeds deterministic contacts (and optionally conversations) so a probe can iterate through a non-empty inbox list repeatedly. Reset between runs to leave them idempotent.
200
with_conversations defaults to true; pass it as false when a probe just needs the contact rows.

6. Reset the sandbox

POST /api/v1/sandbox/reset wipes the tenant’s messages, conversations, and contacts, and releases every number the sandbox provisioned for you. Run it when a test pass needs a clean slate — after a reset the pre-launch checklist above reads exactly the fixture set a fresh sandbox starts with, so the probe sequence is deterministic from the first call.
200
The reset takes no body. Each counter reports how many rows that run removed — assert numbers_released before you provision again when a test depends on the pool being empty.

7. Errors

Errors follow the { error, meta } envelope. The failure every sandbox runner hits: 422 — malformed provisioning body. The reset call above with a body the sandbox pipeline cannot validate (Content-Type set to JSON and a non-JSON payload, or an unexpected JSON shape on the provisioning endpoints in the same namespace):
422
This overlay shows each op’s concrete envelope once; the guide-level Sandbox API cookbook and Magic-numbers playbook walk the full request/response pair across all six supported languages.