Skip to main content
Workspace admin

Knowledge Base API

Add documents to a knowledge base someone already created in the workspace, ask questions against it, and hold conversations with it, all from your own code with an API key.

Everything here needs an API key and the 15-minute pass it exchanges for: how to get both. Paths below are relative to /external/v1/kb.

Not the chat widget

This is the partner API, reached with an account API key. The chat widget you embed in a web page is a different product with a different credential, issued by a signed-in person for one knowledge base: see Knowledge Base widget. The two are not interchangeable: a widget token will not authenticate here, and an API key will not drive a widget.

Every endpoint​

The Scope column is what an administrator ticks on the key. A missing scope is a 403 naming it; a knowledge base outside the key's reach is a 404, the same as one that does not exist, so nobody can probe ids to learn what an account holds.

Finding and adding​

MethodPathScopeWhat it does
GET/knowledge-baseskb:readThe knowledge bases this key may use, id and name only.
POST/knowledge-bases/{kbId}/documentskb:writeSend a document in. multipart/form-data, 25 MB at most.
GET/knowledge-bases/{kbId}/ingest-jobs/{jobId}kb:readCheck an ingest job's progress.
GET/knowledge-bases/{kbId}/documentskb:readList the documents in a knowledge base.
GET/knowledge-bases/{kbId}/documents/{documentId}kb:readOne document's details.
DELETE/knowledge-bases/{kbId}/documents/{documentId}kb:deleteRemove a document.

Ingestion is asynchronous. The upload answers 202 with a document_id and a job_id, and the document becomes searchable when that job reaches INDEXED. Poll the ingest job rather than assuming the document can be queried at once. A job that ends FAILED carries the stage it stopped at and the error.

A document the key's integration may not open answers 403 access_denied, even though the knowledge base itself is within reach.

kb:delete is a scope of its own because removal requests usually arrive through your system rather than ours, so your system has to be able to act on them, and an administrator should agree to that on purpose.

What a delete does. The call answers 202 as soon as the removal is accepted. The document's status becomes deleting, everything indexed from it is removed in the background, and the status then reads deleted: from that point it contributes no passages or citations to any answer. The document's record stays, and the original uploaded file is not deleted by this call. To make the document searchable again, send it in again. Deleting a document that is already deleting or deleted is not an error.

Asking​

MethodPathScopeWhat it does
POST/knowledge-bases/{kbId}/querykb:queryAsk a question, get a grounded answer with citations.
POST/knowledge-bases/{kbId}/retrievekb:queryGet the relevant passages without an answer.

query writes an answer; retrieve returns the source passages, with their scores, and leaves the writing to you. Reach for retrieve when you have your own model, or when you want to show sources without generating an answer. top_k sets how many passages retrieve returns.

Both are charged one request each once they answer, including an answer that declines. Both can answer 402 when billing declines, or a retryable 503 while the knowledge base is re-indexing. Both count against the writes budget, 10 a second, because they are POST calls.

Conversations​

MethodPathScopeWhat it does
POST/knowledge-bases/{kbId}/conversationskb:queryStart a conversation.
POST/knowledge-bases/{kbId}/conversations/{id}/turnskb:querySend a message, get the reply.
GET/knowledge-bases/{kbId}/conversations/{id}kb:queryRead a conversation with its citations.
GET/knowledge-bases/{kbId}/conversationskb:queryList open conversations. Pass status=archived for closed ones.
DELETE/knowledge-bases/{kbId}/conversations/{id}kb:queryArchive it. There is no way to reopen it.

A conversation carries its own history: Elie assembles the earlier turns and feeds them into each answer, so you send only the new message. Sending the turns to query instead loses that context. Citations in a conversation are checked against current access every time you read it, so one you could see last week may be gone today if that document's access changed.

Turns are synchronous: the reply comes back in the response, and each one is charged. Two turns sent to the same conversation at once get a 409 for the second; a turn sent to an archived conversation gets a 422.

Conversations belong to the integration, not to your end users

A conversation is visible to every key of the integration that started it, and to nobody else: not other integrations in the same account, and not people signed into the workspace. If you serve many end users, keep your own mapping of your user to conversation_id. Elie has no concept of your end users.

Access​

MethodPathScopeWhat it does
PATCH/knowledge-bases/{kbId}/documents/{documentId}:accesskb:writeChange who can find a document.
GET/knowledge-bases/{kbId}/access-directorykb:readThe roles, departments and groups this knowledge base accepts.

Use access-directory to fill a picker. It returns only the people this knowledge base already grants, so you are never offered one that could not reach the document anyway.

An access change is not immediate

:access replaces the document's whole access set and returns a job_id. The change takes effect when that job completes, because every stored passage of the document has to be rewritten. Don't read the document back expecting the new access to apply yet.

Ingesting a document​

curl -X POST "$BASE/external/v1/kb/knowledge-bases/$KB/documents" \
-H "Authorization: Bearer $PASS" \
-H "Idempotency-Key: $(uuidgen)" \
-F "file=@handbook.pdf" \
-F "visibility=internal" \
-F "access_role=role_abc"

name overrides the stored filename. visibility is public (everyone who can open the knowledge base) or internal (narrowed by the access_role, department and people_group fields you send, each of which you can repeat). As with every upload, Idempotency-Key makes a retry safe: the same key and the same file replay the original answer instead of adding a second copy.

On an internal document, a reader reaches it by matching any one of the three, and one you leave blank does not narrow anything. So sending access_role=role_abc and nothing else means anyone with that role, not only people who match all three.

An internal document with no access fields reaches almost no one

visibility defaults to internal, so this covers a bare upload with no access fields at all.

With no access_role, no department and no people_group, the document is accepted and indexed and then reaches only two audiences: the integration that uploaded it, and tenant administrators. Everyone else in the workspace is excluded.

That is easy to miss, because your own queries still find it. The document answers your tests perfectly while the colleagues it was meant for see nothing. Send at least one access field, or send visibility=public if everyone who can open the knowledge base should reach it.

Asking a question​

curl -X POST "$BASE/external/v1/kb/knowledge-bases/$KB/query" \
-H "Authorization: Bearer $PASS" \
-H "Content-Type: application/json" \
-d '{"question":"What is the returns window?"}'
{
"data": {
"answer": "Returns are accepted within 30 days.",
"citations": [
{ "index": 1, "document_id": "doc_01H8XK3M9P", "knowledge_base_id": "kb_01H8XK3M9P", "source_span": null }
],
"provenance": "generated",
"abstained": false
},
"meta": { "version": "v1" }
}

abstained set to true means Elie declined to answer because the knowledge base did not support one. That is a successful call, not an error, and it is the behaviour you want: declining beats inventing. Handle it explicitly rather than showing an empty string.

What is not here, and will not be​

No endpoint creates, renames or deletes a knowledge base, changes its settings, edits who can open it, connects a source, or manages curated question and answer pairs. No scope unlocks any of it: that is workspace configuration, and it stays where a person can see what they are changing.

Full schemas​

Field-level request and response shapes are in the workspace: open Developers, then API explorer. The API reference there is generated from the running service, so it cannot drift. This page is the capability reference: what exists, what each call needs, and where the surprises are.

Last updated on Something wrong with this page?