Docs · Native API
Conversations
The conversations that the native chat endpoints store. You can list them, read their messages, export them and delete them. All of these endpoints need your API key and are scoped to your own conversations.
Conventions#
- Successful reads are wrapped as
{"status": "success", "data": {…}}; writes also carry amessage, and the deletes have amessageand nodata. Errors are{"status": "error", "message": "…"}. - Dates are ISO 8601 in UTC.
- Ids are integers.
- The request limit is 300 per minute per account, with one exception noted under permanent deletion. See rate limits and quotas.
The objects#
A conversation carries more fields than the ones below, most of them internal bookkeeping. These are the ones to use:
| Field | Meaning |
|---|---|
id | the conversation id |
title | the title. It is made from the first message unless you set one |
session_id | the key given to POST /api/v1/chat, or null |
model_name, profile_name | the model that answered last. A conversation can change model between turns |
effort | the saved reasoning effort, or null |
total_messages, total_tokens | counters |
is_active | whether it is a candidate for the conversation that the web app has open. Conversations made by the chat endpoints are false. One made with POST /api/v1/conversations starts as true, and the web app may open it |
folder_id | the project it sits in. API conversations are in a project named API |
last_message_at, created_at, updated_at | timestamps |
A message has id, conversation_id, role (user, assistant or system), content, reasoning, prompt_tokens, completion_tokens, total_tokens, response_time_ms, model_name, created_at, and a metadata object with details such as the request_id, the effort and any cost_usd. Do not rely on other keys inside metadata.
GET/api/v1/conversations#
Lists your conversations, most recent first.
| Query | Default | Notes |
|---|---|---|
skip | 0 | how many to skip |
limit | 50 | 1 to 200 |
active_only | false | only conversations whose is_active is true: the one the web app has open, and any made with POST /api/v1/conversations |
{
"status": "success",
"data": {
"conversations": [
{
"id": 3,
"session_id": "support-ticket-1042",
"title": "Say hello",
"model_name": "claude-sonnet-4-6",
"profile_name": "claude-sonnet-4-6@anthropic",
"effort": null,
"total_messages": 4,
"total_tokens": 100,
"is_active": false,
"folder_id": 1,
"last_message_at": "2026-10-10T17:43:47.000000Z",
"created_at": "2026-10-10T17:43:40.000000Z",
"updated_at": "2026-10-10T17:43:47.000000Z"
}
],
"total": 1
}
}total is the number of conversations in this page, not overall. There is no cursor: to read further, add skip. You cannot filter by session_id, so to find the conversation of a session, read the list and match the field, up to 200 at a time.
POST/api/v1/conversations#
Creates an empty conversation without calling a model. All fields are optional: title, session_id (up to 255 characters, although POST /api/v1/chat refuses more than 191 and only looks in the API project, so a conversation made here cannot be continued by session_id from the chat stream), model_name and profile_name. It answers 201.
The conversation starts with is_active set to true, and it is not placed in the API project, so POST /api/v1/chat does not find it by session_id. It can become the conversation that the person sees in the web app: send "is_active": false with PUT to prevent that. To continue it, use its id with POST /api/v1/agent/chat.
GET/api/v1/conversations/{id}#
Returns {"status": "success", "data": {"conversation": {…}}}.
PUT/api/v1/conversations/{id}#
Changes a conversation. All fields are optional:
| Field | Notes |
|---|---|
title | up to 255 characters. Setting it stops the automatic title. An empty title is ignored, so a title cannot be cleared |
effort | low, medium, high, xhigh or max |
is_active | true or false |
folder_id | one of your chat projects, or null to take it out. Another kind of project is a 422 |
GET/api/v1/conversations/{id}/messages#
| Query | Default | Notes |
|---|---|---|
skip | 0 | how many to skip |
limit | 100 | 1 to 500 |
{
"status": "success",
"data": {
"messages": [
{ "id": 5, "conversation_id": 3, "role": "user", "content": "hi", "prompt_tokens": 12, "created_at": "2026-10-10T17:43:47.000000Z" },
{ "id": 6, "conversation_id": 3, "role": "assistant", "content": "Hello from the model.", "prompt_tokens": 12, "completion_tokens": 7, "total_tokens": 19, "model_name": "claude-sonnet-4-6", "created_at": "2026-10-10T17:43:47.000000Z" }
],
"total": 2
}
}Messages are ordered by creation time, oldest first. Two messages created in the same second have no defined order between them. total is again the size of the page.
POST/api/v1/conversations/{id}/messages#
Stores a message. It does not call a model. The body has a required role (user, assistant or system) and a required string content, and optionally model_name, token counts and a metadata object. It answers 201.
Stored messages become part of the history that the next turn of that conversation sends to the model, so this is a way to seed a conversation with context, including a system message, before you call POST /api/v1/agent/chat with its id.
GET/api/v1/conversations/{id}/export#
Downloads a conversation. format is md (the default), html or jsonl; anything else is a 422.
curl -s "$SHVIA_URL/api/v1/conversations/3/export?format=jsonl" \
-H "Authorization: Bearer $SHVIA_API_KEY"The response is the file itself, with a Content-Disposition header. In jsonl the first line describes the conversation, and each later line is one message: {"type":"conversation",…} and then {"type":"message","id","role","content","model","created_at"}.
GET/api/v1/conversations/{id}/usage#
The tokens and cost of one conversation. See usage and cost.
Deleting#
DELETE/api/v1/conversations/{id}#
A soft delete: the conversation disappears from the lists and its messages are kept. There is no endpoint to restore it.
DELETE/api/v1/conversations/{id}/permanent#
Deletes the conversation and its messages for good, including one that was already soft-deleted. The records that meter your usage stay, with the link to the conversation removed. It is one of the numbered endpoints of rate limits and quotas: its ceiling is 20 per minute, on the counter that they share.
Errors#
| Status | Body | When |
|---|---|---|
| 401 | {"message": "Unauthenticated."} | the credential is not valid |
| 404 | {"status": "error", "message": "…"} | the conversation does not exist, was deleted, or is not yours |
| 422 | {"status": "error", "message": "…"}, with errors for a field that failed validation | the body failed validation, the export format is unknown, or the project is not a chat project of yours |
A conversation that is not yours is a 404, as if it did not exist. The body is {"status": "error", …} or the plain {"message": "Not Found"}, depending on the endpoint and on whether the conversation exists, so switch on the status. A bot's own conversation cannot be deleted: that is a 422 with codigo set to chat_do_bot_nao_se_apaga.