Skip to main content

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 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 for how scopes work.

POST /v1/whatsapp/messages

What does the request look like?

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.

FieldTypeRequiredDescription
agent_idUUIDYesThe agent to send from. Must be authorized for this API key.
fromE.164 stringYesThe WhatsApp business number to send from. Must match an active WhatsApp connection on the agent.
toE.164 stringYesRecipient's phone, e.g. +15551234567.
text.bodystringYesMessage body, 1–4096 characters.

Which optional headers should I send?

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

HeaderPurpose
Idempotency-KeyMake retries safe. See Idempotency.
X-Correlation-IDCustom trace id we'll honor and reflect in the response.

What does a success response look like?

HTTP 202 Accepted:

{
"id": "1fc4912f-efa3-4865-9424-da85f8f318a4",
"status": "queued",
"agent_id": "12345678-1234-1234-1234-123456789012",
"to": "+15551234567",
"type": "text",
"platform_message_id": null,
"created_at": "2026-04-30T08:24:38Z",
"sent_at": null,
"failed_at": null
}

The 202 means the message is queued for delivery. Use the returned id with Get Message Status to track delivery.

What can go wrong?

The standard error envelope is returned with one of the codes below. Branch on code (stable) — never on message (may be reworded). Full catalog on the Errors page.

codeHTTPWhen
invalid_request_body422Required field missing — param names which
invalid_phone_number422to isn't valid E.164
invalid_from_phone422from isn't valid E.164
from_phone_not_found_for_agent422from doesn't match any active WhatsApp connection — available_phones lists what would work
agent_not_authorized403API key isn't allowed to use this agent
insufficient_scope403API key lacks whatsapp:send
rate_limit_exceeded429Per-key budget hit — see 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 to see this happen.

For sends outside the window, use a pre-approved 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?

4096 characters. This 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.

Why does my message say to: "" on status lookup?

Currently the status response doesn't echo the recipient phone. The original to you sent is preserved internally; this is a known gap that will be filled in a future release without breaking the v1 contract.