# WhatsApp sandbox

Test WhatsApp sends, replies and webhooks from a shared Pozy number before you buy or connect a number of your own. Free.

The sandbox lets you try WhatsApp end to end without a number, a WABA or a Meta review. Pozy runs one shared WhatsApp number for testing. You prove you own a test phone by replying to it once, and for the next 7 days that phone is a normal conversation in your inbox: you can send it a template, chat with it freely and receive every webhook.

It costs nothing. When you are ready for real traffic, [get a number of your own](https://pozy.ai/docs/whatsapp/phone-numbers).

## Sandbox limits

| Property | Value |
| --- | --- |
| Number | One shared Pozy number, returned under `sandbox` by `GET /v1/phone-numbers` |
| Templates | One, locked: `sandbox_start` in `en`, no variables |
| Free-form messages | Yes, inside the 24-hour window each reply opens |
| Test phones | 1 per user at a time |
| Daily cap | 50 messages and 5 distinct recipients per 24 hours |
| Activation link validity | 24 hours while pending |
| Session length | 7 days after activation |
| Price | Free: no number charge and no message charge |

Because the number is shared, a phone can only be messaged after its owner has replied to it from WhatsApp. That stops anyone from using the sandbox to reach strangers.

## Try it

### Find the sandbox number

Call [`GET /v1/phone-numbers`](https://pozy.ai/docs/api/listPhoneNumbers). The `sandbox` object holds the number, the `accountId` you send from and the locked template.

```json
{
  "numbers": [],
  "connected": [],
  "sandbox": {
    "phoneNumber": "+14155550100",
    "accountId": "6716e3b8a4c7f92d5e1b6c08",
    "template": { "name": "sandbox_start", "language": "en" },
    "isSandbox": true
  }
}
```

### Activate your test phone

Call [`POST /v1/whatsapp/sandbox/sessions`](https://pozy.ai/docs/api/createWhatsAppSandboxSession) with the phone in E.164. Pozy creates a `pending` session and immediately sends `sandbox_start` to that phone.

```bash cURL
curl -X POST https://api.pozy.ai/v1/whatsapp/sandbox/sessions \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "phone": "+351912345678" }'
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/whatsapp/sandbox/sessions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ phone: "+351912345678" }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);

console.log(data.session.status, data.sandboxNumber); // "pending"
```
```python Python
import os
import requests

H = {"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"}
resp = requests.post(
    "https://api.pozy.ai/v1/whatsapp/sandbox/sessions",
    headers=H,
    json={"phone": "+351912345678"},
    timeout=30,
)
print(resp.json()["session"]["status"])
```

Posting the same phone again is safe: it resends the template.

### Reply from the phone

Open WhatsApp on the test phone and reply to the message, or tap its button. Any reply flips the session to `active`. Watch for it with a `message.received` [webhook](https://pozy.ai/docs/webhooks), or poll [`GET /v1/whatsapp/sandbox/sessions`](https://pozy.ai/docs/api/listWhatsAppSandboxSessions) until `status` is `active`.

### Send messages

The phone is now an ordinary WhatsApp conversation. Send the locked template with [`POST /v1/inbox/conversations`](https://pozy.ai/docs/api/createInboxConversation), using the sandbox `accountId` and the phone as digits only:

```bash cURL
curl -X POST https://api.pozy.ai/v1/inbox/conversations \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "6716e3b8a4c7f92d5e1b6c08",
    "participantId": "351912345678",
    "templateName": "sandbox_start",
    "templateLanguage": "en",
    "templateParams": []
  }'
```

Each reply from the phone opens a 24-hour window. Inside it, [`POST /v1/inbox/conversations/{conversationId}/messages`](https://pozy.ai/docs/api/sendInboxMessage) sends free-form text, attachments and interactive messages, exactly as with a real number.

## Switch to another test phone

You can test one phone at a time. Revoke the current session with [`DELETE /v1/whatsapp/sandbox/sessions/{sessionId}`](https://pozy.ai/docs/api/deleteWhatsAppSandboxSession), then activate the new phone. Messages already exchanged stay in the inbox; revoking only blocks new sends.

> **Keep sandbox events apart:**
> 
> To send sandbox traffic to its own endpoint, create a [webhook](https://pozy.ai/docs/webhooks) scoped to the sandbox `accountId`. Note that an account-scoped endpoint no longer receives events that belong to no account, such as `whatsapp.number.*`.

## What the sandbox cannot do

- Send to more than one phone at a time, or to anyone who has not replied.
- Send any template other than `sandbox_start`, or use templates from your own WhatsApp Manager.
- Carry production traffic. It exists to test your integration, not to message customers.

## Common errors

| Error | Cause | Fix |
| --- | --- | --- |
| `400` "You can only test one phone at a time" | Another phone has an active or pending session. The message names it. | Revoke that session first |
| `400` "This phone is not activated for sandbox sends" | The recipient has not replied yet | Finish activation for that phone |
| `400` "Cannot activate the sandbox number itself" | You passed the sandbox number as `phone` | Use your own WhatsApp number |
| `400` "Could not send the activation message" | Meta refused the send, usually because the phone is not on WhatsApp. Meta's message is included. | Check the number |
| `rate_limited` | 50 messages or 5 recipients in 24 hours | Wait for the rolling window, or use your own number |

## Endpoints

| Endpoint | What it does |
| --- | --- |
| [`GET /v1/phone-numbers`](https://pozy.ai/docs/api/listPhoneNumbers) | List phone numbers |
| [`GET /v1/whatsapp/sandbox/sessions`](https://pozy.ai/docs/api/listWhatsAppSandboxSessions) | List your sandbox sessions |
| [`POST /v1/whatsapp/sandbox/sessions`](https://pozy.ai/docs/api/createWhatsAppSandboxSession) | Start a sandbox activation |
| [`DELETE /v1/whatsapp/sandbox/sessions/{sessionId}`](https://pozy.ai/docs/api/deleteWhatsAppSandboxSession) | Revoke a sandbox session |
| [`POST /v1/inbox/conversations`](https://pozy.ai/docs/api/createInboxConversation) | Create conversation |
| [`POST /v1/inbox/conversations/{conversationId}/messages`](https://pozy.ai/docs/api/sendInboxMessage) | Send message |

## Webhook events

| Event | When it fires |
| --- | --- |
| [`message.received`](https://pozy.ai/docs/webhooks/events#message-received) | Message received. |
| [`message.sent`](https://pozy.ai/docs/webhooks/events#message-sent) | Message sent. |
| [`message.delivered`](https://pozy.ai/docs/webhooks/events#message-delivered) | Message delivered. |
| [`message.read`](https://pozy.ai/docs/webhooks/events#message-read) | Message read. |
