List Conversations
curl --request GET \
--url https://app.tuco.ai/api/unibox/conversations \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.tuco.ai/api/unibox/conversations"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.tuco.ai/api/unibox/conversations', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.tuco.ai/api/unibox/conversations",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.tuco.ai/api/unibox/conversations"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.tuco.ai/api/unibox/conversations")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.tuco.ai/api/unibox/conversations")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_bodyReplies
List Conversations
Read a workspace’s inbox — every conversation with its thread, unread count, tags and status, with filters and pagination.
GET
/
api
/
unibox
/
conversations
List Conversations
curl --request GET \
--url https://app.tuco.ai/api/unibox/conversations \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.tuco.ai/api/unibox/conversations"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.tuco.ai/api/unibox/conversations', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.tuco.ai/api/unibox/conversations",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.tuco.ai/api/unibox/conversations"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.tuco.ai/api/unibox/conversations")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.tuco.ai/api/unibox/conversations")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_bodyThis is the inbox behind the Tuco unibox. One call gives you the conversation
list with each thread’s messages, so you can build your own inbox UI.
Endpoint
- Method:
GET - Path:
/api/unibox/conversations - Auth:
Authorization: Bearer tuco_xxxxxxxxxxxxx(workspace key)
Two things that surprise people
A conversation is keyed by recipient, not by lead. There is noleadId on a
conversation — it is identified by the phone number or email address at
recipient. That is also why the deep link is ?conversation=<address>.
The thread holds what actually went out or came in. Messages still queued,
pending or scheduled are deliberately not in it — a conversation shows
history, not intent. Read the outbound queue from
/api/unibox/scheduled instead. If you expect “everything
for this contact” here, you will wrongly conclude the follow-ups were lost.
Pagination
| Param | Default | Notes |
|---|---|---|
limit | 50 | Capped at 100. A larger value is silently reduced — read pagination.limit back |
page | 1 | Offset paging |
cursor | — | Opaque cursor from pagination.nextCursor. Preferred for deep paging |
messageLimit | unlimited | Cap the messages carried per conversation. Use a small N for a list view, then re-fetch one thread with ?conversation= |
"pagination": {
"page": 1,
"limit": 50,
"totalCount": 128,
"totalPages": 3,
"nextCursor": "eyJsYXN0TWVzc2FnZUF0IjoiMjAyNi0xMC0wN1QxODoy…",
"hasMore": true
}
Filters
All are query params and all intersect.| Param | Matches |
|---|---|
tag | Conversations whose lead carries this tag |
status | Conversation state, e.g. unread |
line | Only conversations on this sending line (lineId) |
conversation | One thread, by recipient address. URL-encode a + as %2B |
contactOwner | The workspace member who owns the contact |
propertyKey + propertyOp + propertyValue | A custom-property comparison. propertyStandard=true targets a standard field |
curl "https://app.tuco.ai/api/unibox/conversations?limit=25&tag=vip&status=unread" \
-H "Authorization: Bearer tuco_xxxxxxxxxxxxx"
Response
{
"conversations": [
{
"id": "…",
"recipient": "+15558880001",
"recipientName": "Dana Reyes",
"recipientType": "phone",
"lastMessageAt": "2026-10-07T18:22:05.117Z",
"unreadCount": 1,
"status": "unread",
"tags": [],
"leadTags": ["vip"],
"messages": [
{ "status": "sent", "direction": "outbound", "body": "Hi Dana" },
{ "status": "delivered", "direction": "inbound", "body": "Tell me more" }
]
}
],
"pagination": { "page": 1, "limit": 25, "totalCount": 1, "totalPages": 1, "nextCursor": null, "hasMore": false }
}
The pending queue
Anything not yet sent:curl https://app.tuco.ai/api/unibox/scheduled \
-H "Authorization: Bearer tuco_xxxxxxxxxxxxx"
Related
GET /api/unibox/tags— the tags you can filter onGET /api/unibox/custom-properties— the property keys forpropertyKeyGET /api/replies— a flat, paginated feed of inbound repliesPOST /api/unibox/mark-read,/archive,/flag,/send-reply— act on a conversation
GET /api/unibox/stream (server-sent events) is browser-only and rejects API
keys. For server-side updates use webhooks —
message.reply fires on every inbound message.Error responses
| Status | When |
|---|---|
401 | Missing or invalid key |
403 | An agency key (tucoagency_…) was used — this is per-workspace |