Test keys and the sandbox
A key whose secret starts with msg_test_ runs your calls through the same code as a live key —
same validation, same errors, same response shapes — but nothing reaches WhatsApp and nobody's
phone rings. Build the whole integration on one, then swap in a live key. Nothing in your code
changes.
Responses carry testMode: true, so a test suite can assert it is really in the sandbox.
At a glance
msg_test_ | msg_live_ | |
|---|---|---|
| Reaches WhatsApp | no | yes |
| Needs an approved template for Verify | no | yes |
| The 24-hour window applies | no | yes |
| Counts against your message quota | no | yes |
| Can create or edit templates | no | yes |
| Uploads media for real | yes | yes |
| Delivers webhooks | no | yes |
Verifying a phone number
Both calls work end to end, with no approved authentication template and no phone involved.
verify.start returns the generated code alongside the verificationId, so your test can read
it and pass it straight to verify.check. A live key never returns the code — it goes to the
phone. Nothing is sent and nothing is billed.
Everything else behaves normally: wrong codes still come back invalid with the attempts counted
down, and an expired verification still expires.
Sending a message
The send is accepted, stored and returned to you with an id, but it never leaves for WhatsApp.
Two rules are not applied, because nothing is arriving at anyone's phone:
- The 24-hour window is not enforced, so you can send free-form text to a number that has never
messaged you. Against a live key the same call fails with
window_expired. - No contact record is created. A live send creates one; a test send addresses the number directly.
It does not count against your message quota either.
Creating a template
A test key cannot create or edit a template. Both return 403 test_key_not_allowed. Creating a
template permanently claims a name in your real WhatsApp account, which is not something a sandbox
should be able to spend.
Use templates.validate instead — it checks the same content rules and costs nothing. Listing and
fetching templates work normally.
Uploading media
Media is the exception: a test key uploads for real. The file is stored and pushed to Meta exactly as a live key would, and it counts against your plan's storage allowance. Only the send that carries it is simulated.
So a test suite that uploads on every run does consume your allowance. Upload once and reuse the id.
Webhooks
A test key delivers no webhooks. Delivery, read and failure events all originate at WhatsApp, and a test send never gets there — so nothing arrives at your callback URL.
That makes a test key the wrong tool for building a webhook handler — drive a real send with a live key instead. See Write a webhook handler.
Moving to live
The environment is fixed when a key is created, so going live means creating a live key and swapping the secret in your environment — see Set up your API access.
Keep a test key alive afterwards for CI and for reproducing bugs. It costs nothing and sends nothing.