Send messages
Send text
One endpoint sends text to an individual WhatsApp number. Up to 4096 characters per message.
Endpoint
POST
/v1/messages/sendThe same endpoint accepts text and media. For text, send the text field (or content as a legacy alias).
Request
tostring (E.164)required- Recipient number with leading +.
textstringrequired- Up to 4096 characters. Alias: content (for backwards compatibility).
sender{ id?: uuid, label?: string }- Optional. Exactly one of id or label. Overrides rotation or pinning for this request.
curl -X POST https://api.shiftwa.dev/v1/messages/send \
-H "X-API-Key: $SHIFTWA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+6281234567890",
"text": "Halo dari Shiftwa!"
}'await fetch("https://api.shiftwa.dev/v1/messages/send", {
method: "POST",
headers: {
"X-API-Key": process.env.SHIFTWA_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
to: "+6281234567890",
text: "Tagihan untuk pesanan #100 sudah dikirim.",
sender: { label: "alfi" },
}),
});Response (202 Accepted)
{
"messageId": "01HXXXXXXXXXXXXXXXXXXXXXXX",
"status": "queued",
"queueState": "enqueued",
"kind": "text",
"targetKind": "individual",
"sender": {
"numberId": "num-uuid",
"phoneNumber": "+6285138741668",
"label": "alfi",
"source": "hint_label"
},
"quota": {
"used": 1247,
"limit": 10000,
"remaining": 8753,
"resetAt": "2026-06-01T00:00:00.000Z"
}
}Errors you should handle
| HTTP | Code | When it fires |
|---|---|---|
| 409 | PINNED_SENDER_CONFLICT | Key is pinned but the hint asks for a different sender. |
| 409 | IDEMPOTENCY_KEY_IN_FLIGHT | A request with the same Idempotency-Key is still processing. Wait, then retry. |
| 400 | AMBIGUOUS_HINT | Both sender.id and sender.label were set. |
| 404 | SENDER_NOT_FOUND | Hint refers to a number the workspace doesn't own. |
| 429 | QUOTA_EXCEEDED | Monthly plan cap reached. |
| 503 | NO_HEALTHY_NUMBERS | Pool is empty (all disconnected/banned). Retry with backoff. |
| 503 | SENDER_UNHEALTHY | The hinted number is unhealthy right now. |
See the error reference for the full list including body shape.
Idempotency (safe retries)
Pass an optional Idempotency-Key header (any unique string up to 255 chars, e.g. a UUID or your internal order id). If your connection drops and you retry with the same key within 24 hours, Shiftwa replays the original response instead of sending the WhatsApp message twice. Replayed responses carry an Idempotency-Replayed: true header.
curl -X POST https://api.shiftwa.dev/v1/messages/send \
-H "X-API-Key: $SHIFTWA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-10421-notify" \
-d '{
"to": "+6281234567890",
"text": "Pesanan #10421 sudah dikirim."
}'Implementation patterns
- Idempotency: send an Idempotency-Key header on anything you might retry (see above) — duplicates become replays, not double sends.
- Backoff: only retry 503 (infra). 4xx are caller faults — don't retry.
- Your-side queue: if your app already has a job queue, enqueue the send and let the worker retry. Slow deliveries won't tie up your HTTP request thread.