> ## Documentation Index
> Fetch the complete documentation index at: https://docs.60db.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Send WhatsApp message

> Queue a workspace-scoped message with idempotent retries

Requires a connected workspace number, an active paid platform plan and `whatsapp` API key scope. Owner, admin, developer and member roles may send.

## Request

**Authorization** `string` *required*

`Bearer` followed by your 60db API key.

**Idempotency-Key** `string` *required*

Unique message key, 8–128 characters using letters, digits, dots, colons, underscores or hyphens. The `broadcast:` prefix is reserved for internal campaign messages. Keep the same key and identical payload when retrying an uncertain API response. Reusing a key with a different payload returns `409 IDEMPOTENCY_CONFLICT`.

**number\_id** `string` *required*

The 60db UUID from `GET /whatsapp/numbers`, rather than the provider's phone number ID.

**to** `string` *required*

Recipient's phone number including country code, 7–15 digits without `+`, spaces or punctuation.

**type** `string` *required*

`text`, `template`, `image`, `video`, `audio`, `document` or `interactive`. Supply exactly the matching content object.

**text** `object`

For text messages: `{ "body": "Hello", "preview_url": false }`. Body length is 1–4,096 characters.

**template** `object`

For approved templates: name, `language.code` and optional Meta-format components. Sync the template to 60db first.

**context** `object`

Optional `{ "message_id": "wamid..." }` to reply to a provider message.

### Text reply

Replace the example UUID and phone number with your connected number and consenting recipient. Text requires an inbound message within the preceding 24 hours.

```bash theme={null}
curl --request POST 'https://api.60db.ai/whatsapp/messages' \
  --header "Authorization: Bearer $SIXTYDB_API_KEY" \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: order-123-reply-1' \
  --data '{
    "number_id": "11111111-1111-4111-8111-111111111111",
    "to": "919000000001",
    "type": "text",
    "text": {"body": "Your order is ready."}
  }'
```

### Approved template

```json theme={null}
{
  "number_id": "11111111-1111-4111-8111-111111111111",
  "to": "919000000001",
  "type": "template",
  "template": {
    "name": "order_update",
    "language": {"code": "en_US"},
    "components": [{
      "type": "body",
      "parameters": [{"type": "text", "text": "ORDER-123"}]
    }]
  }
}
```

### Other message types

First [upload media](https://docs.60db.ai/api-reference/whatsapp/media.md) to the sending number, then use its returned `provider_id`, for example `"image": {"id": "123456789", "caption": "Order photo"}`. Media IDs must belong to the same number and workspace and remain unexpired. This also applies to template and interactive media headers. Arbitrary media URLs are not accepted. Video has the same fields; audio accepts an ID and optional `voice: true` for mono Ogg/Opus voice notes; documents can also include a filename. Interactive messages accept `button` or `list`, a body, action and optional header/footer in Meta format.

## Response and delivery

`202 Accepted` means the message is durably queued, not delivered. A replay may return `200 OK` with the existing message once it has left the queue.

```json theme={null}
{
  "success": true,
  "data": {
    "id": "22222222-2222-4222-8222-222222222222",
    "number_id": "11111111-1111-4111-8111-111111111111",
    "direction": "outbound",
    "type": "text",
    "status": "queued"
  }
}
```

Poll `GET /whatsapp/messages/{id}` or subscribe to [message status events](https://docs.60db.ai/api-reference/whatsapp/webhook-events.md).

| Status | Meaning |
| - | - |
| `queued` | Stored, awaiting dispatch |
| `sending` | Dispatch in progress |
| `sent` | Provider accepted the message |
| `delivered` | Delivery confirmed |
| `read` | Read confirmed |
| `failed` | Dispatch or delivery failed |
| `unknown` | Acceptance could not be confirmed; not automatically resent |

## Errors

| HTTP | Code | Recovery |
| - | - | - |
| 400 | `IDEMPOTENCY_KEY_REQUIRED` / `VALIDATION_ERROR` | Correct the request |
| 403 | `POLICY_DENY` | Check membership, plan and WhatsApp API key scope |
| 403 | `CONTACT_OPTED_OUT` | Respect the customer's opt-out |
| 409 | `NUMBER_DISCONNECTED` | Reconnect the number |
| 409 | `TEMPLATE_REQUIRED` | Send an approved template outside the reply window |
| 409 | `TEMPLATE_NOT_APPROVED` | Sync and select an approved template |
| 409 | `OPT_IN_REQUIRED` | Record explicit consent before initiating the conversation |
| 409 | `IDEMPOTENCY_CONFLICT` | Use a new key for a different message |
| 404 | `MEDIA_NOT_FOUND` | Upload the attachment to this number and workspace |
| 410 | `MEDIA_EXPIRED` | Upload the file again and use the new ID |
| 503 | Authorization unavailable | Retry the same request and idempotency key later |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.