Test mode
Build against the real API with a test key — every check runs, nothing reaches a carrier, and it costs nothing.
What you'll build
A test setup where simulated customers write to you and your code (or your agent) answers, through the real API, at no cost.
How test mode works
A test key (vl_test_…) runs the whole sending pipeline — validation, scopes, content checks, rate limits — except the two things that cost: the carrier and the charge. Messages leave through a simulated carrier instead of a real one.
Prerequisites
- A test key: in the dashboard, Developers → API keys → Create key, "Your servers or the SDK", environment Testing. See Authentication and API keys.
- Nothing else. You don't need a phone number or a connected account: without one, test mode uses your free test sender.
- curl, Node.js 18 or later with
@voicelayer/api, or Python 3.9 or later withvoicelayer.
Reserved test addresses
Test keys talk only to these addresses, and only test keys may use them:
| Channel | Addresses |
|---|---|
| SMS, MMS, WhatsApp | +15550100 to +15550199 |
| Telegram, Discord | test-1, test-2, … (test- and a number) |
any address at test.vlayers.ai |
A test key that names any other address is refused with 422 test_address_required. A live key that names a test address is refused with 422 test_address_not_allowed.
Your free test sender
Every workspace gets a free test sender the first time it needs one. When you have no number, connected account or mailbox on a channel, POST /v1/test/inbound without to and a test key's POST /v1/messages without from use it. It never reaches a carrier and costs nothing.
- It has no agent, so its conversations start with the controller
api: your code answers them (see Conversations and handoff). - It works only in test mode: a live key never sends through it.
- It has no reply window or registration of its own. To test your agent's replies, or a channel's real rules (WhatsApp's 24-hour window, for example), simulate the message on your real number or account by naming it in
to.
Step 1: Simulate someone writing to you
POST /v1/test/inbound plays a person writing from a test address. It works with test keys only; a live key gets test_mode_only.
curl -X POST https://api.vlayers.ai/v1/test/inbound \
-H "Authorization: Bearer $VOICELAYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel": "sms",
"from": "+15550123",
"text": "Do you have a table for two tonight?"
}'{
"conversationId": "0192f5b0-7c3e-7a51-9b2d-4e8f1a6c3d21",
"messageId": "0192f5b0-8d1f-7b62-8c3e-5f9a2b7d4e32",
"test": true
}to is optional: left out, the message goes to your one sender on that channel, or to your free test sender if you have none. Name a sender in to when you have several. Email works too: send "channel": "email" with a from at test.vlayers.ai, and the message arrives on your mailbox (or the test sender).
Step 2: Reply or send
Reply as usual with POST /v1/conversations/{id}/messages, or send to a test address with POST /v1/messages:
curl -X POST https://api.vlayers.ai/v1/messages \
-H "Authorization: Bearer $VOICELAYER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel": "sms",
"to": "+15550123",
"content": {
"type": "text",
"text": "Your table for two is booked for 7pm."
}
}'What a test conversation does
The conversation a simulated message opens is marked test: true. That mark decides the rest:
- every send in it — your replies, and your agent's — goes to the simulated carrier, never a real one;
- it costs $0;
- its events (
message.received,message.sent, …) reach your webhooks and the stream like any other, with"livemode": false; - the dashboard hides test conversations by default: turn on Show test conversations on the Conversations page to see them.
Daily caps
Test mode is free and capped per workspace per UTC day: by default 1,000 test messages (simulated inbound messages and sends together) and 100 agent replies. Past a cap, requests are refused with 429 test_limit_reached until midnight UTC. Ask support if you need more.
Test it
Run Step 1, save the conversationId as CONVERSATION_ID, reply, then read the conversation with GET /v1/conversations/{id}/messages. The Quickstart walks through it.
Going live
Switch the test key for a live one and the test addresses for real ones. Nothing else in your code changes: the routes, bodies, events and error codes are the same.
Common errors
| Error | Status | How to fix |
|---|---|---|
test_mode_only | 403 | Use a test key (vl_test_…) from the dashboard. |
test_address_required | 422 | Use a reserved test address, or a live key to reach a real person. |
test_address_not_allowed | 422 | Use a test key (vl_test_…) for test addresses. |
test_limit_reached | 429 | Try again after midnight UTC, or ask for a higher test limit. |
sender_ambiguous | 409 | Name one of details.senders in from. |