Send a WhatsApp message to a lead
const url = 'https://app.whatsetter.com/api/v1/messages';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"lead_id":"example","text":"example","humanize":true}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://app.whatsetter.com/api/v1/messages \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "lead_id": "example", "text": "example", "humanize": true }'Sends a text message to a lead through WhatSetter’s anti-ban engine. Two rules are enforced server-side and cannot be bypassed:
- No cold outreach: the lead must already have a conversation.
First contact goes through
POST /lists/{id}/leads+ a campaign (paced, verified sending). Otherwise you get409 lead_not_contacted. - Daily quota: the send consumes the same daily budget as the
campaign engine for that WhatsApp number. When the number’s safe
daily volume is reached you get
429 quota_exceeded(reset at midnight UTC). Check theX-Quota-Remainingresponse header.
The message is sent from the lead’s own campaign number and appears in
the dashboard conversation thread. Requires scope messages:send.
Note: 201 means the message was accepted by the WhatsApp engine.
Like all WhatsApp APIs, final delivery is asynchronous (device online,
contact state…). Delivery/read receipts will surface via webhooks in a
future version.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
The lead’s id from /leads or /conversations.
When true (default, recommended), the send replays a human choreography (read receipt, “typing…” indicator, a typing delay paced to the message length) before the message goes out. Expect the request to take ~3–15 s. Set to false for an instant send (still quota-gated).
Responses
Section titled “Responses”Message sent
object
object
Conversation row id
object
Examplegenerated
{ "data": { "id": "example", "message_id": "example", "lead_id": "example", "text": "example", "sent_at": "2026-04-15T12:00:00Z", "quota": { "limit": 1, "used": 1, "remaining": 1, "resets_at": "2026-04-15T12:00:00Z" } }}Headers
Section titled “Headers”Daily quota of the sending number
Missing, unknown or revoked API key
object
object
Examplegenerated
{ "error": { "code": "example", "message": "example" }}The key lacks the required scope
object
object
Examplegenerated
{ "error": { "code": "example", "message": "example" }}Resource not found in this workspace
object
object
Examplegenerated
{ "error": { "code": "example", "message": "example" }}Either the lead was never contacted (lead_not_contacted: cold
outreach must go through lists + campaigns) or a request with the
same Idempotency-Key is still in flight (request_in_flight).
object
object
Examplegenerated
{ "error": { "code": "example", "message": "example" }}Invalid parameter or body
object
object
Examplegenerated
{ "error": { "code": "example", "message": "example" }}Daily anti-ban quota reached for this number
object
object
Examplegenerated
{ "error": { "code": "example", "message": "example" }}Send failed (transient), retry shortly
object
object
Examplegenerated
{ "error": { "code": "example", "message": "example" }}The campaign’s WhatsApp number is disconnected
object
object
Examplegenerated
{ "error": { "code": "example", "message": "example" }}
