Reference
Messages
Messages are individual entries in a conversation. Each carries a channel
(sms, email, whatsapp, etc.), direction (inbound or outbound), and lifecycle
timestamps (sent_at, delivered_at, read_at). Outbound messages are routed
by ChannelRouter to the appropriate provider integration.
List messages in a conversation
Lists the messages of one conversation, oldest first, cursor-paginated by
id. For infinite scroll,
page backwards using prev_cursor.
GET
/api/conversations/{conversation_id}/messages
Path parameters
-
conversation_idinteger requiredConversation ID.
Example:
17
Query parameters
-
per_pageintegerRows per page. Default 50, maximum 200. See Lists, paging and filters.
Example:
50 -
cursorstringOpaque cursor from a previous response.
Example:
architecto
curl --request GET \
--get "https://klozzo.com/api/conversations/17/messages?per_page=50&cursor=architecto" \
--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/messages"
);
const params = {
"per_page": "50",
"cursor": "architecto",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
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/messages';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'per_page' => '50',
'cursor' => 'architecto',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json
url = 'https://klozzo.com/api/conversations/17/messages'
params = {
'per_page': '50',
'cursor': 'architecto',
}
headers = {
'Authorization': 'Bearer {YOUR_API_TOKEN}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()
{
"data": [
{
"id": 9001,
"conversation_id": 17,
"channel": "whatsapp",
"direction": "outbound",
"body": "Hi Ada, your appointment is confirmed.",
"attachments": null,
"status": "delivered",
"provider_message_id": "wamid.HBgN...",
"sent_at": "2026-06-05T13:42:00+00:00",
"delivered_at": "2026-06-05T13:42:02+00:00",
"read_at": null,
"error": null,
"user_id": 7,
"created_at": "2026-06-05T13:42:00+00:00"
}
],
"next_cursor": "eyJpZCI6OTAwMSwiX3BvaW50c1RvTmV4dEl0ZW1zIjp0cnVlfQ",
"prev_cursor": null
}
-
dataobject[]-
idinteger -
conversation_idinteger -
channelstring -
directionstring -
bodystring -
attachmentsstring -
statusstring -
provider_message_idstring -
sent_atstring -
delivered_atstring -
read_atstring -
errorstring -
user_idinteger -
created_atstring
-
-
next_cursorstring -
prev_cursorstring
{
"message": "No query results for model."
}
Every error shares the same shape — message and errors.
See Errors.
Send a message
Records the outbound message immediately (returning 201) and dispatches
delivery via the appropriate provider asynchronously. Poll the message
resource for delivered_at / read_at updates, or subscribe to outbound
webhooks (message.delivered, message.read).
POST
/api/conversations/{conversation_id}/messages
If recipient is omitted, it is inferred from the contact:
email→contact.email- everything else →
contact.phone_e164orcontact.phone
Path parameters
-
conversation_idinteger requiredConversation ID.
Example:
17
Body parameters
-
channelstring requiredChannel to send on. One of
sms,email,whatsapp,messenger,instagram,gmb.Example:
whatsapp -
bodystring requiredMessage body (plain text). Max 8000 chars.
Example:
Hi Ada, your appointment is confirmed. -
attachmentsstring[]Optional list of attachment URLs. Provider-specific support.
Example:
["architecto"] -
recipientstringOverride destination (e.g. specific email or phone). Defaults to the contact's primary handle for the channel.
Example:
architecto
curl --request POST \
"https://klozzo.com/api/conversations/17/messages" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"channel\": \"whatsapp\",
\"body\": \"Hi Ada, your appointment is confirmed.\",
\"attachments\": [
\"architecto\"
],
\"recipient\": \"architecto\"
}"
const url = new URL(
"https://klozzo.com/api/conversations/17/messages"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"channel": "whatsapp",
"body": "Hi Ada, your appointment is confirmed.",
"attachments": [
"architecto"
],
"recipient": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://klozzo.com/api/conversations/17/messages';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'channel' => 'whatsapp',
'body' => 'Hi Ada, your appointment is confirmed.',
'attachments' => ['architecto'],
'recipient' => 'architecto',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json
url = 'https://klozzo.com/api/conversations/17/messages'
payload = {
"channel": "whatsapp",
"body": "Hi Ada, your appointment is confirmed.",
"attachments": [
"architecto"
],
"recipient": "architecto"
}
headers = {
'Authorization': 'Bearer {YOUR_API_TOKEN}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()
{
"data": {
"id": 9001,
"conversation_id": 17,
"channel": "whatsapp",
"direction": "outbound",
"body": "Hi Ada, your appointment is confirmed.",
"attachments": null,
"status": "queued",
"provider_message_id": null,
"sent_at": null,
"delivered_at": null,
"read_at": null,
"error": null,
"user_id": 7,
"created_at": "2026-06-05T13:42:00+00:00"
}
}
-
dataobject-
idinteger -
conversation_idinteger -
channelstring -
directionstring -
bodystring -
attachmentsstring -
statusstring -
provider_message_idstring -
sent_atstring -
delivered_atstring -
read_atstring -
errorstring -
user_idinteger -
created_atstring
-
{
"message": "Channel no es una opción válida.",
"errors": {
"channel": [
"Channel no es una opción válida."
]
}
}
Every error shares the same shape — message and errors.
See Errors.
{
"message": "Falta body.",
"errors": {
"body": [
"Falta body."
]
}
}
Every error shares the same shape — message and errors.
See Errors.
{
"message": "El contacto tiene activado No molestar para este canal.",
"errors": {
"channel": [
"El contacto tiene activado No molestar para este canal."
]
}
}
Every error shares the same shape — message and errors.
See Errors.