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 isPOST /api/v1/settings/api-keys with
mode: "test" (documented on the Settings page):
cURL
201
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). Digit2 is the happy path:
cURL
200
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
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 samemessage.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
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
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
{ 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
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
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