Reference
Conversations
Conversations are per-contact message threads spanning all channels (SMS, email, WhatsApp, etc.). A contact has at most one open conversation per location. New inbound or outbound messages auto-create the conversation if it does not exist.
List conversations for a contact
Lists the conversations of one contact, ordered by most recent activity. There is typically one per contact-location pair, but historical conversations remain queryable.
GET
/api/contacts/{contact_id}/conversations
Path parameters
-
contact_idinteger requiredContact ID.
Example:
42
curl --request GET \
--get "https://klozzo.com/api/contacts/42/conversations" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"
const url = new URL(
"https://klozzo.com/api/contacts/42/conversations"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://klozzo.com/api/contacts/42/conversations';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json
url = 'https://klozzo.com/api/contacts/42/conversations'
headers = {
'Authorization': 'Bearer {YOUR_API_TOKEN}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()
{
"data": [
{
"id": 17,
"contact_id": 42,
"location_id": 1,
"last_message_at": "2026-06-05T13:42:00+00:00",
"last_channel": "whatsapp",
"unread_count": 3
}
]
}
-
dataobject[]-
idinteger -
contact_idinteger -
location_idinteger -
last_message_atstring -
last_channelstring -
unread_countinteger
-
Open or fetch a conversation for a contact
Opens a conversation for one contact, or hands back the one that already exists. Idempotent: returns the existing conversation for the contact-location pair when one already exists (200), or creates and returns a fresh one (201).
POST
/api/contacts/{contact_id}/conversations
Path parameters
-
contact_idinteger requiredContact ID.
Example:
42
curl --request POST \
"https://klozzo.com/api/contacts/42/conversations" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"
const url = new URL(
"https://klozzo.com/api/contacts/42/conversations"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://klozzo.com/api/contacts/42/conversations';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json
url = 'https://klozzo.com/api/contacts/42/conversations'
headers = {
'Authorization': 'Bearer {YOUR_API_TOKEN}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers)
response.json()
{
"data": {
"id": 17,
"contact_id": 42,
"location_id": 1,
"last_message_at": "2026-06-05T13:42:00+00:00",
"last_channel": "whatsapp",
"unread_count": 3
}
}
-
dataobject-
idinteger -
contact_idinteger -
location_idinteger -
last_message_atstring -
last_channelstring -
unread_countinteger
-
{
"data": {
"id": 17,
"contact_id": 42,
"location_id": 1,
"last_message_at": null,
"last_channel": null,
"unread_count": 0
}
}
-
dataobject-
idinteger -
contact_idinteger -
location_idinteger -
last_message_atstring -
last_channelstring -
unread_countinteger
-
Fetch a conversation
Returns one conversation by id: the thread, the contact it belongs to and
its channel. Use it to check
the state of a thread you were handed the id of; to read the messages,
call GET /api/conversations/{id}/messages, which is paginated because a
WhatsApp thread is not.
GET
/api/conversations/{id}
Path parameters
-
idinteger requiredConversation ID.
Example:
17
curl --request GET \
--get "https://klozzo.com/api/conversations/17" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"
const url = new URL(
"https://klozzo.com/api/conversations/17"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://klozzo.com/api/conversations/17';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json
url = 'https://klozzo.com/api/conversations/17'
headers = {
'Authorization': 'Bearer {YOUR_API_TOKEN}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()
{
"data": {
"id": 17,
"contact_id": 42,
"location_id": 1,
"last_message_at": "2026-06-05T13:42:00+00:00",
"last_channel": "whatsapp",
"unread_count": 3
}
}
-
dataobject-
idinteger -
contact_idinteger -
location_idinteger -
last_message_atstring -
last_channelstring -
unread_countinteger
-
{
"message": "This action is unauthorized."
}
Every error shares the same shape — message and errors.
See Errors.
{
"message": "No query results for model."
}
Every error shares the same shape — message and errors.
See Errors.