What do I need first?
- A session with status
ready. See Sessions and statuses. - An API key (
tkw_live_...) sent in theX-API-Keyheader. See Authentication. - An active WhatsApp plan or trial. Expired or missing plans cannot send.
How do I address the recipient?
Use a JID in the chatId field.
- Person
<phone>@c.uswith digits only: country code first, no+, no spaces or dashes. For example[email protected].- Group
<id>@g.us, from the groups endpoints.
As a convenience, a bare phone number in common formats (+254 700 000 001, 0700000001, 00254700000001) is also accepted and normalised to <digits>@c.us. A leading 0 is read as a Kenyan number, so prefer the full international form for anything else. See Chat ids.
How do I send a text message?
Post to send-text under the session. The base URL is the one on the introduction, and text may be 1 to 4096 characters.
curl -X POST "https://api.tuskmail.africa/v1/whatsapp/sessions/$SESSION_ID/messages/send-text" \
-H "X-API-Key: $TUSK_WA_KEY" \
-H "Content-Type: application/json" \
-d '{
"chatId": "[email protected]",
"text": "Hi Amina, your order 1001 is ready for pickup."
}'{
"message_id": "3EB0A1B2C3D4E5F60789",
"chat_id": "[email protected]",
"timestamp": 1768469400
}Keep the message_id. You need it to reply to, react to, edit or delete the message, and to match later message.ack and message.failed webhook events. timestamp is Unix seconds and can be null.
Which other message types can I send?
All of these live under /sessions/{sessionId}/messages/ and return the same 201 shape. Full parameters are in the send messages reference.
| Endpoint | Body |
|---|---|
send-text | chatId, text, linkPreview?, quotedMessageId? |
send-image, send-video, send-document | chatId, url or base64 (exactly one), mimetype (required with base64), filename?, caption? (up to 1024), quotedMessageId? |
send-audio | Same as media, plus ptt (true sends a voice note) |
send-location | chatId, latitude, longitude, description?, address? |
send-contact | chatId, contactName, contactNumber |
send-poll | chatId, name, options (2 to 12 strings), allowMultipleAnswers? |
react | chatId, messageId, emoji (empty string removes the reaction) |
A media url must be a public http(s) address: localhost, private network addresses and URLs with credentials are rejected.
curl -X POST "https://api.tuskmail.africa/v1/whatsapp/sessions/$SESSION_ID/messages/send-image" \
-H "X-API-Key: $TUSK_WA_KEY" \
-H "Content-Type: application/json" \
-d '{
"chatId": "[email protected]",
"url": "https://example.com/receipt.png",
"caption": "Your receipt"
}'How do I send to many people?
Use send-bulk instead of looping over send-text. It accepts up to 100 messages, returns 202 Accepted straight away, and sends them one at a time with a pause between each. Set options.delayBetweenMessages, randomizeDelay and stopOnError, then poll the batch for progress.
curl -X POST "https://api.tuskmail.africa/v1/whatsapp/sessions/$SESSION_ID/messages/send-bulk" \
-H "X-API-Key: $TUSK_WA_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"chatId": "[email protected]",
"type": "text",
"content": {
"text": "Hi Amina, your order 1001 is ready."
}
},
{
"chatId": "[email protected]",
"type": "text",
"content": {
"text": "Hi Brian, order 1002 is ready."
}
}
],
"options": {
"delayBetweenMessages": 5000,
"randomizeDelay": true,
"stopOnError": true
}
}'How do I check a number first?
Call the contact check before the first message to a new number. It tells you whether the number is on WhatsApp and gives you the chat id to use. Checks are limited to 30 per minute per session.
curl "https://api.tuskmail.africa/v1/whatsapp/sessions/$SESSION_ID/contacts/check/254700000001" \
-H "X-API-Key: $TUSK_WA_KEY"{
"number": "254700000001",
"exists": true,
"chat_id": "[email protected]"
}When exists is false, chat_id is null. Do not send to it, and remove it from your list.
Which errors can sending return?
Errors use the usual { "error": { "code", "message", "request_id" } } envelope.
| HTTP | code | What to do |
|---|---|---|
| 402 | subscription_required | No WhatsApp plan yet. Pick a plan in the web app. |
| 402 | subscription_expired | The plan or trial ended. Renew to send. The API can also answer payment_required at authentication. |
| 409 | session_not_ready | The session is not connected. Check its status, relink or start it, then retry. |
| 429 | rate_limited | Over 60 messages per minute for this session. Wait and retry, and slow down well below it. |
| 422 | invalid_phone | The number could not be read. Use digits with the country code. |
| 422 | invalid_chat_id | The chat id is malformed. |
| 422 | validation_error | A field is missing or invalid, for example text over 4096 characters or fewer than 2 poll options. |
| 400 | bad_request | WhatsApp rejected the request. |
| 404 | not_found | The session does not exist or is not yours. |
| 413 | payload_too_large | The body or media is too large. Send a url instead of base64. |
| 502 | gateway_unavailable | Temporary problem on our side. Retry with backoff, and check history before resending. |
The limit is 60 messages per minute per session. That is a ceiling on our side, not a safe sending rate for WhatsApp.