Build safely with test mode
Test-mode API keys (ody_test_…) behave exactly like live keys except that anything that would spend money or reach the outside world is simulated — message sends, number purchases, outbound AI calls, agent publishing, and 10DLC registrations (which run in mock mode) — which makes them perfect for development and CI.
Before you start
- You must be a workspace admin or owner to create keys.
- Know what test mode does and doesn't isolate: a test key works against your real workspace data. Contact creates, updates, and deletes are real, conversation status changes are real, and agent draft saves (
PATCH /v1/api/agent) are real. What's simulated is the spending: sends, number purchases, outbound AI calls, agent publishing, and 10DLC filings.
Step-by-step
Click Settings in the sidebar, then Developer API.
Fill in the create form as usual — name and scopes (see Get your Ody API key).
Under Key type, select Test. The hint confirms it: test keys read real data but never send real texts — sends are recorded as simulated.
Click Create key and copy the
ody_test_…secret from the one-time banner.Use it exactly like a live key — same base URL, endpoints, scopes, and rate limits:
curl https://api.ody.co/v1/api/messages \
-H "Authorization: Bearer ody_test_…" \
-H "Content-Type: application/json" \
-d '{"to":"+15125550123","body":"CI smoke test"}'
The response is a normal 201 with "simulated": true, and the message appears in the thread with deliveryStatus: "simulated".
What's identical, what's simulated
| Behavior | With an ody_test_… key |
|---|---|
| Reading contacts, conversations, calls, numbers | Real data, identical to live |
| Creating/updating/deleting contacts | Real — changes your workspace |
| Setting conversation status | Real |
POST /v1/api/messages / MCP send_message |
Simulated — recorded, never transmitted |
POST /v1/api/numbers / MCP buy_number |
Simulated — "simulated": true, nothing ordered, provisioned, or charged |
POST /v1/api/calls / MCP place_call |
Simulated — a sim_… call id, nothing dialed, no credits used |
POST /v1/api/agent/publish |
Simulated — your config stays a draft, nothing goes live |
PATCH /v1/api/agent (draft saves) |
Real — same as live |
POST /v1/api/messaging/registration |
Mock mode — the full 10DLC pipeline runs, but nothing is submitted to the registry and there's no fee |
| Scopes, rate limits, errors, pagination, idempotency | Identical to live |
| Webhook endpoint management | Real endpoints, same as live |
In the key list, test keys show a Test badge and an ody_test_…WXYZ hint so you can't confuse them with live keys.
Using test mode in CI
- Create a dedicated test key per pipeline (for example "GitHub Actions") so you can revoke it independently and the "last used" column stays meaningful.
- Store it as a CI secret (e.g.
ODY_API_KEY) — your code doesn't change between environments, only the key. - Assert on
simulated: truein send responses to catch a live key accidentally configured in CI. - Remember that contact writes are real: point CI at a dedicated workspace, or clean up the contacts your tests create.
Related articles
Frequently asked questions
Do test keys use a separate sandbox workspace?
No — a test key reads and writes your real workspace data. Message sends, number purchases, outbound AI calls, agent publishing, and 10DLC filings are simulated; everything else behaves exactly like a live key.
How do I tell a simulated message apart?
The send response includes "simulated": true, and the recorded message carries deliveryStatus "simulated" instead of queued/sent/delivered.
Do test keys work with MCP?
Yes. Connect an AI assistant with an ody_test_ key and every tool works normally, but send_message records a simulated message instead of texting anyone.