Skip to main content
A conversation is the WhatsApp thread between your business and a contact. Almost every send and action endpoint (/conversation/*) takes it in the request body under the conversation object.

Identifying a conversation

The conversation object accepts one of four identifiers, plus channel (required), the target channel’s public key. The resolver evaluates them in this order: uuidwhatsapp_user_idusernamephone: whatsapp_user_id looks up by exact match and username by a case-insensitive match (case is ignored); phone is normalized to digits. When the contact has a BSUID, phone and whatsapp_user_id identify different values (see Note).
string
Stable key of a conversation you already know (for example, persisted from a previous integration). The most direct path, though it can become stale if two conversations of the same contact are merged — see the merge note below.
string
The contact’s BSUID (Meta’s opaque identity). Resolves by exact match against the stored BSUID; if it does not resolve, the endpoint returns 404 without falling back to phone. Recommended identifier going forward.
string
The contact’s public WhatsApp username, with or without a leading @ and case-insensitive. Best-effort resolution against the last username observed by the platform (users can change their handle); if several contacts share it, the endpoint returns 409 (USERNAME_AMBIGUOUS).
string
Number in E.164 format (+5215512345678). Normalized to digits before the lookup. Useful when all you have is the phone.
string
required
The channel’s public key — copy it from the dashboard under Settings → Channels (the Channel key field), or list them with GET /channels. Required in every request: it sets the target channel. It is case-insensitive (normalized server-side). It does not identify on its own. On the contact-based paths (phone, username, whatsapp_user_id) it restricts resolution to the given channel; with uuid the engine ignores it, but the contract still requires it. It disambiguates the channel, not the contact: if several contacts share a phone or username, that ambiguity is resolved with whatsapp_user_id or uuid. On the contact-based paths, if you send a key that does not exist, the API returns 404 (CHANNEL_NOT_FOUND).
The phone number can be duplicated across contacts (recycled numbers or separate identities sharing a number): in that case the API returns 409 (PHONE_NUMBER_AMBIGUOUS) instead of guessing — identify the conversation by whatsapp_user_id or uuid. Also, whatsapp_user_id can change if the user re-registers their WhatsApp account (identity rotation on Meta’s side). Use the uuid as the stable key in your system and whatsapp_user_id as the preferred resolution identifier.
phone and whatsapp_user_id are different identifiers: phone is the phone number (wa_id without +) and whatsapp_user_id is the contact’s BSUID. They coincide only while the contact has no BSUID. In responses, whatsapp_user_id returns the real BSUID when present, with a fallback to the phone so there is always a resolvable value. Watch the round-trip: if the contact has no BSUID yet, this field carries the phone; re-send it as phone then, not as whatsapp_user_id — the resolver matches whatsapp_user_id by exact match and would return 404.
When the same contact ended up recorded twice by identity (for example, first only with their phone and later with their BSUID), the two conversations are merged automatically into one: the full history moves to the survivor. The absorbed conversation’s uuid stops resolving and returns 404; the phone and the BSUID migrate to the survivor. If a uuid you stored starts returning 404, re-resolve the conversation by phone or whatsapp_user_id.
channel is required in every request: you always set the target channel with its public key (list them with GET /channels). This way, if a contact has conversations across more than one channel (multiple WhatsApp numbers), resolution by phone, username, or whatsapp_user_id already knows which one you mean. A key that does not exist returns 404 (CHANNEL_NOT_FOUND).

Reading a conversation

You address a conversation with one of the references above: the public API does not offer a listing to enumerate them. Those references come from your own inbound —the phone or whatsapp_user_id of the contact who messaged you— or from a uuid you have persisted. With that reference, GET /conversation returns its state (conversation_info and contact_info) and GET /conversation/messages its history. The full shape and parameters are in Conversation and thread.

The 24h window

This is WhatsApp’s core concept and it decides what you can send.
1

The contact messages you

WhatsApp opens a 24-hour window. Each new message from the contact resets the timer.
2

Window open

You can send free-form messages: text, media, and quick replies.
3

24h pass with no reply

The window closes. The only allowed message is a template approved by Meta.
4

Sending a template reopens the window

You can send free-form messages again.
Sending text, media, or a quick reply with the window closed fails with WINDOW_CLOSED (422). Send a template first to reopen it.

Checking the window status

Before sending a free-form message, you can verify whether the window is open with POST /conversation/status:
Response:
The response includes conversation.uuid (the conversation’s stable key), window.status (open or closed), window.expires_at (ISO timestamp of the window’s close; null if the contact never messaged), and window.hours_remaining, plus the assignment, mailbox, tags, and inbox_status of the conversation. Use it to decide between a free-form message and a template.

Reading the thread

Returns the conversation’s messages and events. It is identified with the same reference as the rest of the API, so you do not need to know any uuid.
The thread paginates backwards in time: the first call returns the most recent items and next_cursor moves towards the oldest ones.
Every item answers four questions: who spoke (direction and author), what happened (event), with what content (content_type and body), and when (sent_at). Read in order, they look like the actual conversation:
And this is how it travels in the JSON:
type tells the two kinds of item apart: message is content that travelled over WhatsApp (it carries content_type and status), and event is something that happened in the history — a context note, an updated contact field, a conversion — with no content sent. Items within each page are in chronological order, and sent_at is the key that orders the full thread as you page backwards.

Next steps

Send messages

Text, media, quick replies, and templates.

Templates

How to send templates when the 24h window is closed.