Quickstart
Оваа содржина сè уште не е достапна на вашиот јазик.
From a fresh API key to a streamed answer with citations. Four steps, all
curl — nothing to install beyond curl and jq.
1. Get a key
Section titled “1. Get a key”Open the API Keys screen and create one. Copy it immediately: the key is shown once and cannot be retrieved later, because only a hash of it is stored. If you lose it, regenerate rather than hunt for it.
Put it in your environment, along with the origin you are reading these docs on:
export KOVAC_API_KEY="the-key-you-just-copied"export KOVAC_BASE="https://your-platform-host"Every snippet below uses $KOVAC_API_KEY. Keep it that way in your own
code too — a key pasted literally into a script tends to end up in a
commit, and from there in a backup.
2. Find an agent
Section titled “2. Find an agent”Every run targets an agent by its assistant_id. List the agents your
tenant has:
curl -s "$KOVAC_BASE/api/manage/agents" \ -H "Authorization: Bearer $KOVAC_API_KEY" | jq '.[] | {name, assistant_id}'Pick one that has knowledge attached to it — an agent with no collections configured can still answer, but it has nothing to cite. The Agents screen shows each agent’s attached collections, and Attach knowledge to an agent covers how to add them.
export ASSISTANT_ID="the-assistant-id-you-picked"3. Create a thread
Section titled “3. Create a thread”A thread holds one conversation’s message history and state. Runs happen on a thread, and a second run on the same thread sees everything the first one said:
export THREAD_ID=$(curl -s -X POST "$KOVAC_BASE/api/agent/threads" \ -H "Authorization: Bearer $KOVAC_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' | jq -r '.thread_id')Threads belong to the identity that created them. A thread created with this key is not visible to a different key, even one in the same tenant — so give each service its own key rather than sharing one and expecting shared history.
4. Stream a run
Section titled “4. Stream a run”Start a run and read the server-sent events as they arrive:
curl -N -X POST "$KOVAC_BASE/api/agent/threads/$THREAD_ID/runs/stream" \ -H "Authorization: Bearer $KOVAC_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"assistant_id\": \"$ASSISTANT_ID\", \"input\": {\"messages\": [{\"role\": \"user\", \"content\": \"What does the handbook say about expenses?\"}]}, \"stream_mode\": [\"messages\", \"values\"] }"-N matters: without it curl buffers, and you get the whole stream at
the end instead of watching it arrive.
The response is a text/event-stream. Each event has a name, a JSON
payload and an id:
event: metadatadata: {"run_id":"7d183a7b-...","attempt":1}id: 7d183a7b-..._event_1
event: messages/partialdata: [{"content":"According to","type":"AIMessageChunk","id":"lc_run--..."}]id: 7d183a7b-..._event_14
event: valuesdata: {"messages":[...],"files":{}}id: 7d183a7b-..._event_58
event: enddata: {"status":"success"}id: 7d183a7b-..._event_129stream_mode decides which names you see, and the mode you ask for is not
always the event name you get:
messagesemitsmessages/partial(token chunks as the model produces them),messages/complete(a message finished) andmessages/metadata(per-message run detail). This is what makes a UI feel live.valuesemitsvalues— the full thread state after each step. Easier to work with if you only want the finished answer: keep the last one.updatesemitsupdates— just what changed at each step.
metadata and end are always sent. Ask for the modes you want, as
above, and ignore what you do not need.
end carries the verdict: {"status": "success"} or
{"status": "error"}. A run that fails emits an error event first, and
the HTTP response is still 200 — the request succeeded, the run did not.
See Errors & limits.
Reading the citations
Section titled “Reading the citations”If the agent has collections attached, it will call its search tool
before answering. Two things show up in the stream as a result.
First, the tool’s result message — a JSON array, one entry per retrieved passage:
[ { "ref": "c9f3a12b", "chunk_id": "c9f3a12b-4d5e-6f70-8192-a3b4c5d6e7f8", "document_id": "dddddddd-1111-2222-3333-444444444444", "content": "Expenses over 100 EUR require prior approval...", "source_uri": "upload://handbook.pdf", "collection": "handbook", "metadata": { "page": 4, "section": "Expenses" }, "confidence": "supported" }]metadata carries whatever the parser could establish about where the
passage came from. page and section are null for formats that have
no such structure — a Markdown file, say — so do not assume they are
present.
Second, the answer itself, which cites those passages inline by ref:
According to the handbook, expenses over 100 EUR require prior approvalfrom your line manager [c9f3a12b].ref names one exact passage, which is the whole point of it: a document
and a page do not, because a page is usually several passages. Resolve
each bracketed ref back to its row in the tool result, and render it as
whatever your interface wants — a numbered marker, a footnote, a link.
Answers never contain a file path or URL. If you are building your own
interface, do not expect one, and do not print source_uri to a reader:
it is a storage location, not a citation. It is still in the payload
because it is what you resolve a link against — which is the argument
for setting it to the document’s real home at upload time rather than
letting it default.
confidence is supported or weak, or null when the passage was not
scored. Treat null as “unknown”, never as weak.
Verifiable citations covers the full payload, and the API for fetching a rendered image of the cited page with the passage outlined.
Every claim drawn from retrieved text should carry a citation. If an answer has none, the agent either did not search or found nothing — Ingest a document and query it walks through telling those two apart.
Beyond curl
Section titled “Beyond curl”The same two calls in Python, JavaScript, PHP and Java — pre-filled with a
real assistant_id, in both raw-HTTP and official-SDK form — are on any
agent’s API tab in the platform. The
API reference can also send these requests for you,
against your own tenant, with a key you paste into the page.
