Guides

Send messages from a session

Once a session is ready, one authenticated POST sends a message from that number. This guide covers addressing, the response, the other message types, bulk sends and the errors you can expect.

Tusk WhatsApp API: Send messages from a session

What do I need first?

  • A session with status ready. See Sessions and statuses.
  • An API key (tkw_live_...) sent in the X-API-Key header. 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.us with 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."
}'
201 Created
{
  "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.

EndpointBody
send-textchatId, text, linkPreview?, quotedMessageId?
send-image, send-video, send-documentchatId, url or base64 (exactly one), mimetype (required with base64), filename?, caption? (up to 1024), quotedMessageId?
send-audioSame as media, plus ptt (true sends a voice note)
send-locationchatId, latitude, longitude, description?, address?
send-contactchatId, contactName, contactNumber
send-pollchatId, name, options (2 to 12 strings), allowMultipleAnswers?
reactchatId, 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"
200 OK
{
  "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.

HTTPcodeWhat to do
402subscription_requiredNo WhatsApp plan yet. Pick a plan in the web app.
402subscription_expiredThe plan or trial ended. Renew to send. The API can also answer payment_required at authentication.
409session_not_readyThe session is not connected. Check its status, relink or start it, then retry.
429rate_limitedOver 60 messages per minute for this session. Wait and retry, and slow down well below it.
422invalid_phoneThe number could not be read. Use digits with the country code.
422invalid_chat_idThe chat id is malformed.
422validation_errorA field is missing or invalid, for example text over 4096 characters or fewer than 2 poll options.
400bad_requestWhatsApp rejected the request.
404not_foundThe session does not exist or is not yours.
413payload_too_largeThe body or media is too large. Send a url instead of base64.
502gateway_unavailableTemporary 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.