# Send WhatsApp Message

> Send a free-form WhatsApp text message via POST /v1/whatsapp/messages — request shape, validation rules, and the 24-hour service window restriction.

# Send a WhatsApp Message

Sends a free-form text message to a WhatsApp recipient. Free-form text is restricted to **Meta's 24-hour customer-service window** — the customer must have messaged the agent within the last 24 hours. For sends outside the window, use [Send Template](./send-template) instead.

## Which endpoint do I call?

`POST` to `/v1/whatsapp/messages` over HTTPS. The endpoint requires the `whatsapp:send` scope on your API key — see [Authentication](../authentication) for how scopes work.

```
POST /v1/whatsapp/messages
```

## What does the request look like?

```bash
curl -X POST https://api.mojeeb.app/v1/whatsapp/messages \
  -H "Authorization: Bearer mk_live_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: reply-to-ticket-9001" \
  -d '{
    "agent_id": "12345678-1234-1234-1234-123456789012",
    "from":     "+15557654321",
    "to":       "+15551234567",
    "text":     { "body": "Thanks for reaching out! How can I help?" }
  }'
```

## What goes in the request body?

Four required fields. The agent and `from` together identify which WhatsApp number you're sending from; `to` and `text.body` carry the destination and the message itself.

| Field | Type | Required | Description |
|---|---|---|---|
| `agent_id` | UUID | Yes | The agent to send from. Must be authorized for this API key. |
| `from` | E.164 string | Yes | The WhatsApp business number to send from. Must match an active WhatsApp connection on the agent. |
| `to` | E.164 string | Yes | Recipient's phone, e.g. `+15551234567`. |
| `text.body` | string | Yes | The message text. Its length limit is in the [reference](../api-reference/send-whats-app-message). |

## Which optional headers should I send?

Two are worth knowing about — both improve reliability and traceability without changing what gets sent.

| Header | Purpose |
|---|---|
| `Idempotency-Key` | Make retries safe. See [Idempotency](../idempotency). |
| `X-Correlation-ID` | Custom trace id we'll honor when valid and reflect in the response; see [Errors](../errors) for the rules. |

## What does a success response look like?

`HTTP 202 Accepted` with the message object: its `id`, `status: "queued"`, the `agent_id` and `to` you sent, `type: "text"`, and `created_at`. Every field, with an example, is in the [reference for this endpoint](../api-reference/send-whats-app-message), rendered from the API's own contract so it is never out of date.

The 202 means the message is **queued** for delivery. Use the returned `id` with [Get Message Status](./message-status) to track delivery.

## What can go wrong?

The standard error envelope is returned. Common codes are below; branch on `code` (stable) — never on `message` (may be reworded). The full catalog is on the [Errors](../errors) page.

| `code` | HTTP | When |
|---|---|---|
| `invalid_request_body` | 422 | Required field missing — `param` names which |
| `invalid_phone_number` | 422 | `to` isn't valid E.164 |
| `invalid_from_phone` | 422 | `from` isn't valid E.164 |
| `from_phone_not_found_for_agent` | 422 | `from` doesn't match any active WhatsApp connection — `available_phones` lists what would work |
| `agent_not_authorized` | 403 | API key isn't allowed to use this agent |
| `insufficient_scope` | 403 | API key lacks `whatsapp:send` |
| `rate_limit_exceeded` | 429 | Per-key budget hit — see [Rate Limits](../rate-limits) |

## What about the 24-hour window?

WhatsApp Business rules require free-form text to be sent **inside the 24-hour customer-service window** — the customer must have messaged the agent within the last 24 hours. Outside this window, Meta will reject delivery even though Mojeeb accepts the request and returns a `202`.

If you send free-form outside the window, the immediate response is still `202` (we accept and queue), but the message will transition to `status: "failed"` with a populated `failed_at` once Meta rejects delivery. Poll [Get Message Status](./message-status) to see this happen. A failed send is retried a few times, so wait a couple of minutes before treating `failed` as final; see [Is `failed` final?](./message-status#is-failed-final)

For sends outside the window, use a [pre-approved template](./send-template) instead — templates can be sent at any time.

## Common questions

### Can I send media (images, audio, documents)?

Not in v1 of the public API. Media support is reserved as an additive change. Use the dashboard for media sends in the meantime.

### What's the maximum body length?

The exact limit is in the [reference for this endpoint](../api-reference/send-whats-app-message); it matches WhatsApp's own limit. Longer bodies are rejected with `invalid_request_body`.

### Does the API check whether the recipient is inside the 24h window?

No — Meta enforces the window, not us. The API accepts your request and queues the send; if Meta rejects on delivery, you'll see `status: "failed"` on the message status lookup.

### Can the same number send to itself?

You can't send to your own business number — Meta rejects this at the platform level. The send returns 202 from us but `failed` on status lookup.
