A conversation is the stored history of a chat: the messages exchanged with a model or
an assistant, its name, and a few links (assistant, organization, task,
workflow run). Conversations are created by the chat endpoint; there is no
endpoint to create or edit one directly. To continue a conversation, send its id as
conversation_id to chat.
All four endpoints see only the conversations of the API key's user:
- conversations the user started in the app or through the API, in personal use and in any organization they belong to;
- conversations started on the user's behalf by scheduled tasks and workflow runs.
Conversations of other users are never returned, even when they belong to the same organization or are shared publicly by a link. A conversation that exists but is not yours is reported as not found.
The cost of a conversation is available from the usage cost endpoint.
List conversations
| Endpoint | POST /api/v3/conversation/find |
| Scope | ayeto.conversation |
| Rate limit | default |
Returns the user's conversations that match the filters, newest first unless you sort otherwise. Each conversation is returned with its full message history, so always page through the results.
Request
The body is a query object with the usual filters, sorting and paging
fields. Send {} to get everything; the body itself is required.
| Field | Type | Required | Description |
|---|---|---|---|
filters |
array | No | Filter conditions, see filters and the examples below. Several conditions are combined with AND. |
sort_key |
string | No | Field to sort by, for example created.timestamp, updated.timestamp, accessed.timestamp or name. Default: created.timestamp, descending. |
sort_order |
integer | No | 0 ascending, 1 descending. Default 0 when sort_key is set. |
limit_from |
integer | No | Index of the first result (zero-based). Without it, all matching conversations are returned. |
limit_to |
integer | No | Index after the last result (exclusive), not a page size: limit_from: 20, limit_to: 40 returns the results at positions 20 to 39 (counting from 0). |
To read everything, request pages until a page comes back shorter than requested. The total for a pager comes from count conversations.
Useful filters
Filters can use any field of the conversation, including fields of the
messages (messages.content, messages.role, messages.model). Message conditions
are checked against every stored message, including tool results and other messages
that are not returned in messages.
| Goal | Filter |
|---|---|
| Conversations with one assistant | ["assistant_id", "==", "3f6c1a2e-8b4d-4e7a-9c1f-2d5e6a7b8c90"] |
| Personal conversations only (no organization) | ["organization_id", "==", null] |
| Conversations in one organization | ["organization_id", "==", "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"] |
| Conversations you had yourself, without task and workflow runs | ["task_id", "==", null], ["task_execution_id", "==", null], ["workflow_run_id", "==", null] |
| Conversations created by one workflow | ["workflow_id", "==", "5d4c3b2a-1f0e-4d9c-8b7a-6f5e4d3c2b1a"] |
| Changed since a moment (ms since epoch) | ["updated.timestamp", ">=", 1759363200000] |
| Name contains a word (case-insensitive) | ["name", "regex", "invoice"] |
| Any message contains a phrase (case-insensitive) | ["messages.content", "regex", "delivery date"] |
| Started with one of several models | ["model", "in", ["gpt-5", "claude-sonnet-4-5"]] |
| Any answer written by a model | ["messages.model", "==", "gpt-5"] |
Text filters are matched against the stored text after the API escapes <, >, "
and ', so a filter value containing these characters rarely matches; see
filters.
Response
200 OK with an array of conversations.
Errors
| Status | detail |
Cause |
|---|---|---|
403 |
permission denied |
The user's account is not allowed to read conversations. |
422 |
validation error | The body is missing, or a filter or sort_key is invalid. |
Authentication, rate limit and server errors are described in conventions.
Example
The 20 most recently updated conversations with one assistant:
curl -X POST "https://ayeto.ai/api/v3/conversation/find" \
-H "uni-api-key: $AYETO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filters": [["assistant_id", "==", "3f6c1a2e-8b4d-4e7a-9c1f-2d5e6a7b8c90"]],
"sort_key": "updated.timestamp",
"sort_order": 1,
"limit_from": 0,
"limit_to": 20
}'
[
{
"id": "8e2f4a6c-1b3d-4e5f-8a7b-9c0d1e2f3a4b",
"name": "Delivery terms for order 1042",
"model": "gpt-5",
"summary": "",
"organization_id": null,
"assistant_id": "3f6c1a2e-8b4d-4e7a-9c1f-2d5e6a7b8c90",
"task_id": null,
"task_execution_id": null,
"workflow_id": null,
"workflow_run_id": null,
"public_sharing": false,
"messages": [
{
"id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
"timestamp": 1759395601000,
"role": "user",
"content": "What delivery date did we agree for order 1042?",
"reasoning_content": null,
"model": null,
"attachments": [],
"tool_runs": {}
},
{
"id": "1b2c3d4e-5f6a-4b7c-9d8e-0f1a2b3c4d5e",
"timestamp": 1759395606000,
"role": "assistant",
"content": "The agreed delivery date for order 1042 is 14 October 2026.",
"reasoning_content": null,
"model": "gpt-5",
"attachments": [],
"tool_runs": {}
}
],
"created": {"timestamp": 1759395600000, "user_id": "6c5b4a39-2817-4f6e-9d5c-4b3a29180f7e"},
"updated": {"timestamp": 1759395606000, "user_id": "6c5b4a39-2817-4f6e-9d5c-4b3a29180f7e"},
"accessed": {"timestamp": 1759395606000, "user_id": "6c5b4a39-2817-4f6e-9d5c-4b3a29180f7e"}
}
]
Count conversations
| Endpoint | POST /api/v3/conversation/count |
| Scope | ayeto.conversation |
| Rate limit | default |
Returns the number of the user's conversations that match the filters, see counting.
Request
The same query object as list conversations; send {} to count
all of them. Only filters is used: limit_from, limit_to and the sort fields are
ignored, so you can send the same body as for the page you display.
Response
200 OK with the number of matching conversations as a JSON integer.
42
Errors
| Status | detail |
Cause |
|---|---|---|
403 |
permission denied |
The user's account is not allowed to read conversations. |
422 |
validation error | The body is missing, or a filter or sort_key is invalid. |
Authentication, rate limit and server errors are described in conventions.
Example
Conversations with one assistant changed since a moment:
curl -X POST "https://ayeto.ai/api/v3/conversation/count" \
-H "uni-api-key: $AYETO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filters": [
["assistant_id", "==", "3f6c1a2e-8b4d-4e7a-9c1f-2d5e6a7b8c90"],
["updated.timestamp", ">=", 1759363200000]
]
}'
7
Get a conversation
| Endpoint | POST /api/v3/conversation/get |
| Scope | ayeto.conversation |
| Rate limit | default |
Returns one conversation with its full message history.
Request
| Field | Type | Required | Description |
|---|---|---|---|
id |
UUID | Yes | Id of the conversation. |
The id can also be sent as the entity_id query parameter instead, with no body or
{}; see parameters.
Reading a conversation counts as opening it: its accessed timestamp is updated (at
most once a minute).
Response
200 OK with a conversation.
Errors
| Status | detail |
Cause |
|---|---|---|
403 |
permission denied |
The user's account is not allowed to read conversations. |
404 |
entity not found, id: <id> |
No conversation with this id exists. |
404 |
entity not found |
The conversation exists but belongs to another user. |
422 |
validation error | The id is missing or not a UUID, or id and entity_id differ. |
Example
curl -X POST "https://ayeto.ai/api/v3/conversation/get" \
-H "uni-api-key: $AYETO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"id": "8e2f4a6c-1b3d-4e5f-8a7b-9c0d1e2f3a4b"}'
The response is a single object in the same shape as one item of the list example.
Delete a conversation
| Endpoint | POST /api/v3/conversation/delete |
| Scope | ayeto.conversation |
| Rate limit | default |
Deletes a conversation permanently, together with the files that belong to it (attachments sent in it and images or other files generated in it). This cannot be undone.
Request
| Field | Type | Required | Description |
|---|---|---|---|
id |
UUID | Yes | Id of the conversation. |
The id can also be sent as the entity_id query parameter instead, with no body or
{}; see parameters.
Response
200 OK with the id of the deleted conversation as a JSON string.
"8e2f4a6c-1b3d-4e5f-8a7b-9c0d1e2f3a4b"
Errors
| Status | detail |
Cause |
|---|---|---|
403 |
permission denied |
The user's account is not allowed to delete conversations. |
404 |
entity not found, id: <id> |
No conversation with this id exists. |
404 |
entity not found |
The conversation exists but belongs to another user. |
422 |
validation error | The id is missing or not a UUID, or id and entity_id differ. |
500 |
can not delete entity |
The conversation could not be deleted; retry later. |
Example
curl -X POST "https://ayeto.ai/api/v3/conversation/delete" \
-H "uni-api-key: $AYETO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"id": "8e2f4a6c-1b3d-4e5f-8a7b-9c0d1e2f3a4b"}'
Conversation
The object returned by all conversation endpoints.
| Field | Type | Required | Description |
|---|---|---|---|
id |
UUID | Yes | Conversation id. The same value is the conversation_id of chat. |
name |
string | Yes | Display name. New conversation until a name is suggested after the first answer or set in the app. |
model |
string | Yes | Id of the model the conversation was started with (see models). Individual answers may come from another model; see the message model. |
summary |
string | Yes | Summary generated in the app on request; empty string when none was generated. |
organization_id |
UUID or null | No | Organization the conversation belongs to; null for personal use. |
assistant_id |
UUID or null | No | Assistant the conversation is held with; null when chatting directly with a model. |
task_id |
UUID or null | No | Scheduled task that created the conversation. |
task_execution_id |
UUID or null | No | Run of that task. |
workflow_id |
UUID or null | No | Workflow whose assistant step created the conversation. |
workflow_run_id |
UUID or null | No | Run of that workflow. |
public_sharing |
boolean | Yes | true when the user has shared the conversation publicly by a link in the app. |
messages |
array of Message | Yes | User and assistant messages in chronological order. System, tool and other internal messages are left out. |
created, updated, accessed |
object | Yes | {timestamp, user_id} metadata, see common fields. updated changes with every new message; accessed is when the conversation was last opened. |
Message
| Field | Type | Required | Description |
|---|---|---|---|
id |
UUID | Yes | Message id. |
timestamp |
timestamp | Yes | When the message was created (ms since epoch). |
role |
string | Yes | user or assistant. |
content |
string or null | No | Message text in Markdown. In assistant messages it also contains the tool calls made while answering. |
reasoning_content |
string or null | No | The model's reasoning text, when the model returns it. |
model |
string or null | No | Model that wrote an assistant message; null on user messages. |
attachments |
array of Attachment | Yes | Files attached to the message. |
tool_runs |
object | Yes | Answers of sub-assistants called during this message, see tool calls. Usually empty. |
Attachment
| Field | Type | Required | Description |
|---|---|---|---|
filename |
string | Yes | File name. |
content_type |
string | Yes | MIME type, for example image/png or application/pdf. |
file_url |
string | Yes | Public URL of the file. Files sent or generated in the conversation are deleted with it. |
name |
string or null | No | Display name, when different from filename. |
description |
string or null | No | Short description of the file. |
Tool calls in message content
Every tool the model called while writing an assistant message appears in content as
a block that starts with "\n\n### calling AI tool ...\n" and ends with "\n***\n".
The text between the two markers is the tool's output shown to the user:
{
"role": "assistant",
"content": "Let me look that up.\n\n### calling AI tool ...\nSearching the web for \"order 1042 delivery\"...\n***\nThe agreed delivery date is 14 October 2026."
}
Remove these blocks if you only need the answer text. (The chat endpoint can
leave them out of its own response with remove_tool_calls; the stored conversation always keeps them.)
When an assistant calls another assistant as a tool, that assistant's answer is stored
in tool_runs, keyed by the position of the tool call in content as a string ("0"
for the first tool call, "1" for the second, and so on). Each value has a content
string in the same format and its own nested tool_runs.