Zernio
Zernio
PlatformsWhatsAppConnection & SetupBroadcastsTemplatesContactsPhone NumbersCallingSandboxGroup ChatsInboxMeta Business AgentFlowsClick-to-WhatsApp AdsPricing & CostsMedia & Limits
Dashboard
llms.txtOpenAPI
OverviewPlatformsAPI ReferenceResources
WhatsApp

Sandbox

Test WhatsApp sending, replies and webhooks against Zernio's shared sandbox number before buying a number of your own.


When you finish this page you have sent a WhatsApp template to your own phone from Zernio's shared sandbox number, received the reply, and can exchange free-form messages with it for 7 days. You need an API key and a phone with WhatsApp. No number purchase, no WABA and no Meta review is involved, and nothing is billed.

PropertyValue
Shared numberOne Zernio-owned WhatsApp number, returned by GET /v1/phone-numbers under sandbox
VerificationReply-based: Zernio sends a template to your test phone, you reply, the session activates
Templates allowedOne, locked: sandbox_start (no variables)
Free-form text and interactive messagesYes, inside the 24-hour customer service window the reply opens
Test phones per user1 (one active session at a time)
Daily caps per user50 messages, 5 distinct recipients (in practice 1)
Pending session TTL24 hours
Activated session TTL7 days
BillingFree: no number charge, no message charge

The sandbox is for testing. To send to more than one recipient, sync templates from WhatsApp Manager or run production traffic, get a number of your own.

The number is shared, so a phone can only be targeted once its owner has proved control of it with a WhatsApp reply.

Step 1: Discover the sandbox

Call GET /v1/phone-numbers. The sandbox number, the accountId you send from and the locked template come back under sandbox.

import Zernio from '@zernio/node';

const zernio = new Zernio();

const { data: numbers } = await zernio.phonenumbers.listPhoneNumbers();

if (!numbers.sandbox) throw new Error('Sandbox not configured on this environment');
const sandbox = numbers.sandbox;

Response (200), trimmed to the sandbox:

{
  "numbers": [],
  "connected": [],
  "sandbox": {
    "phoneNumber": "+12029087457",
    "accountId": "66b2e19d8c3f5a7e9d0b1c2d",
    "template": { "name": "sandbox_start", "language": "en" },
    "isSandbox": true
  }
}

sandbox.accountId is the accountId for every send below.

Step 2: Start activation for your phone

Call POST /v1/whatsapp/sandbox/sessions with phone. This creates a pending session and at once sends the sandbox_start template from the sandbox number to that phone. The phone's owner opens WhatsApp and replies; any text reply, including a tap on the "Reply and verify" button, flips the session to active.

const { data: started } = await zernio.whatsappsandbox.createWhatsAppSandboxSession({
  body: { phone: '+13105551234' }
});

console.log(started.session.id, started.session.status);

Response (200):

{
  "session": {
    "id": "66f7d4e5f6a7b8c9d0e1f2a4",
    "phoneE164": "13105551234",
    "status": "pending",
    "expiresAt": "2027-01-02T09:00:00Z",
    "activatedAt": null,
    "createdAt": "2027-01-01T09:00:00Z"
  },
  "sandboxNumber": "+12029087457"
}

Tell the user to check WhatsApp on that phone and reply. Posting the same phone again is idempotent and resends the template.

Step 3: Wait for the reply

Activation is automatic. Poll GET /v1/whatsapp/sandbox/sessions, or listen for message.received on your webhook endpoint, which fires for the reply itself.

const { data: sessions } = await zernio.whatsappsandbox.listWhatsAppSandboxSessions();

const session = sessions.sessions[0];
if (session?.status === 'active') {
  console.log('Verified at', session.activatedAt);
}

Response (200), once the phone has replied:

{
  "sessions": [
    {
      "id": "66f7d4e5f6a7b8c9d0e1f2a4",
      "phoneE164": "13105551234",
      "status": "active",
      "expiresAt": "2027-01-08T09:02:30Z",
      "activatedAt": "2027-01-01T09:02:30Z",
      "createdAt": "2027-01-01T09:00:00Z"
    }
  ],
  "sandboxNumber": "+12029087457"
}

Step 4: Send to the activated phone

The phone is now a normal WhatsApp conversation in your inbox. Call POST /v1/inbox/conversations with the sandbox accountId, the phone as participantId (digits only, matching phoneE164) and the locked template. A phone with no active session is rejected.

const { data: opened } = await zernio.messages.createInboxConversation({
  body: {
    accountId: sandbox.accountId,
    participantId: '13105551234',
    templateName: sandbox.template.name,
    templateLanguage: sandbox.template.language,
    templateParams: []
  }
});

const conversationId = opened.data.conversationId;

Response (201):

{
  "success": true,
  "data": {
    "messageId": "wamid.HBgLMTMxMDU1NTEyMzQVAgARGBI5QTNEMEY3RjQ4RjE2QjA3QzYA",
    "conversationId": "66c3d2ae7b4f6c8d0e1f2a3b",
    "participantId": "13105551234"
  }
}

Each reply from the phone opens a 24-hour customer service window, inside which POST /v1/inbox/conversations/{conversationId}/messages with accountId and message sends free-form text, attachments and interactive messages exactly as in any other conversation (WhatsApp inbox).

Replace the activated phone

Revoke the current session with DELETE /v1/whatsapp/sandbox/sessions/{sessionId}, then start activation for the new phone:

curl -X DELETE "https://zernio.com/api/v1/whatsapp/sandbox/sessions/66f7d4e5f6a7b8c9d0e1f2a4" \
  -H "Authorization: Bearer $ZERNIO_API_KEY"

curl -X POST "https://zernio.com/api/v1/whatsapp/sandbox/sessions" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone": "+442071234567"}'

Response (200), the revoke:

{
  "success": true
}

Response (200), the new activation, which sends the template to the new phone at once:

{
  "session": {
    "id": "66f7d4e5f6a7b8c9d0e1f2a5",
    "phoneE164": "442071234567",
    "status": "pending",
    "expiresAt": "2027-01-02T09:10:00Z",
    "activatedAt": null,
    "createdAt": "2027-01-01T09:10:00Z"
  },
  "sandboxNumber": "+12029087457"
}

Messages already exchanged with the old phone stay in the inbox; revocation only blocks future sends.

If it fails

A 400 from Step 2 while another phone still has a session names that phone so you can revoke it first:

{
  "error": "You can only test one phone at a time. Revoke +13105551234 first",
  "code": "invalid_field_value"
}

The other 400s on the sandbox endpoints:

ErrorCauseFix
invalid_field_value "This phone is not activated for sandbox sends"The recipient has no active session for the calling userRun Steps 2 and 3 for that phone first
invalid_field_value "Cannot activate the sandbox number itself"phone is the sandbox numberUse a real, different WhatsApp number
invalid_field_value "Could not send the activation message..."Meta rejected the template send (the number is not on WhatsApp, the WABA is paused); Meta's message is includedCheck the phone is a WhatsApp account on a registered number
rate_limitedThe 50 messages or 5 recipients per 24 hours capWait for the rolling window, or use a number of your own
403 from any /sandbox/* endpoint, code: "INBOX_REQUIRED"The team is on legacy billing without inbox accessMove to usage-based billing, which includes the inbox on every account, or start the free 7-day trial the error offers while trialAvailable is true (inbox access)

Related

  • WhatsApp phone numbers: get a number of your own when the sandbox is not enough.
  • WhatsApp inbox: what you can send inside the 24-hour window.
  • Inbox webhooks: message.received for the activation reply and every message after it.
  • Sandbox API: the 3 session endpoints.
Was this page helpful?

Calling

Turn on WhatsApp Business Calling for a connected number, forward calls to a phone, SIP endpoint or AI voice agent, and place outbound calls with the customer's permission.

Group Chats

Create WhatsApp group chats from the API, add and remove participants, share invite links, approve join requests, and message the group from the inbox.

On this page

Step 1: Discover the sandboxStep 2: Start activation for your phoneStep 3: Wait for the replyStep 4: Send to the activated phoneReplace the activated phoneIf it failsRelated