Aller au contenu

Messages

POST /messages écrit à un lead depuis le numéro WhatsApp de sa campagne, comme si un coéquipier avait pris la main dans le dashboard. Les règles anti-ban sont appliquées côté serveur et ne peuvent pas être contournées.

Méthode Chemin Scope
POST /messages messages:send
Terminal window
curl -X POST "https://app.whatsetter.com/api/v1/messages" \
-H "Authorization: Bearer ws_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 8c3d2f9e-7b1a-4e6f-9c0d-2a1b3c4d5e6f" \
-d '{"lead_id":"66f1a2b3c4d5e6f7a8b9c0d1","text":"On se confirme pour demain 14h ?"}'
{
"data": {
"id": "66f1a2b3c4d5e6f7a8b9c0e5",
"message_id": "true_33612345678@c.us_3EB0C8F2A1D4B5E6F7A8",
"lead_id": "66f1a2b3c4d5e6f7a8b9c0d1",
"text": "On se confirme pour demain 14h ?",
"sent_at": "2026-09-19T08:12:04.000Z",
"humanized": true,
"typing_ms": 3210,
"quota": {
"limit": 250,
"used": 41,
"remaining": 209,
"resets_at": "2026-09-20T00:00:00.000Z"
}
}
}
Champ Signification
id L’id de la ligne de conversation dans WhatSetter, ou null si la ligne n’a pas pu être écrite après l’envoi.
message_id L’id du message WhatsApp.
lead_id Le lead à qui tu as écrit.
text Le texte envoyé.
sent_at Quand le moteur l’a accepté.
humanized Si la chorégraphie humaine a été jouée.
typing_ms Le délai de frappe simulé, 0 quand humanize vaut false.
quota Le budget quotidien du numéro après cet envoi.

201 signifie que le message a été accepté par le moteur WhatsApp. La livraison finale est asynchrone, comme sur toute API WhatsApp. Le message apparaît dans la conversation du dashboard comme une réponse manuelle.

Le lead doit déjà avoir une conversation : au moins un message échangé. Sinon tu reçois 409 lead_not_contacted. Le premier contact passe par une liste et une campagne ou un lead opt-in.

Chaque envoi consomme le même budget quotidien que le moteur de campagne pour ce numéro WhatsApp (250 messages par jour quand aucun quota spécifique n’est réglé). Une fois atteint : 429 quota_exceeded, remise à zéro à minuit UTC.

Chaque 201 et chaque 429 porte le budget dans les en-têtes :

X-Quota-Limit: 250
X-Quota-Remaining: 209
X-Quota-Reset: 2026-09-20T00:00:00.000Z

Si la campagne n’a pas de numéro WhatsApp, ou si le numéro est déconnecté, tu reçois 503 agent_disconnected. Vérifie whatsapp_connected sur GET /campaigns et reconnecte-le depuis le dashboard.

Par défaut (humanize: true), l’envoi rejoue une chorégraphie humaine : accusé de lecture, indicateur « en train d’écrire », un délai de frappe proportionnel à la longueur du message, puis l’envoi. Compte 3 à 15 secondes par requête. C’est le mode recommandé.

"humanize": false envoie instantanément, toujours sous quota. Réserve-le aux confirmations urgentes.

text est obligatoire, 1 à 4096 caractères, texte seulement.

Une API de messages doit pouvoir être rejouée sans danger : un timeout ne doit jamais se transformer en second vrai message WhatsApp. Passe un en-tête Idempotency-Key à chaque envoi, unique par intention (un UUID, un id de commande…), 200 caractères maximum. WhatSetter s’en souvient 15 minutes.

Situation Ce qu’obtient un retry avec la même clé
Le premier appel a répondu 201 Le même corps 201, avec l’en-tête X-Idempotent-Replay: true. Pas de second message.
Le premier appel est encore en cours 409 request_in_flight. Attends et réessaie.
Le premier appel a échoué proprement (422, 409, 429, 503) La clé est libérée : ton retry est une vraie nouvelle tentative.
Le premier appel a expiré (502 send_failed) 409 request_in_flight jusqu’à la fin de la fenêtre : le message est peut-être parti. Vérifie la transcription avant de renvoyer.
HTTP code Quand
401 missing_api_key, invalid_api_key Clé absente, inconnue ou révoquée.
403 insufficient_scope La clé n’a pas messages:send.
404 not_found Lead inconnu, ou lead d’un autre espace de travail.
409 lead_not_contacted Le lead n’a jamais été contacté. Passe par une liste ou un lead opt-in.
409 request_in_flight Même Idempotency-Key encore en traitement, ou une tentative précédente avec cette clé a expiré.
422 validation_error lead_id absent, text vide ou trop long, humanize qui n’est pas un booléen, clé de plus de 200 caractères.
429 quota_exceeded Budget quotidien du numéro atteint. Voir X-Quota-Reset.
429 rate_limited Trop de requêtes cette minute. Respecte Retry-After.
500 internal_error Moteur de messagerie non configuré de notre côté.
502 send_failed Le moteur a refusé ou n’a pas confirmé l’envoi. Réessaie avec la même clé.
503 agent_disconnected Pas de numéro sur la campagne, ou numéro hors ligne.