> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Prefix-scoped sandbox tokens and the shared pre-launch checklist

> Mint sandbox API keys whose prefix isolates them from production traffic, then read the shared five-step pre-launch checklist the API evaluates against your live workspace before go-live.

# Prefix-Scoped Sandbox Tokens

Every Orbit API key carries a prefix you read straight off the key itself:
`dv_live_sk_` and `dv_live_pk_` for production, `dv_test_sk_` and
`dv_test_pk_` for sandbox. The prefix is not cosmetic. It's the scoping
mechanism — a key starting with `dv_test_` can never send real traffic,
bill your wallet, or ring a live carrier, no matter which endpoint you aim
it at. Build your integration with a `dv_test_` key and production senders
stay unreachable to it. Swap to a `dv_live_` key at launch and the same
request bodies hit production paths.

This page covers the token model, how to mint sandbox tokens from the
dashboard and the API, the shared pre-launch checklist endpoint that gates
go-live, and a full sequence from token mint to a green checklist.

## How the scoping works

Orbit derives everything from the key prefix at authentication time:

| Prefix        | Class          | What it can reach                                                |
| ------------- | -------------- | ---------------------------------------------------------------- |
| `dv_test_sk_` | Sandbox secret | Sandbox endpoints only; never bills; never delivers real traffic |
| `dv_test_pk_` | Sandbox public | Sandbox endpoints only; safe to embed in client-side code        |
| `dv_live_sk_` | Live secret    | Production paths; bills your wallet                              |
| `dv_live_pk_` | Live public    | Production paths; for client-side embedding                      |

Two properties make the model safe to build against:

1. **A sandbox token cannot touch production.** Every `/sandbox/*` mutation
   endpoint refuses a non-sandbox context with `403 SANDBOX_ONLY`, and
   sends signed with a `dv_test_*` key are short-circuited before any
   carrier sees them. There is no request shape that escapes the prefix's
   class.

2. **Rotation preserves the dyad.** A sandbox public key (`dv_test_pk_`)
   rotates into a sandbox public key — never a secret one, never a live
   one. Re-minting from the dashboard or the API cannot change which side
   of your app may safely hold the credential, so a leaked public-test
   rotation never quietly escalates privilege.

Your organisation always has one paired sandbox workspace available on
**Developer → Sandbox** in the dashboard. The checklist endpoint below
reconciles both workspaces against the live one, so "test token minted"
and "live workspace ready" are properties of the pair, not of whichever
key you used to call.

## Mint a prefix-scoped sandbox token

**From the dashboard:** open **Settings → API Keys → Create API key**,
choose the **Test** environment, pick **Secret** (server-side) or
**Public** (browser/mobile embed), and save. The key mints with the
matching sandbox prefix — no other configuration is needed; scoping is the
prefix.

**From the API:** POST to the key creation endpoint with `mode: "test"`:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/settings/api-keys \
  -H "X-API-Key: $ORBIT_LIVE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "integration-test",
    "type": "secret",
    "mode": "test"
  }'
```

```json theme={null}
{
  "data": {
    "id": "key_…",
    "key": "dv_test_sk_…",
    "keyPrefix": "dv_test_sk_",
    "name": "integration-test",
    "type": "secret",
    "mode": "test"
  }
}
```

The plaintext `key` is returned exactly once — store it now. Send
`"type": "public"` with `"mode": "test"` to mint a `dv_test_pk_` key for a
client-side integration. Restrict who can mint which class by pinning the
call to a key with an [IP allowlist](/guides/api-key-ip-allowlist) and a
role-bounded scope.

A rotated sandbox token keeps its class: `POST
/api/v1/settings/api-keys/{keyId}/rotate` on a `dv_test_pk_` key returns a
fresh `dv_test_pk_` key and retires the old one on the same class axis.

## The shared pre-launch checklist

`GET /api/v1/sandbox/pre-launch-checklist` evaluates five ordered launch
steps against your **live** workspace, callable with either a live or a
sandbox key — the response always reports against the live side, so your
dashboard, a launch script, and an agent session all read the same state.

The five steps, in order:

1. `sandbox_workspace_ready` — a paired sandbox workspace exists.
2. `live_workspace_ready` — your live workspace exists and is not itself a
   test workspace.
3. `sandbox_token_minted` — at least one active `dv_test_sk_` or
   `dv_test_pk_` key exists on the live workspace.
4. `integration_exercised` — at least one sandbox key has authenticated a
   request, so an integration actually ran against the sandbox.
5. `ip_allowlist_configured` — at least one active key on the live
   workspace carries an IP allowlist.

Pass/fail is per item — each is `complete` or `pending`.
`readyForLaunch: true` only when all five are `complete`.

```bash theme={null}
curl https://api.orbit.devotel.io/api/v1/sandbox/pre-launch-checklist \
  -H "X-API-Key: $ORBIT_SANDBOX_KEY"
```

```json theme={null}
{
  "data": {
    "readyForLaunch": false,
    "isSandbox": true,
    "sandboxPairId": "org_…",
    "items": [
      { "id": "sandbox_workspace_ready", "status": "complete" },
      { "id": "live_workspace_ready", "status": "complete" },
      { "id": "sandbox_token_minted", "status": "complete" },
      { "id": "integration_exercised", "status": "pending" },
      { "id": "ip_allowlist_configured", "status": "pending" }
    ]
  }
}
```

Wire it into a launch script and fail CI on any `pending` item:

```bash theme={null}
npx --yes @devotel-orbit/cli checklist pre-launch --json \
  | jq -e '.data.readyForLaunch == true' || exit 1
```

The full item-to-remedy table lives on the [pre-launch
checklist](/sandbox/pre-launch-checklist) reference.

## Promote from sandbox to production

When `readyForLaunch` reads `true`, promotion is a key swap, not a rewrite:

1. Complete **KYC** and fund the balance — the two hard gates on live
   traffic.
2. Mint a live secret key (`dv_live_sk_`) under **Settings → API Keys**.
3. Swap the key in your environment from `dv_test_*` to `dv_live_*`.
4. Walk the humanised sign-off in the [production go-live
   checklist](/guides/go-live-checklist) — numbers, webhooks, guardrails,
   and compliance.

Code, request bodies, and webhook handlers stay identical; the class moves
with the credential.

## Full pre-launch sequence

End to end, from minting the sandbox token to a green checklist:

```bash theme={null}
# 1. Mint a sandbox secret key.
curl -X POST https://api.orbit.devotel.io/api/v1/settings/api-keys \
  -H "X-API-Key: $ORBIT_LIVE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"pre-launch","type":"secret","mode":"test"}'

# 2. Exercise the integration once (any sandbox send counts).
curl -X POST https://api.orbit.devotel.io/api/v1/messages/sms \
  -H "X-API-Key: dv_test_sk_…" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+15005550002",
    "from": "+15550100",
    "body": "pre-launch exercise"
  }'

# 3. Add an IP allowlist to the live key (satisfies step 5).
curl -X PATCH \
  https://api.orbit.devotel.io/api/v1/settings/api-keys/{keyId}/allowed-ips \
  -H "X-API-Key: $ORBIT_LIVE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"allowed_ips": ["203.0.113.10"]}'

# 4. Read the checklist until every item is complete.
curl https://api.orbit.devotel.io/api/v1/sandbox/pre-launch-checklist \
  -H "X-API-Key: dv_test_sk_…"
# -> {"data": {"readyForLaunch": true, …}}

# 5. Mint the live key and promote.
curl -X POST https://api.orbit.devotel.io/api/v1/settings/api-keys \
  -H "X-API-Key: $ORBIT_LIVE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"production","type":"secret","mode":"live"}'
```

Use the [Sandbox overview](/sandbox/overview) to orient the endpoints this
sequence exercises, the [magic numbers](/sandbox/magic-numbers) for the
DLR scenarios a sandbox send simulates, and the [sandbox and test
mode](/guides/sandbox-test-mode) walkthrough when you need inbound
simulation or deterministic OTPs before checklist step 4 can count the
integration as exercised.

## Next steps

* [Pre-launch checklist](/sandbox/pre-launch-checklist) — the step-by-step
  remedy table behind `GET /sandbox/pre-launch-checklist`
* [Production go-live checklist](/guides/go-live-checklist) — the human
  sign-off that follows the API gate
* [Sandbox overview](/sandbox/overview) — orientation for every sandbox
  endpoint the token can reach
* [Sandbox and test mode](/guides/sandbox-test-mode) — the full
  sandbox-to-live walkthrough
* [API key IP allowlists](/guides/api-key-ip-allowlist) — the control the
  final checklist step enforces
