Conversations
A conversation is the thread between your agent and one lead. The API gives you the inbox (leads with message activity) and the transcript of each lead, read-only.
Endpoints
Section titled “Endpoints”| Method | Path | Scope |
|---|---|---|
| GET | /conversations |
conversations:read |
| GET | /conversations/{leadId}/messages |
conversations:read |
List conversations
Section titled “List conversations”The inbox: every lead that exchanged at least one message, most recent activity first.
curl "https://app.whatsetter.com/api/v1/conversations?campaign_id=acme-france-1-ab12&limit=25" \ -H "Authorization: Bearer ws_live_…"const res = await fetch( 'https://app.whatsetter.com/api/v1/conversations?campaign_id=acme-france-1-ab12&limit=25', { headers: { Authorization: 'Bearer ws_live_…' } },);const { data, pagination } = await res.json();import requests
r = requests.get( "https://app.whatsetter.com/api/v1/conversations", params={"campaign_id": "acme-france-1-ab12", "limit": 25}, headers={"Authorization": "Bearer ws_live_…"},)data = r.json()["data"]{ "data": [ { "lead_id": "66f1a2b3c4d5e6f7a8b9c0d1", "phone": "33612345678", "whatsapp_id": "33612345678@c.us", "name": "Julien", "status": "in_progress", "qualified": false, "replied": true, "campaign_id": "acme-france-1-ab12", "messages_count": 6, "last_message_at": "2026-09-18T16:42:05.000Z", "last_message_direction": "inbound" } ], "pagination": { "has_more": false, "next_cursor": "66f1a2b3c4d5e6f7a8b9c0d1" }}campaign_id accepts the campaign’s slug or its id. Without it, conversations of group-purpose campaigns stay out, same as the dashboard. last_message_direction: "inbound" means the lead spoke last: that is your “needs an answer” signal.
Read a lead’s messages
Section titled “Read a lead’s messages”leadId is the lead’s id from /leads or /conversations. Messages come back newest first.
curl "https://app.whatsetter.com/api/v1/conversations/66f1a2b3c4d5e6f7a8b9c0d1/messages?limit=50" \ -H "Authorization: Bearer ws_live_…"const res = await fetch( 'https://app.whatsetter.com/api/v1/conversations/66f1a2b3c4d5e6f7a8b9c0d1/messages?limit=50', { headers: { Authorization: 'Bearer ws_live_…' } },);const { data: messages, pagination } = await res.json();import requests
r = requests.get( "https://app.whatsetter.com/api/v1/conversations/66f1a2b3c4d5e6f7a8b9c0d1/messages", params={"limit": 50}, headers={"Authorization": "Bearer ws_live_…"},)messages = r.json()["data"]{ "data": [ { "id": "66f1a2b3c4d5e6f7a8b9c0e6", "direction": "inbound", "text": "Yes, 2pm works for me.", "type": "USER_MESSAGE", "has_media": false, "sent_at": "2026-09-18T16:42:05.000Z" }, { "id": "66f1a2b3c4d5e6f7a8b9c0e5", "direction": "outbound", "text": "Are we still on for tomorrow at 2pm?", "type": "MANUAL_RESPONSE", "has_media": false, "sent_at": "2026-09-18T16:40:10.000Z" } ], "pagination": { "has_more": false, "next_cursor": "66f1a2b3c4d5e6f7a8b9c0e5" }}| Field | Meaning |
|---|---|
direction |
inbound when the lead wrote, outbound when the agent, a teammate or the API wrote. |
text |
The message text, or null for a media-only message. |
type |
Internal interaction type, for example USER_MESSAGE, AI_RESPONSE, LEAD_WELCOME, FOLLOWUP, MANUAL_RESPONSE. Treat unknown values as opaque. |
has_media |
true when the message carried an image, voice note or document. The media itself is never linked. |
sent_at |
ISO 8601, UTC. |
Pagination
Section titled “Pagination”Both endpoints return pagination.has_more and pagination.next_cursor. Pass cursor=<next_cursor> to get the next page; limit is 1 to 100, 25 by default. A cursor that points to a deleted row answers 422 validation_error with “Invalid or expired cursor”: restart from the first page.
To sync an inbox, poll GET /conversations and only fetch the transcript of leads whose last_message_at moved. For real-time, prefer webhooks.
Personal data
Section titled “Personal data”Transcripts contain phone numbers, names and whatever the person wrote. Treat them as personal data: store only what your use case needs, respect your retention policy, and never expose them to people who could not open the WhatSetter inbox. phone is digits only, without the +.
Errors you will meet
Section titled “Errors you will meet”| HTTP | code |
When |
|---|---|---|
| 401 | missing_api_key, invalid_api_key |
Key absent, unknown or revoked. |
| 403 | insufficient_scope |
The key lacks conversations:read. |
| 404 | not_found |
Unknown lead, or a lead of another workspace. |
| 422 | validation_error |
limit out of 1 to 100, invalid or expired cursor. |
| 429 | rate_limited |
Too many requests this minute. Honor Retry-After. |

