Sandbox
Test WhatsApp sending against a sandbox number before going to production
Sandbox
Zernio runs a shared WhatsApp sandbox number every account can test against without purchasing or verifying their own number. Use it to validate message flows, bot replies, and webhook payloads end-to-end before going live.
| Property | Value |
|---|---|
| Shared number | One Zernio-owned WhatsApp number (returned by GET /v1/whatsapp/phone-numbers under sandbox) |
| Verification model | Reply-based: we send a verified template to your test phone, you reply, the session activates |
| Templates allowed | One, locked: sandbox_start (no merge fields) |
| Free-form text & interactive | Yes, within the 24h customer-service window after the user replies |
| Test phones per user | 1 (one active session at a time) |
| Daily caps per user | 50 messages / 5 distinct recipients (effectively 1) |
| Pending session TTL | 24 hours |
| Activated session TTL | 7 days |
| Billing | Free (no per-number charge, no message charge) |
The sandbox is for testing only. To send to multiple recipients, sync templates from WhatsApp Manager, or run production traffic, purchase a real WhatsApp number instead.
Why activation matters
Without ownership verification, anyone with a Zernio API key could fire the verified Meta template from +1 202 908 7457 at any phone they typed. That would degrade the WABA's quality rating (shared across all users) and drain its messaging tier. Activation closes the abuse vector: only phones the user proved they control via a WhatsApp reply can be targeted from the sandbox.
Step 1 — Discover the sandbox
The sandbox phone number, the SocialAccount id you need to send from, and the locked template name all come back from the standard phone-numbers endpoint:
const { data } = await zernio.whatsappphonenumbers.getWhatsAppPhoneNumbers();
if (!data.sandbox) throw new Error('Sandbox not configured on this environment');
const { accountId, phoneNumber, template } = data.sandbox;
// accountId → use as `accountId` in inbox compose calls
// phoneNumber → the sandbox MSISDN (display only)
// template → { name: 'sandbox_start', language: 'en' }response = client.whatsappphonenumbers.get_whats_app_phone_numbers()
if not response.sandbox:
raise Exception("Sandbox not configured on this environment")
account_id = response.sandbox.account_id
phone_number = response.sandbox.phone_number
template = response.sandbox.template # { name, language }curl "https://zernio.com/api/v1/whatsapp/phone-numbers" \
-H "Authorization: Bearer YOUR_API_KEY"
# → { "numbers": [...], "sandbox": { "accountId": "...", "phoneNumber": "+12029087457", "template": { "name": "sandbox_start", "language": "en" } } }Step 2 — Start activation for a test phone
Posting a phone creates a pending session and immediately fires the verified sandbox_start template from the sandbox number to that phone. The end user opens WhatsApp on their phone and replies to that message — any text reply (including tapping the "Reply and verify" quick-reply button) flips the session to active.
One phone per user: if you already have a pending or active session for a different phone, the request returns a 400 naming the existing phone so you know to revoke it first.
const { data } = await zernio.whatsappsandbox.createWhatsAppSandboxSession({
body: { phone: '+34688246216' }
});
console.log(data.session.id, data.session.status); // 'pending'
// Tell the user: "Check WhatsApp on +34 688 24 62 16 and reply to verify"response = client.whatsappsandbox.create_whats_app_sandbox_session(phone='+34688246216')
print(response.session.id, response.session.status) # 'pending'curl -X POST "https://zernio.com/api/v1/whatsapp/sandbox/sessions" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phone": "+34688246216"}'Step 3 — Wait for the user to reply
Activation is automatic. You can poll the list endpoint, or — if you've configured webhooks — listen for the conversation events.
const { data } = await zernio.whatsappsandbox.listWhatsAppSandboxSessions();
const session = data.sessions[0];
if (session?.status === 'active') {
console.log('Verified at', session.activatedAt);
}response = client.whatsappsandbox.list_whats_app_sandbox_sessions()
session = response.sessions[0] if response.sessions else None
if session and session.status == 'active':
print('Verified at', session.activated_at)curl "https://zernio.com/api/v1/whatsapp/sandbox/sessions" \
-H "Authorization: Bearer YOUR_API_KEY"Step 4 — Send to the activated phone
Once active, the phone is a normal WhatsApp conversation in the user's inbox. Send the locked template using the standard inbox compose endpoint, addressed at the sandbox account. The same template + activation gate applies — random phone numbers are rejected.
const { data } = await zernio.inbox.createConversation({
body: {
accountId: sandbox.accountId,
participantId: '34688246216', // digits only, must match the activated phone
templateName: sandbox.template.name, // 'sandbox_start'
templateLanguage: sandbox.template.language, // 'en'
templateParams: []
}
});
console.log('Conversation:', data.data.id);response = client.inbox.create_conversation(
account_id=sandbox.account_id,
participant_id='34688246216',
template_name=sandbox.template.name,
template_language=sandbox.template.language,
template_params=[]
)
print('Conversation:', response.data.id)curl -X POST "https://zernio.com/api/v1/inbox/conversations" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "SANDBOX_ACCOUNT_ID",
"participantId": "34688246216",
"templateName": "sandbox_start",
"templateLanguage": "en",
"templateParams": []
}'After the user replies, the 24h customer-service window opens — within that window you can send free-form text via POST /v1/inbox/conversations/{conversationId}/messages exactly like any other conversation.
Replacing the activated phone
Activate a different phone by deleting the current session first, then creating a new one:
# 1. Revoke the existing session
curl -X DELETE "https://zernio.com/api/v1/whatsapp/sandbox/sessions/SESSION_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
# 2. Start activation for the new phone
curl -X POST "https://zernio.com/api/v1/whatsapp/sandbox/sessions" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phone": "+34999888777"}'Common errors
| Error | Cause | Fix |
|---|---|---|
invalid_field_value with "This phone is not activated for sandbox sends" | The recipient phone has no active session for the calling user | Run the activation flow (Steps 2–3) for that phone first |
invalid_field_value with "You can only test one phone at a time. Revoke +X first" | The user already has a session for a different phone | DELETE the existing session, then POST the new phone |
invalid_field_value with "Cannot activate the sandbox number itself" | The phone passed is the sandbox MSISDN | Use a real, different WhatsApp number |
invalid_field_value with "Could not send the activation message…" | Meta rejected the template send (number not on WhatsApp, paused WABA, etc.) — the underlying Meta message is included | Verify the phone is a WhatsApp account on a registered MSISDN |
rate_limited | Hit the 50 messages / 5 recipients per 24h cap | Wait for the rolling window to free up, or use a paid number |
403 from any /sandbox/* endpoint | Inbox addon not enabled on the user's plan | Enable the Inbox addon |