HTTP API
Send WhatsApp messages from your CRM
Use the Send API from CRMs, automations, backend jobs, and support tools. v1 supports direct text, image, and document messages from the connected WhatsApp account.
Authentication
Send a Send API bearer token with every request. Send API tokens are separate from MCP tokens and only grant message-send access. Personal API usage sends from the WhatsApp account attached to the token owner. Owners and admins can also use workspace sender mode by passing a from number that belongs to the same workspace.
Authorization: Bearer YOUR_SEND_API_TOKENEndpoint
POST https://whatan.app/api/v1/messages/sendSender Rules
| Request shape | Who can use it | Sender behavior |
|---|---|---|
| from + to | Workspace owners and admins | Sends from the connected workspace number in from. |
| to only | Owners, admins, and members | Sends from the WhatsApp account attached to that token. |
Workspace Sender Mode
Use this mode for Attio workflows and other workspace-level automations that need to choose the sender at runtime. Include both from and to. The from number must be one of the connected WhatsApp numbers in this workspace. This mode is available to owner/admin tokens.
curl -X POST "https://whatan.app/api/v1/messages/send" \
-H "Authorization: Bearer YOUR_SEND_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: attio-workflow-123" \
-d '{
"from": "+917000000001",
"to": "+917999709798",
"type": "text",
"text": "Following up from Attio."
}'The API normalizes from, finds the matching connected WhatsApp account in the workspace, and queues the send against that account. If the number is not part of the workspace, the request is rejected before sending.
Personal Sender Mode
This is the original Send API behavior and remains supported for backwards compatibility. Omit from; the bearer token determines the sender. Owners and members using this mode send only from their own connected WhatsApp account.
curl -X POST "https://whatan.app/api/v1/messages/send" \
-H "Authorization: Bearer YOUR_SEND_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: invoice-123-reminder" \
-d '{
"to": "+917999709798",
"type": "text",
"text": "Your invoice is ready."
}'Send Text
Text messages support both sender modes. Use from for workspace automation, or omit it for personal-token sending.
curl -X POST "https://whatan.app/api/v1/messages/send" \
-H "Authorization: Bearer YOUR_SEND_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: attio-workflow-123" \
-d '{
"from": "+917000000001",
"to": "+917999709798",
"type": "text",
"text": "Following up from Attio."
}'Send Image
Image URLs must be public HTTPS URLs. Localhost and private network URLs are rejected.
curl -X POST "https://whatan.app/api/v1/messages/send" \
-H "Authorization: Bearer YOUR_SEND_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: promo-image-456" \
-d '{
"to": "+917999709798",
"type": "image",
"image": {
"url": "https://example.com/promo.png",
"caption": "New arrivals are live."
}
}'Send Document
Documents use public HTTPS URLs. Provide a file name and MIME type when your CRM knows them.
curl -X POST "https://whatan.app/api/v1/messages/send" \
-H "Authorization: Bearer YOUR_SEND_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: quote-789" \
-d '{
"to": "+917999709798",
"type": "document",
"document": {
"url": "https://example.com/quote.pdf",
"fileName": "quote.pdf",
"mimetype": "application/pdf",
"caption": "Attached quote for review."
}
}'Node Example
const response = await fetch("https://whatan.app/api/v1/messages/send", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_SEND_API_TOKEN",
"Content-Type": "application/json",
"Idempotency-Key": "attio-record-1001",
},
body: JSON.stringify({
from: "+917000000001",
to: "+917999709798",
type: "text",
text: "Thanks for contacting support. We are checking this now.",
}),
});
const result = await response.json();Idempotency
Include an Idempotency-Key header for every CRM event or automation run. If the CRM retries the same request with the same key, CRM returns the original send result instead of sending a duplicate WhatsApp message.
Rate Limits
v1 queues accepted API sends and paces delivery with a random 2-5 minute delay per connected WhatsApp account. The daily limit is 100 API messages per WhatsApp account. Repeating the same request with the same Idempotency-Key does not count as a new queued send.
The Send API is for transactional CRM messages, support replies, and expected automations. Bulk CSV/XLSX sending belongs in the upcoming Mass Messaging workflow, which will have campaign scheduling, reports, and dedicated safety controls.
Responses
{
"accepted": true,
"sendAttemptId": "f1f7a5a0-2ff4-4ef7-b4bf-1a3b50ad6f53",
"messageId": null,
"remoteJid": null,
"from": "+917000000001",
"to": "+917999709798",
"normalizedPhone": "917999709798",
"status": "queued"
}The Send API queues accepted requests. Use the send attempt ID to poll final status. Personal tokens can read their own send attempts. Owner/admin tokens can also read workspace sender mode attempts created from connected senders in the same workspace.
GET https://whatan.app/api/v1/messages/SEND_ATTEMPT_IDcurl "https://whatan.app/api/v1/messages/SEND_ATTEMPT_ID" \
-H "Authorization: Bearer YOUR_SEND_API_TOKEN"{
"sendAttemptId": "f1f7a5a0-2ff4-4ef7-b4bf-1a3b50ad6f53",
"status": "accepted",
"messageId": "BAE5...",
"remoteJid": "[email protected]",
"errorCode": null,
"errorMessage": null,
"createdAt": "2026-05-14T10:00:00.000Z",
"updatedAt": "2026-05-14T10:00:04.000Z"
}Error Codes
| Code | Meaning |
|---|---|
| invalid_request | Request JSON is missing required fields. |
| invalid_phone_number | Phone number must be international format. |
| whatsapp_not_connected | The connected WhatsApp account is offline. |
| sender_selection_forbidden | The token owner is not an owner or admin. |
| sender_not_allowed | The from number is not a sender in this workspace. |
| recipient_not_found | The phone number is not reachable on WhatsApp. |
| invalid_media_url | Media URL is not a safe public HTTPS URL. |
| idempotency_conflict | The key was reused with a different request. |
| rate_limited | This WhatsApp account hit the v1 send limit. |