Sandbox Mode
Sandbox mode simulates a mutation request without executing it. When the request body of a supported endpoint contains "sandbox": true, the API authenticates and validates the request as usual, then returns a simulated response with sample data instead of performing the operation.
For test-suite, CI, and debugging workflows built on sandbox mode, see Testing with sandbox mode.
Request Field
| Field | Type | Location | Default |
|---|---|---|---|
sandbox | boolean | JSON request body | false |
sandbox must be the JSON boolean true; the string "true" is not a valid value. The API reads the field from the request body of the endpoints listed under Supported Endpoints, never from headers or query parameters.
How It Works
For a request with "sandbox": true:
- Authentication runs. An invalid or missing API key is rejected with
401. - Request validation runs. A malformed payload returns the same
400errors as a live request. - Execution is skipped. The database, the send queue, downstream provider APIs, and your balance are all left untouched.
- A simulated response is returned. It uses the same status code, envelope, and schema as a live response, populated with sample data. Identifiers such as
message_idare newly generated and do not reference stored resources.
Sandbox mode does not look up stored resources. A sandbox send referencing a nonexistent template ID still returns 202; only request-level validation is applied.
Response Headers
| Header | Value |
|---|---|
X-Sandbox | true when the sandbox field was detected. Present on success responses and on 400 validation errors. |
Example
POST /v3/messages
Content-Type: application/json
x-api-key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
{
"sandbox": true,
"to": ["+14155550123"],
"channel": ["sms"],
"template": {
"id": "7ba7b820-9dad-11d1-80b4-00c04fd430c8",
"parameters": {
"name": "Test User"
}
}
}Response:
HTTP/1.1 202 Accepted
X-Sandbox: true
X-Request-Id: req_5f2c1b3a9d8e4f67
Content-Type: application/json
{
"success": true,
"data": {
"status": "QUEUED",
"template_id": "7ba7b820-9dad-11d1-80b4-00c04fd430c8",
"template_name": "",
"recipients": [
{
"message_id": "8ba7b830-9dad-11d1-80b4-00c04fd430c8",
"to": "+14155550123",
"channel": "sms",
"body": null
}
]
},
"meta": {
"request_id": "req_5f2c1b3a9d8e4f67",
"timestamp": "2026-07-25T10:30:00Z",
"version": "v3"
}
}In the simulated response, template_name is empty and body is null because the template is referenced by id and sandbox mode performs no template lookup. message_id is generated per recipient and does not correspond to a stored message.
Supported Endpoints
| Endpoint | Sandbox response |
|---|---|
POST /v3/messages | 202 with "status": "QUEUED" and one simulated recipient per to entry per channel. Nothing is queued or sent. |
POST /v3/contacts | 201 with a simulated contact echoing phone_number. No contact is created. |
PATCH /v3/contacts/{id} | 200 with a simulated updated contact. |
DELETE /v3/contacts/{id} | 204. The contact is not deleted. |
POST /v3/templates | 201 with a simulated template in DRAFT status. |
PUT /v3/templates/{id} | 200 with a simulated updated template. |
DELETE /v3/templates/{id} | 204. The template is not deleted. |
POST /v3/webhooks | 201 with a simulated webhook and a placeholder signing secret. |
PUT /v3/webhooks/{id} | 200 with a simulated updated webhook. |
PATCH /v3/webhooks/{id}/toggle-status | 200 with a simulated webhook reflecting the requested is_active. |
POST /v3/webhooks/{id}/rotate-secret | 200 with a placeholder signing_secret. The real secret is unchanged. |
POST /v3/webhooks/{id}/test | 200 with a simulated delivery result. No test event is delivered. |
POST /v3/profiles | 201 with a simulated profile. |
PATCH /v3/profiles/{profileId} | 200 with a simulated updated profile. |
DELETE /v3/profiles/{profileId} | 204. The profile is not deleted. |
POST /v3/profiles/{profileId}/complete | 202. No completion is started. |
POST /v3/profiles/{profileId}/campaigns | 201 with a simulated campaign. Nothing is submitted to TCR. |
PUT /v3/profiles/{profileId}/campaigns/{campaignId} | 200 with a simulated updated campaign. |
DELETE /v3/profiles/{profileId}/campaigns/{campaignId} | 204. The campaign is not deleted. |
POST /v3/users | 201 with a simulated invited user. No invitation is sent. |
PATCH /v3/users/{userId} | 200 with a simulated updated user. |
DELETE /v3/users/{userId} | 204. The user is not removed. |
GET endpoints do not accept the sandbox field. DELETE /v3/webhooks/{id} does not support sandbox mode: a request to it always deletes the webhook, even if the body contains "sandbox": true.
Sandbox Mode vs Idempotency
| Feature | Purpose | Side effects | Response data |
|---|---|---|---|
| Sandbox mode | Simulate a request | None | Sample data |
| Idempotency | Execute a request at most once | On the first request only | Real data, cached for replays |
Refer to Idempotency for key format, expiry, and replay headers.
Troubleshooting
| Symptom | Cause | Check |
|---|---|---|
Response has no X-Sandbox header and the operation executed | The sandbox field was not detected | Send sandbox as the JSON boolean true, not the string "true", in the request body of a supported endpoint. Headers and query parameters are not read. |
401 | Invalid or missing API key | Sandbox mode does not bypass authentication. The x-api-key header must contain a valid key. |
400 with X-Sandbox: true | The payload failed validation | Sandbox requests return the same validation errors as live requests. Correct the fields listed in the error details. |
Idempotency
Idempotency-Key contract for the Sent API v3: supported methods, key format, response caching, replay headers, and concurrency behavior.
Data Models
Reference for the Sent API v3 response envelope, ApiError and ApiMeta objects, and pagination metadata, plus links to per-resource request and response schemas.