Conversations
A conversation is every message exchanged with one person, in both directions, oldest to newest. It is what the console shows under Messages, and it is two endpoints if you would rather build your own.
List them
GET /v1/chats
curl https://njiwa.upeo.ai/v1/chats \
-H "Authorization: Bearer sk_live_..."{
"object": "list",
"data": [
{
"jid": "254712345678",
"msisdn": "254712345678",
"is_group": false,
"last_message_at": "2026-09-02T11:19:46Z",
"message_count": 29,
"last_message": {
"direction": "outbound", "type": "text",
"status": "delivered", "text": "Your invoice for August"
}
}
]
}
Ordered by most recent activity. last_message is there so a list of
conversations can show a preview line without a request per row.
Read one
GET /v1/chats/{id}/messages?limit=200
id is the jid from the list. Messages come back newest first,
in the same shape as GET /v1/messages, with direction telling
you which side each one is. Reverse the list to render a thread.
curl https://njiwa.upeo.ai/v1/chats/254712345678/messages \
-H "Authorization: Bearer sk_live_..."Replying
There is no reply endpoint. Send to the same number with
POST /v1/messages and it joins the thread.
What identifies a conversation
The other person's phone number, not the address WhatsApp used.
This distinction is worth a paragraph because it is not obvious. WhatsApp addresses the same person two different ways: messages from them can arrive from an internal account id, while messages to them go to their phone number. Keyed on the address, one conversation becomes two, each holding one half, and neither reads like a conversation. Keyed on the number, which is the same in both directions, it is one thread.
So jid is a phone number for a one to one chat. A group has no single
other person, so a group is identified by its own group id and msisdn is
null; check is_group rather than guessing from the shape of the
string. An old chat id still resolves to the right conversation, so a stored id does not
go stale.