> For the complete documentation index, see [llms.txt](https://docs.360dialog.com/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.360dialog.com/docs/get-started/sandbox.md).

# Sandbox

Learn how to get started with 360Dialog Sandbox API

We provide a developer-friendly test environment for anyone who wants to explore the usage of our WhatsApp API. You can test sending messages and templates in the test environment.&#x20;

**Sandbox Base Path:** `https://waba-sandbox.360dialog.io/`

Each test account has its own API key and one fixed recipient, either your phone number or your business-scoped user ID. You can only send test messages to that recipient. If you want to message more than one number, set up a WhatsApp Business Account instead of using the sandbox.

## Limitations

### **What you can do with the sandbox**&#x20;

* You can use the sandbox API key to send freeform messages and templates to your own phone number (or your own business-scoped user ID if your phone number was not shared).
* A maximum of 200 messages can be sent with the sandbox. Every request that reaches the messaging API counts towards this limit, even if the message itself is rejected. Once the limit is reached, requests fail with HTTP 429.
* Your phone number acts like a user and can send and receive messages.
* The number can be a landline number, as long it is able to use WhatsApp.
* You can change your webhook URL at any time.
* Each Sandbox API key is linked to one fixed recipient (either your phone number or your business-scoped user ID) and you can only send test messages to that recipient. If you wish to send messages to more than just your recipient, then we recommend setting up a WhatsApp Business Account to use instead of the sandbox.

### **What you can't do with the sandbox**

* The Sandbox is a test environment only. Messages and templates can be sent only to your own phone number, or to your own business-scoped user ID if your phone number was not shared.
* You can send only predefined templates available in the sandbox: `disclaimer`, `first_welcome_messsage`, and `interactive_template_sandbox`. *(Note: Template inventory requires confirmation with the Partner APIs team).*
* Uploading and retrieving media files using the media ID.

## How to get started with sandbox

{% stepper %}
{% step %}

### **Get an API Key**

You can get an API key by sending a message on WhatsApp to phone number `+551146733492` with the content **`START`** (**`START`** must be in all UPPERCASE). You can quickly send the message by:

* Clicking this link: <https://wa.me/551146733492?text=START>
* Or scanning this QR code on your phone:

<figure><img src="https://3527970750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M4sMxKjL6eJRvZn6jeG-887967055%2Fuploads%2Fd0O76h54j7uPnvGAtfol%2Fimage.png?alt=media&#x26;token=3fe7d501-8e88-47f6-8202-d1e23ba0036b" alt="" width="303"><figcaption></figcaption></figure>

You will later receive a response containing your API key. This API key needs to be used when making API requests.

### If your phone number is not shared

If your phone number is not shared when sending the **START** message, WhatsApp will trigger a contact-request message template (`request_contact_info_en`) on your device prompting you to share your contact info.

If your phone number is not shared or is delayed, you will receive an API key after a timeout (within 5 minutes). The exact message you receive will be:

> `Your new api-key is: "<API_KEY>". Your business-scoped user id is: "<BSUID>". Because your phone number was not shared, send messages using the "recipient" field set to your business-scoped user id instead of "to".`

> **Business-scoped user ID (BSUID)**: an identifier WhatsApp assigns to a user for one specific business. WhatsApp sends it instead of the phone number when the user's number is not shared with the business.
>
> Accounts created in this manner are identified by BSUID only, do not have a registered phone number associated, and must use the `recipient` field instead of `to` when sending API requests.
> {% endstep %}

{% step %}

### **Set the Sandbox Webhook URL**

<mark style="color:green;">`POST`</mark> `https://waba-sandbox.360dialog.io/v1/configs/webhook`

Setting a webhook URL is necessary for receiving messages (and message status updates). Any message you send to `+551146733492` will be forwarded to the webhook URL you set.

You can use an external service (such as [requestbin.com](https://pipedream.com/requestbin) or [webhook.site](https://webhook.site/)) to create a temporary webhook URL. You can also use an external service such as ngrok to tunnel a port from localhost to a temporary public URL.

{% hint style="warning" %}
**Important: HTTPS is mandatory.** A non-HTTPS URL will be rejected with HTTP 400: `"url must be HTTPS"`. When picking a URL from webhook.site, requestbin, or ngrok, you must select the secure `https://` variant.
{% endhint %}

The API key you received in the previous step needs to be passed in the **D360-API-KEY** header.

**Request Example**

```shellscript
curl --request POST \
  --url https://waba-sandbox.360dialog.io/v1/configs/webhook \
  --header 'Content-Type: application/json' \
  --header 'D360-API-KEY: YOUR_API_KEY' \
  --data '{"url": "https://your-webhook-adress"}'
```

**Headers**

| Name           | Value              |
| -------------- | ------------------ |
| `Content-Type` | `application/json` |
| `D360-API-KEY` | Your API key       |

**Request Body**

| Name  | Type   | Value            |
| ----- | ------ | ---------------- |
| `url` | string | Your webhook URL |

{% tabs %}
{% tab title="200 Webhook URL Set" %}

```json
{
    "url": "https://example-webhook-url"
}
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### **Send a Message to the Sandbox**

Send a message to the number `+551146733492` again. You will receive an Inbound Message event on your webhook. This webhook event contains the phone number that sent the message, along with the message's contents. An example of it can be seen below.

The webhook is not just used for receiving messages; it is also used for receiving status updates on your outbound messages. After you send a message via the API, you will receive a webhook event when the message is sent/delivered to your recipient, and when your recipient has read the message.
{% endstep %}

{% step %}

### Inbound Message Event

{% tabs %}
{% tab title="Phone Number Account" %}

```json
{
  "entry": [
    {
      "changes": [
        {
          "field": "messages",
          "value": {
            "contacts": [
              {
                "profile": {
                  "name": "USER_NAME"
                },
                "wa_id": "PHONE_NUMBER"
              }
            ],
            "messages": [
              {
                "from": "PHONE_NUMBER",
                "id": "wamid.ID",
                "timestamp": "1591955533",
                "type": "text",
                "text": {
                  "body": "MESSAGE_BODY"
                }
              }
            ]
          }
        }
      ]
    }
  ]
}
```

{% endtab %}

{% tab title="Business-scoped User ID (BSUID) Account" %}

```json
{
  "entry": [
    {
      "changes": [
        {
          "field": "messages",
          "value": {
            "contacts": [
              {
                "profile": {
                  "name": "USER_NAME"
                },
                "user_id": "YOUR_BUSINESS_SCOPED_USER_ID"
              }
            ],
            "messages": [
              {
                "from_user_id": "YOUR_BUSINESS_SCOPED_USER_ID",
                "id": "wamid.ID",
                "timestamp": "1591955533",
                "type": "text",
                "text": {
                  "body": "MESSAGE_BODY"
                }
              }
            ]
          }
        }
      ]
    }
  ]
}
```

{% endtab %}
{% endtabs %}

### Message Status Event

{% tabs %}
{% tab title="Phone Number Account" %}

```json
{
  "entry": [
    {
      "changes": [
        {
          "field": "messages",
          "value": {
            "statuses": [
              {
                "id": "wamid.ID",
                "recipient_id": "PHONE_NUMBER",
                "status": "read",
                "timestamp": "1591955668"
              }
            ]
          }
        }
      ]
    }
  ]
}
```

{% endtab %}

{% tab title="Business-scoped User ID (BSUID) Account" %}

```json
{
  "entry": [
    {
      "changes": [
        {
          "field": "messages",
          "value": {
            "statuses": [
              {
                "id": "wamid.ID",
                "recipient_user_id": "YOUR_BUSINESS_SCOPED_USER_ID",
                "status": "read",
                "timestamp": "1591955668"
              }
            ]
          }
        }
      ]
    }
  ]
}
```

{% endtab %}
{% endtabs %}

If your phone number is not shared, webhook events identify you by your business-scoped user ID instead of your phone number:

* Inbound messages carry `from_user_id` (and `contacts[].user_id`) instead of `from` / `wa_id`.
* Status events carry `recipient_user_id` instead of `recipient_id`.
*

{% endstep %}

{% step %}

### **Respond using the Sandbox API**

**Send response message**

<mark style="color:green;">`POST`</mark> `https://waba-sandbox.360dialog.io/v1/messages`

You can send a response message (or a message template) to the phone WhatsApp number with the above request URL and the body parameters as described below.

**Request Example**

```shellscript
curl --request POST \
  --url https://waba-sandbox.360dialog.io/v1/messages \
  --header 'Content-Type: application/json' \
  --header 'D360-API-KEY: YOUR_API_KEY' \
  --data '{"messaging_product": "whatsapp", "to":"YOUR_PHONE_NUMBER","type":"text","text":{"body":"Hello world"}}'
```

**Headers**

| Name           | Value              |
| -------------- | ------------------ |
| `Content-Type` | `application/json` |
| `D360-API-KEY` | Your API key       |

**Request Body**

<table><thead><tr><th width="185">Name</th><th width="199.800048828125">Description</th><th>Key</th></tr></thead><tbody><tr><td><code>recipient_type</code></td><td>string</td><td>Set to <code>individual</code></td></tr><tr><td><code>to</code></td><td>number</td><td>Set to your phone number</td></tr><tr><td><code>type</code></td><td>string</td><td>Set to <code>text</code></td></tr><tr><td><code>text</code></td><td>object</td><td>Contains a <code>body</code> string field, which is where your message should go (e.g. <code>"body": "Hello world"</code>)</td></tr><tr><td><code>messaging_product</code></td><td>string</td><td>Mandatory payload that should be set to <code>whatsapp</code></td></tr></tbody></table>

{% hint style="warning" %}
**Important Configuration Note:**

Send either `to` or `recipient`, matching how your test account was created. `to` must be exactly the phone number your API key is tied to, and `recipient` must be exactly the business-scoped user ID your API key is tied to. Any other value is rejected with HTTP 403.
{% endhint %}

#### Example Request Payload <a href="#example-request-payload" id="example-request-payload"></a>

{% tabs %}
{% tab title="Example Request Payload  " %}

```json
{
    "messaging_product": "whatsapp",
    "recipient_type": "individual",
    "to": "<YOUR_PHONE_NUMBER>",
    "type": "text",
    "text": {
        "body": "Hello world"
    }
}
```

{% endtab %}

{% tab title="Business-scoped User ID (BSUID) Payload" %}

```json
{
    "messaging_product": "whatsapp",
    "recipient": "<YOUR_BUSINESS_SCOPED_USER_ID>",
    "type": "text",
    "text": {
        "body": "Hello world"
    }
```

{% endtab %}
{% endtabs %}

#### Response (201 Created) <a href="#response-201-created" id="response-201-created"></a>

```
{
    "messages": [
        {
            "id": "message_id"
        }
    ],
    "meta": {
        "api_status": "stable",
        "version": "2.31.5"
    }
}
```

#### Error Reference <a href="#error-reference" id="error-reference"></a>

| Status        | Meaning                                                                                                                    |
| ------------- | -------------------------------------------------------------------------------------------------------------------------- |
| **401**       | Missing or invalid `D360-API-KEY` header.                                                                                  |
| **403**       | The `to` or `recipient` value does not match the recipient your API key is tied to, or the request contains neither field. |
| **429**       | You have used up the 200 message limit of the sandbox.                                                                     |
| **502**       | The message could not be delivered to the messaging API. Try again later.                                                  |
| **504**       | Timeout while delivering the message. Try again later.                                                                     |
| {% endstep %} |                                                                                                                            |

{% step %}

### **Send a Template Message (optional)**

<mark style="color:green;">`POST`</mark> `https://waba-sandbox.360dialog.io/v1/messages`

There are 3 templates available to test in the sandbox. There is no possibility to add or edit templates; you must use one of the predefined templates listed below.\
\
**Available templates are:**

* `disclaimer`\
  Using this template will just send a regular text message, which can’t be edited.
* `first_welcome_messsage`\
  This is an example of a template with a personalization possibility (you can personalize the receiver's name).
* `interactive_template_sandbox`\
  This template contains 2 buttons for which you can pass customer URLs.

**Request Example**

```bash
curl --request POST \
  --url https://waba-sandbox.360dialog.io/v1/messages \
  --header 'Content-Type: application/json' \
  --header 'D360-API-KEY: YOUR_API_KEY' \
  --data '{"to": "YOUR_PHONE_NUMBER", "messaging_product": "whatsapp", "type": "template", "template": { "name": "first_welcome_messsage", "language": { "code": "en" }, "components": [ { "type": "body", "parameters": [ { "type": "text", "text": "Placeholder 1"}'
```

**Headers**

| Name           | Value              |
| -------------- | ------------------ |
| `Content-Type` | `application/json` |
| `D360-API-KEY` | Your API key       |

**Request Body**

If the request is valid, you will receive an HTTP 201 success response.

{% tabs %}
{% tab title="201: Created " %}

```json
{
    "messages": [
        {
            "id": "message_id"
        }
    ],
    "meta": {
        "api_status": "stable",
        "version": "2.35.4"
    }
}
```

{% endtab %}
{% endtabs %}

**Example Request Payload**

**`first_welcome_messsage`**

```json
{
  "to": "<YOUR_PHONE_NUMBER>",
  "messaging_product": "whatsapp",
  "type": "template",
  "template": {
    "name": "first_welcome_messsage",
    "language": {
      "code": "en"
    },
    "components": [
      {
        "type": "body",
        "parameters": [
          {
            "type": "text",
            "text": "Placeholder 1"
          }
        ]
      }
    ]
  }
}
```

Note that this example covers `first_welcome_messsage` with personalization. In the case of the `disclaimer` template, the payload is similar but with an empty `components` array (it holds personalization parameters which are not used in `disclaimer`).

If you would like to quickly give the other two templates a try, you can use the JSON body snippets below:

{% tabs %}
{% tab title="'disclaimer' template JSON body" %}

```json
{
  "to": "<YOUR_PHONE_NUMBER>",
  "messaging_product": "whatsapp",
  "type": "template",
  "template": {
    "name": "disclaimer",
    "language": {
      "code": "en"
    },
    "components": []
  }
}
```

{% endtab %}

{% tab title="'interactive\_template\_sandbox' template JSON body" %}

```json
{
  "to": "<YOUR_PHONE_NUMBER>",
  "messaging_product": "whatsapp",
  "type": "template",
  "template": {
    "name": "interactive_template_sandbox",
    "language": {
      "code": "en"
    },
    "components": [
      {
        "type": "button",
        "sub_type": "quick_reply",
        "index": 0,
        "parameters": [
          {
            "type": "payload",
            "payload": "aGlzIHRoaXMgaXMgY29vZHNhc2phZHdpcXdlMGZoIGFTIEZISUQgV1FEV0RT"
          }
        ]
      },
      {
        "type": "button",
        "sub_type": "quick_reply",
        "index": 1,
        "parameters": [
          {
            "type": "payload",
            "payload": "aGlzIHRoaXMgaXMgY29vZHNhc2phZHdpcXdlMGZoIGFTIEZISUQgV1FEV0RT"
          }
        ]
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}
{% endstep %}
{% endstepper %}

{% hint style="info" %}
**Note for Business-Scoped User ID (BSUID) Accounts:**

If your test account is identified by a business-scoped user ID, replace `"to": "<YOUR_PHONE_NUMBER>"` with `"recipient": "<YOUR_BUSINESS_SCOPED_USER_ID>"` in every template payload and API example shown on this page.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.360dialog.com/docs/get-started/sandbox.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
