ShvIA

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 a message, and the deletes have a message and no data. 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:

FieldMeaning
idthe conversation id
titlethe title. It is made from the first message unless you set one
session_idthe key given to POST /api/v1/chat, or null
model_name, profile_namethe model that answered last. A conversation can change model between turns
effortthe saved reasoning effort, or null
total_messages, total_tokenscounters
is_activewhether 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_idthe project it sits in. API conversations are in a project named API
last_message_at, created_at, updated_attimestamps

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.

QueryDefaultNotes
skip0how many to skip
limit501 to 200
active_onlyfalseonly conversations whose is_active is true: the one the web app has open, and any made with POST /api/v1/conversations
json
{
  "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:

FieldNotes
titleup to 255 characters. Setting it stops the automatic title. An empty title is ignored, so a title cannot be cleared
effortlow, medium, high, xhigh or max
is_activetrue or false
folder_idone of your chat projects, or null to take it out. Another kind of project is a 422

GET/api/v1/conversations/{id}/messages#

QueryDefaultNotes
skip0how many to skip
limit1001 to 500
json
{
  "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.

bash
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#

StatusBodyWhen
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 validationthe 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.