API reference / Knowledge base

Knowledge base

The Knowledge base endpoints of the Voiceland AI API, with parameters, schemas and curl examples.

Last updated:

GET /v1/kb/collections#

List collections. Every collection on your account, with its document and chunk counts.

Responses

Code Description
200 Success.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl "https://api.voiceland.ai/v1/kb/collections" \
  -H "Authorization: Bearer VL_API_KEY"

Example response

{
  "items": [
    {
      "doc_count": 12,
      "id": "col_9f2a1c7d",
      "name": "Τιμοκατάλογος",
      "vector_count": 143
    }
  ]
}

POST /v1/kb/collections#

Create a collection. Creates a collection, a named set of documents your agents can answer from. Collections belong to your account, not to one agent: any number of agents may attach the same collection, and attaching never changes it. Supply id to make the call repeatable (a second create with the same id returns 409), or omit it for a generated one.

Request body

application/json Schema: KbCollectionRequest

Field Type Required Description
default_lang string
description string
id string
name string Yes

Responses

Code Description
201 Created.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
409 The collection id is already taken (collection_exists), or your plan's collection allowance is used up (kb_collection_limit).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl -X POST "https://api.voiceland.ai/v1/kb/collections" \
  -H "Authorization: Bearer VL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "default_lang": "el",
  "description": "Price list and billing rules.",
  "name": "Τιμοκατάλογος"
}'

A successful response returns a KnowledgeCollection.

Example response

{
  "default_lang": "el",
  "doc_count": 0,
  "id": "col_9f2a1c7d",
  "name": "Τιμοκατάλογος",
  "vector_count": 0
}

DELETE /v1/kb/collections/{id}#

Delete a collection. Deletes an empty collection. A collection that still holds documents returns 409, pass ?cascade=true to delete its documents and remove them from the search index in the same call.

Parameters

Name In Type Required Description
id path string Yes Collection id.
cascade query boolean Also delete every document in the collection.

Responses

Code Description
204 Deleted.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
409 The collection still holds documents and cascade was not set (collection_not_empty).
4XX Request error (validation, not-found, etc.).
501 The collection holds indexed documents and this deployment has no search index, so the cascade cannot remove their passages (vector_store_unavailable).
502 The search index could not be reached (vector_store_error), or passages could not be removed from it (kb_purge_failed). The request made no change; retry the call.
5XX Server or upstream error.

Example request

curl -X DELETE "https://api.voiceland.ai/v1/kb/collections/{id}" \
  -H "Authorization: Bearer VL_API_KEY"

GET /v1/kb/collections/{id}#

Get a collection. Returns one collection by id.

Parameters

Name In Type Required Description
id path string Yes Collection id.

Responses

Code Description
200 Success.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl "https://api.voiceland.ai/v1/kb/collections/{id}" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KnowledgeCollection.

Example response

{
  "doc_count": 12,
  "id": "col_9f2a1c7d",
  "name": "Τιμοκατάλογος"
}

PATCH /v1/kb/collections/{id}#

Update a collection. Changes the name, description, or default language. Omitted fields are left alone; the documents inside are untouched.

Parameters

Name In Type Required Description
id path string Yes Collection id.

Request body

application/json Schema: KbCollectionPatchRequest

Field Type Required Description
default_lang string
description string
name string

Responses

Code Description
200 Success.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl -X PATCH "https://api.voiceland.ai/v1/kb/collections/{id}" \
  -H "Authorization: Bearer VL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "description": "Τιμές και εκπτώσεις 2026"
}'

A successful response returns a KnowledgeCollection.

Example response

{
  "description": "Τιμές και εκπτώσεις 2026",
  "id": "col_9f2a1c7d"
}

GET /v1/kb/collections/{id}/documents#

List documents. One page of the documents in a collection, in id order. The response omits each document's body, fetch a single document for its text. **Paging.** Pass the next_cursor from the previous response back as ?cursor= to get the page after it. **An empty next_cursor is the only signal that you have seen everything**, a short page, including an empty one, is normal and means "ask again": status is applied after each read, so a page can legitimately match nothing and still have more behind it. Follow the cursor until it comes back empty and your account's whole collection is covered, however large it is. Ordering is by document id within the collection, not by when a document was last edited: a page cannot sort documents it has not read, and a listing that claimed an order it could not keep across pages would repeat and skip documents.

Parameters

Name In Type Required Description
id path string Yes Collection id.
status query string Filter by lifecycle state: draft, in_review, published, archived.
audience query string Only documents that declare this audience. Matched against the stored, lower-cased value, so the case you send does not matter. A document that declares no audience is not returned, absent is not "every audience".
source_authority query string Only documents with this authority: official, verified, community or unverified. A document that declares none is not returned. Anything outside the four is a 400 rather than an empty page, so a typo cannot read as "you have no official documents".
limit query integer Documents per page, 1 to 100 (default 50). Larger values are capped rather than refused, keep following next_cursor for the rest.
cursor query string The next_cursor from the previous page. Omit for the first page. A cursor belongs to the collection that issued it.

Responses

Code Description
200 Success.
400 status is not one of the four lifecycle states, source_authority is not one of the four authority levels, or limit is not a positive whole number (bad_request); or cursor was not issued by this listing for this collection (invalid_cursor), start again without it.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such collection on your account (collection_not_found).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl "https://api.voiceland.ai/v1/kb/collections/{id}/documents" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KbDocumentsResponse.

Example response

{
  "items": [
    {
      "chunk_count": 7,
      "id": "doc_4b1e77a0",
      "status": "published",
      "title": "Τιμολόγηση 2026",
      "version": "1.1.0"
    }
  ],
  "next_cursor": "Y29sXzlmMmExYzdkI2RvY180YjFlNzdhMA"
}

POST /v1/kb/collections/{id}/documents#

Add a document. Adds a document to the collection. Five shapes are accepted on this one route: - **inline text**, JSON with title + body. Indexed synchronously; responds **201** with the document. - **file upload**, multipart/form-data with a file part (PDF, DOCX, HTML, Markdown, TXT, CSV) plus optional title, lang, tags, categories, audience, source_authority fields. Send the fields BEFORE the file. - **a web page**, JSON {"url": "https://example.com"}. The page is fetched, extracted and indexed. - **your website**, JSON {"website": "https://example.com"}, and the one to reach for. You do not have to know where your sitemap lives: we read your robots.txt, then try the conventional paths, and crawl the sitemap we find. A site that publishes none is crawled by FOLLOWING ITS LINKS instead, same result, one document made of many pages. A deep link is reduced to the site it belongs to, so pasting the page you had open works. The **202** carries a discovery block saying which sitemap was found and where it was declared, or, when there was none, which addresses were tried and that links will be followed. Takes the same optional scope as a sitemap. - **a sitemap**, JSON {"sitemap": "https://example.com/sitemap.xml"} with an optional scope (allow/deny path prefixes, max_pages). **Leave max_pages out and you get every page**, there is no default cap; a limit applies when you state one or when your plan sets one, and the job says when it stopped short and why. The whole crawl becomes ONE document made of many pages, so each answer still links to the exact page it came from. Add scope.recrawl_interval ("24h", between 1h and 8760h, one hour to one year) to have the source re-fetched on a timer, the re-fetch is conditional, so a page that has not changed costs you nothing, and the schedule only runs while the document is published. It applies to a single url as well as to a sitemap. You send it as a duration string; the document reports it back as a number of nanoseconds. Everything except inline text responds **202** with the draft document AND an ingestion job, poll GET /kb/jobs/{jobID} for progress. Every shape starts as a draft at version 1.0.0 and is NOT searchable until you publish it. **Standardised taxonomy.** Beside the free-form tags and categories, every shape accepts two typed fields that search and the document listing can narrow by: - audience, who the document is written for (["customers", "partners"], «συνεργάτες»). Your own vocabulary, in any language, but it is stored lower-cased with runs of spaces collapsed and matched EXACTLY, so pick the words once and reuse them. At most 8 values, 64 characters each. Nothing is enforced; if you have no opinion of your own, these are the words we suggest so that two collections agree: customers, prospects, partners, employees, agents, public, internal. - source_authority, how much weight the document carries: official (your own published position), verified (third-party material somebody checked), community (written by users), unverified (explicitly not checked). Nothing else is accepted. **Leaving either one out is not a value.** A document with no source_authority has made no claim, and unverified says the opposite, that somebody looked. Filters honour the difference: narrowing a search to unverified returns the documents marked so, never the ones nobody has labelled. Nothing on the platform fills these in for you. If you have been keeping the same information in metadata (metadata.audience and the like), it stays exactly where it is and keeps working, it is not read as a fallback and nothing was migrated, because a value nobody validated should not silently become a retrieval filter. Move it across when you are ready, one document at a time.

Parameters

Name In Type Required Description
id path string Yes Collection id.

Request body

application/json Schema: KbDocumentRequest

Field Type Required Description
audience array of string
body string
categories array of string
effective_from string
effective_until string
id string
lang string
metadata map of string
scope KbCrawlScope
sitemap string
source_authority string
tags array of string
title string Yes
url string
website string

Request body

multipart/form-data

Responses

Code Description
201 Created as a draft (inline text).
202 Accepted for ingestion (upload, url or sitemap).
400 The address could not be parsed or is not http(s) (invalid_url); the address resolves somewhere we will not fetch from (url_not_allowed); scope.recrawl_interval is not a duration or is outside the 1h, 8760h range (recrawl_interval_out_of_range); effective_from/effective_until could not be parsed as RFC3339 or does not close after it opens (bad_request); or a platform-managed metadata key was set, anything starting crawl_ or vl_, plus filename and content_type (reserved_metadata_key).
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
409 A quota was reached, documents on the account (kb_document_limit), upload size (kb_upload_limit), or pages per crawl (kb_crawl_page_limit), the document id is already taken by another document anywhere on your account (document_exists), or that document already has an ingestion job in flight (kb_job_in_flight), so wait for it to finish or cancel it.
413 The request body exceeds the accepted size (body_too_large).
415 The uploaded file is not one of the supported document types (unsupported_document_type).
4XX Request error (validation, not-found, etc.).
501 This deployment has no document storage (document_storage_unavailable) or no search index (vector_store_unavailable) configured; inline documents still work.
502 The stored copy of your file or crawled pages could not be read or written (document_storage_error). The request made no change; retry the call.
5XX Server or upstream error.

Example request

curl -X POST "https://api.voiceland.ai/v1/kb/collections/{id}/documents" \
  -H "Authorization: Bearer VL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "body": "# Τιμολόγηση\n\nΗ χρέωση γίνεται ανά λεπτό ομιλίας…",
  "categories": [
    "Τιμολόγηση/Εκπτώσεις"
  ],
  "lang": "el",
  "tags": [
    "pricing"
  ],
  "title": "Τιμολόγηση 2026"
}'

A successful response returns a KnowledgeDocument.

Example response

{
  "collection_id": "col_9f2a1c7d",
  "id": "doc_4b1e77a0",
  "status": "draft",
  "version": "1.0.0"
}

DELETE /v1/kb/documents/{docID}#

Delete a document. Deletes the document and everything derived from it, in order: any ingestion still running is cancelled, its passages are removed from the search index, the stored copy of your file or crawled pages is erased, and then the document itself. Permanent, archive instead if you may want the text back.

Parameters

Name In Type Required Description
docID path string Yes Document id.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Responses

Code Description
204 Deleted.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such document on your account (document_not_found).
4XX Request error (validation, not-found, etc.).
501 The document has passages in search and this deployment has no search index, so they cannot be removed (vector_store_unavailable). Deleting a never-indexed document is unaffected.
502 The search index could not be reached (vector_store_error), or passages could not be removed from it (kb_purge_failed). The stored copy of your file or crawled pages could not be read or written (document_storage_error). The request made no change; retry the call.
5XX Server or upstream error.

Example request

curl -X DELETE "https://api.voiceland.ai/v1/kb/documents/{docID}" \
  -H "Authorization: Bearer VL_API_KEY"

GET /v1/kb/documents/{docID}#

Get a document. Returns one document including its full text. A document with a re-crawl schedule also carries recrawl_state: last_crawled_at and next_due_at. Read the ABSENCE of next_due_at beside a non-zero recrawl_interval as meaningful rather than as an error, only a published document is ever re-fetched, so that combination is the usual answer to "I set a schedule and nothing is happening". The listing does not carry it; ask for the document.

Parameters

Name In Type Required Description
docID path string Yes Document id.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Responses

Code Description
200 Success.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such document on your account (document_not_found).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl "https://api.voiceland.ai/v1/kb/documents/{docID}" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KnowledgeDocument.

Example response

{
  "id": "doc_4b1e77a0",
  "status": "published",
  "title": "Τιμολόγηση 2026",
  "version": "1.1.0"
}

PATCH /v1/kb/documents/{docID}#

Update a document. Edits the title, text, language, tags, categories, audience, source authority, metadata, validity window or re-crawl schedule. **Sending body is a redaction, and it is destructive.** The text you send becomes the whole content of the document, and everything derived from the old text is destroyed rather than left to reappear: - the passages already in search are removed, so the old text stops being answerable the moment you save; - a published document drops back to draft, publish again to make the new text live. A draft stays a draft, and editing anything other than body leaves a published document published; - **on an uploaded or crawled document, the stored copy of your file or crawled pages is erased, any ingestion still running on it is cancelled, and the document's source becomes inline.** From then on the text on the row is the only content of record: re-index and publish rebuild from what you sent, never from the file you originally uploaded or the page we crawled. source.url is kept as provenance for citations, but nothing re-fetches it. That last point is the difference between an edit and a redaction, and it is one-way. If you need the original bytes back, upload them again as a new document. If you only meant to correct a typo in a file you still want us to hold, edit the file and re-upload it instead. **Moving the validity window also takes a published document out of search**, and for the same reason a text edit does: the window is stamped into every passage when you publish, so a row whose dates moved while its passages still carry the old ones is a document that answers on a day it should not. It is a NARROWER action than a body edit, nothing stored is destroyed and the source is not flipped to inline, the document simply drops back to draft, and publishing again re-stamps the passages. Send "" on either side to clear it. recrawl_interval sets or clears the schedule (a Go duration, "24h", between 1h and 8760h, one hour to one year; "" stops it). It applies to documents whose source is a url or a sitemap, and only a published one is ever re-fetched. audience and source_authority are the standardised taxonomy, see the create call for what the values mean. Send "audience": [] or "source_authority": "" to take a claim back off a document; there is no word in the vocabulary for "we have not said", so clearing is how you say it. **Re-labelling does NOT take a published document out of search**, it is a label, not content, and the same is true of tags and categories. The passages already in search keep the labels they were published with until you publish again, so a search narrowed by the new label finds the document only after the next publish. metadata replaces the map you own. Keys the platform manages, anything starting crawl_ or vl_, plus filename and content_type, cannot be set and are preserved across the replacement (400 reserved_metadata_key if you send one). The same rule applies when you CREATE a document. metadata.audience and friends are NOT reserved and are NOT read as a fallback for the typed fields: if you have been keeping this information there it keeps working untouched.

Parameters

Name In Type Required Description
docID path string Yes Document id.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Request body

application/json Schema: KbDocumentPatchRequest

Field Type Required Description
audience array of string
body string
categories array of string
effective_from string
effective_until string
lang string
metadata map of string
recrawl_interval string
source_authority string
tags array of string
title string

Responses

Code Description
200 Success.
400 A platform-managed metadata key was supplied (reserved_metadata_key); effective_from/effective_until could not be parsed as RFC3339 or from is not before until (bad_request); or recrawl_interval is not a duration, is outside 1h, 8760h (recrawl_interval_out_of_range), or names a document with no address to fetch again (recrawl_not_applicable); or the new primary lang is already held by one of the document's translations (primary_language_conflict), delete that variant first, or the row would carry the same language twice.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such document on your account, or it was deleted while the edit was in flight (document_not_found). The edit is discarded rather than recreating the document.
4XX Request error (validation, not-found, etc.).
501 The edit takes a published document out of search and this deployment has no search index, so its passages cannot be removed (vector_store_unavailable); or it redacts an uploaded document and this deployment has no document storage, so the stored bytes cannot be erased (document_storage_unavailable). Editing an unpublished inline document is unaffected.
502 The search index could not be reached (vector_store_error), or passages could not be removed from it (kb_purge_failed). The stored copy of your file or crawled pages could not be read or written (document_storage_error). The request made no change; retry the call.
5XX Server or upstream error.

Example request

curl -X PATCH "https://api.voiceland.ai/v1/kb/documents/{docID}" \
  -H "Authorization: Bearer VL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "body": "# Τιμολόγηση\n\nΑναθεωρημένες τιμές…"
}'

A successful response returns a KnowledgeDocument.

Example response

{
  "id": "doc_4b1e77a0",
  "source": {
    "kind": "inline"
  },
  "status": "draft",
  "version": "1.1.0"
}

POST /v1/kb/documents/{docID}/archive#

Archive a document. Takes the document out of search while keeping the text and its history. Safe to repeat. Publish again to bring it back. The passages are removed from the index before the document is marked archived, and the removal is attempted whether or not the document is recorded as indexed, so an archive can only ever end with less in search than it started with, never more.

Parameters

Name In Type Required Description
docID path string Yes Document id.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Responses

Code Description
200 Success.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
403 Signed in as a console user without the account_admin role (role_required). API keys are unaffected.
404 No such document on your account, or it was deleted while the archive was in flight (document_not_found).
4XX Request error (validation, not-found, etc.).
501 The document has passages in search and this deployment has no search index, so they cannot be removed (vector_store_unavailable). Archiving a never-indexed document is unaffected.
502 The search index could not be reached (vector_store_error), or passages could not be removed from it (kb_purge_failed). The request made no change; retry the call.
5XX Server or upstream error.

Example request

curl -X POST "https://api.voiceland.ai/v1/kb/documents/{docID}/archive" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KnowledgeDocument.

Example response

{
  "chunk_count": 0,
  "id": "doc_4b1e77a0",
  "status": "archived"
}

GET /v1/kb/documents/{docID}/chunks#

Preview passages. Shows how the document splits into retrievable passages, with the heading each one sits under. count is what the current text would produce; indexed_count is what search actually holds, they differ when the text was edited after the last publish.

Parameters

Name In Type Required Description
docID path string Yes Document id.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Responses

Code Description
200 Success.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such document on your account (document_not_found).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl "https://api.voiceland.ai/v1/kb/documents/{docID}/chunks" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KbChunksResponse.

Example response

{
  "count": 7,
  "document_id": "doc_4b1e77a0",
  "indexed_count": 7,
  "items": [
    {
      "chunk_id": "doc_4b1e77a0#0",
      "index": 0,
      "section": "Τιμολόγηση",
      "text": "Η χρέωση γίνεται ανά λεπτό ομιλίας…"
    }
  ],
  "status": "published",
  "version": "1.1.0"
}

POST /v1/kb/documents/{docID}/publish#

Publish a document. Makes the document answerable: the text is split into passages, indexed for search, and the version's minor number is bumped so an answer can name the exact revision it used. Safe to repeat, republishing replaces the previous passages rather than duplicating them. Publishable from any state: a draft, a document **in review** (this is the approval step), a published one you are re-indexing, or an archived one you are bringing back. An inline document is indexed inside the call (**200**). An uploaded or crawled one cannot be, its content is not on the row, so publishing it responds **202** with {document, job} and the passages appear once the job reports indexed.

Parameters

Name In Type Required Description
docID path string Yes Document id.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Responses

Code Description
200 Success.
202 Queued for indexing (uploaded or crawled document).
400 The text produces no retrievable passages (document_empty), or it splits into more passages than one document may hold (document_too_large), split it into several documents.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
403 Signed in as a console user without the account_admin role (role_required). API keys are unaffected.
404 No such document on your account, or it was deleted while the publish was in flight (document_not_found). Any passages already written are taken back out, so a deleted document is never resurrected by a publish that raced it.
409 That document already has an ingestion job in flight (kb_job_in_flight), wait for it to finish or cancel it.
4XX Request error (validation, not-found, etc.).
501 Publishing needs a search index and this deployment has none configured (vector_store_unavailable).
502 The text could not be turned into vectors by the embedding provider (embed_failed). The search index could not be reached (vector_store_error), or passages could not be removed from it (kb_purge_failed). The request made no change; retry the call.
5XX Server or upstream error.

Example request

curl -X POST "https://api.voiceland.ai/v1/kb/documents/{docID}/publish" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KnowledgeDocument.

Example response

{
  "chunk_count": 7,
  "id": "doc_4b1e77a0",
  "status": "published",
  "version": "1.1.0"
}

POST /v1/kb/documents/{docID}/recrawl#

Fetch this document's source again. Goes back to the address this document came from and reads it again, because the PAGE changed, not because we did. **This is the other half of re-index, and the difference matters.** /reindex rebuilds passages from the copy we already keep, so it is right when our chunking or embedding model improved and useless when your website changed. /recrawl is the opposite: it re-fetches, and the fetch is conditional, a page that has not changed costs you nothing. The kind of walk is the document's own: a document built from a sitemap re-walks that sitemap, one built by following links walks them again, and a single page re-fetches that page. You do not choose, and cannot: it is what the document is made of. Refused with **400** not_a_remote_document when there is no address to go back to, text you typed, a file you uploaded, a bucket import. One job at a time per document, because this one reaches out to somebody's web server.

Parameters

Name In Type Required Description
docID path string Yes Document id.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Responses

Code Description
202 Queued.
400 This document has no address to fetch again (not_a_remote_document).
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such document on your account (document_not_found).
409 An ingestion job is already running on it (kb_job_in_flight).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl -X POST "https://api.voiceland.ai/v1/kb/documents/{docID}/recrawl" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KbIngestResponse.

Example response

{
  "document": {
    "id": "doc_4b1e77a0",
    "status": "published"
  },
  "job": {
    "id": "job_1c4f9ab2",
    "kind": "sitemap",
    "status": "queued"
  }
}

POST /v1/kb/documents/{docID}/reindex#

Re-index a document. Queues the document to be extracted, split and embedded again, after the platform's chunking improves, or once you have corrected the collection's language. It re-reads the copy we already keep of your file or crawled pages, so re-indexing never hits your website again, which is exactly why it will NOT see a page you have edited. Use /recrawl for that.

Parameters

Name In Type Required Description
docID path string Yes Document id.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Responses

Code Description
202 Queued.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such document on your account (document_not_found).
409 That document already has an ingestion job in flight (kb_job_in_flight), wait for it to finish or cancel it.
4XX Request error (validation, not-found, etc.).
501 This deployment has no search index configured, so nothing can be re-indexed (vector_store_unavailable).
5XX Server or upstream error.

Example request

curl -X POST "https://api.voiceland.ai/v1/kb/documents/{docID}/reindex" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KbIngestResponse.

Example response

{
  "document": {
    "id": "doc_4b1e77a0",
    "status": "published"
  },
  "job": {
    "id": "job_1c4f9ab2",
    "kind": "reindex",
    "status": "queued"
  }
}

POST /v1/kb/documents/{docID}/review-flag/dismiss#

Dismiss a review flag. Closes a review flag by saying the document was not at fault. **A reason is required**: the flag exists because a human confirmed an answer was wrong, so a dismissal is a claim that the cause was somewhere else, and one with no reason attached is indistinguishable from clearing the queue. The other way out is to publish the document again, which closes the flag as resolved with no extra call: republishing states its case by changing the content. A decision is one-shot. What reopens a closed flag is a NEW confirmed-wrong report naming the same document.

Parameters

Name In Type Required Description
docID path string Yes Document id.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Request body

application/json Schema: KbReviewDismissRequest

Field Type Required Description
dismissed_by string
reason string Yes

Responses

Code Description
200 The closed flag.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
403 Signed in as a console user without the account_admin role (role_required). API keys are unaffected.
404 No such document on your account (document_not_found), or it carries no review flag (review_flag_not_found).
409 The flag is already closed (review_flag_closed).
422 No reason was given (reason_required).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl -X POST "https://api.voiceland.ai/v1/kb/documents/{docID}/review-flag/dismiss" \
  -H "Authorization: Bearer VL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "dismissed_by": "maria@acme.example",
  "reason": "The document is correct; the agent quoted the wrong section."
}'

A successful response returns a KnowledgeReviewFlag.

Example response

{
  "collection_id": "col_9f2a1c7d",
  "document_id": "doc_4b1e77a0",
  "report_count": 3,
  "resolution_note": "The document is correct; the agent quoted the wrong section.",
  "resolved_by": "maria@acme.example",
  "state": "dismissed"
}

POST /v1/kb/documents/{docID}/submit#

Submit a document for review. Marks a draft as ready for somebody to check before it goes live: the status becomes in_review. Nothing about search changes, a document in review is no more answerable than a draft, so this is a hand-off, not a publish. Only a **draft** can be submitted. Repeating the call on a document already in review succeeds and changes nothing. A **published** document cannot be sent back for review by this call: its passages are live in search, and a status change alone would leave them answering. Edit its text, that takes it out of search and returns it to draft, then submit. Archiving is not a route back: an archived document cannot be submitted either, and the only way out of archived is to publish it.

Parameters

Name In Type Required Description
docID path string Yes Document id.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Responses

Code Description
200 Success.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
403 Signed in as a console user without the account_admin role (role_required). API keys are unaffected.
404 No such document on your account (document_not_found).
409 The document is not in a state that can be submitted for review (invalid_transition), the message names the state it is in and the way out of it; or an ingestion job is still running on it (kb_job_in_flight), in which case the move would make that job throw its own work away, so wait for it to finish or cancel it. Repeating a move the document has already made is never refused, job or no job.
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl -X POST "https://api.voiceland.ai/v1/kb/documents/{docID}/submit" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KnowledgeDocument.

Example response

{
  "id": "doc_4b1e77a0",
  "status": "in_review",
  "version": "1.0.0"
}

GET /v1/kb/documents/{docID}/variants#

List a document's languages. Every language the document holds, the primary one first. The primary is in the list because search falls back to it, a language picker built from this list offers exactly the choices search can honour.

Parameters

Name In Type Required Description
docID path string Yes Document id.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Responses

Code Description
200 Success.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such document on your account (document_not_found).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl "https://api.voiceland.ai/v1/kb/documents/{docID}/variants" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KbVariantsResponse.

Example response

{
  "document_id": "doc_4b1e77a0",
  "items": [
    {
      "chunk_count": 7,
      "lang": "el",
      "primary": true
    },
    {
      "chunk_count": 6,
      "lang": "en",
      "primary": false
    }
  ],
  "primary_lang": "el"
}

POST /v1/kb/documents/{docID}/variants#

Add or replace a language version. Stores another language of the same document. A variant carries content only, the status, version, tags, categories and validity window stay on the document, so there is one thing to review and publish rather than one per language. Search prefers the language the person is searching in and falls back to the document's own, so a document with no Greek version still answers a Greek question, in the language it has. Adding a language to a **published** document takes it out of search until you publish again, exactly as editing its text does: what search holds is stamped when you publish, and quietly re-indexing inside this call would spend an embedding batch you did not ask for. Available for documents with inline text. A document ingested from a file, a page or a sitemap gets its language from its source.

Parameters

Name In Type Required Description
docID path string Yes Document id.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Request body

application/json Schema: KbVariantRequest

Field Type Required Description
body string Yes
lang string Yes Language tag for this version. It may not repeat the document's primary language.
title string

Responses

Code Description
200 Replaced an existing language.
201 Added.
400 lang is missing or is not a language tag, body is empty, the document is not an inline one (variants_not_supported), or this language's text splits into more passages than one document may hold (document_too_large), the cap is a TOTAL across every language, because the languages share one document.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such document on your account (document_not_found).
409 The document already carries the maximum number of languages, or this one repeats its primary language (variant_conflict).
413 This language's text is too large (body_too_large), the languages share one document.
4XX Request error (validation, not-found, etc.).
502 The search index could not be reached (vector_store_error), or passages could not be removed from it (kb_purge_failed). The request made no change; retry the call.
5XX Server or upstream error.

Example request

curl -X POST "https://api.voiceland.ai/v1/kb/documents/{docID}/variants" \
  -H "Authorization: Bearer VL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "body": "# Pricing\n\nBilling is per minute of speech…",
  "lang": "en",
  "title": "Pricing 2026"
}'

A successful response returns a KbVariantResponse.

DELETE /v1/kb/documents/{docID}/variants/{lang}#

Delete one language. Removes that language and its passages from search. The other languages are untouched, the removal names this language only. The primary language cannot be deleted this way; deleting the document removes it.

Parameters

Name In Type Required Description
docID path string Yes Document id.
lang path string Yes Language tag.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Responses

Code Description
204 Deleted.
400 That language is the document's primary one (primary_language).
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such document (document_not_found), or it has no version in that language (variant_not_found).
4XX Request error (validation, not-found, etc.).
502 The search index could not be reached (vector_store_error), or passages could not be removed from it (kb_purge_failed). The request made no change; retry the call.
5XX Server or upstream error.

Example request

curl -X DELETE "https://api.voiceland.ai/v1/kb/documents/{docID}/variants/{lang}" \
  -H "Authorization: Bearer VL_API_KEY"

GET /v1/kb/documents/{docID}/variants/{lang}#

Get one language. Returns that language's title and text, or 404 if the document does not have it. It does NOT fall back to the primary, a console asking "is there a Greek version" has to be able to get a no.

Parameters

Name In Type Required Description
docID path string Yes Document id.
lang path string Yes Language tag.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Responses

Code Description
200 Success.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such document (document_not_found), or it has no version in that language (variant_not_found).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl "https://api.voiceland.ai/v1/kb/documents/{docID}/variants/{lang}" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KbVariantResponse.

Example response

{
  "body": "# Pricing…",
  "chunk_count": 6,
  "lang": "en",
  "primary": false,
  "title": "Pricing 2026"
}

PATCH /v1/kb/documents/{docID}/variants/{lang}#

Update one language. Changes that language's title or text. The language itself cannot be changed, delete it and add the other one. Editing the primary language goes through PATCH /kb/documents/{docID} instead, which is the call that also removes the old text from search and from storage.

Parameters

Name In Type Required Description
docID path string Yes Document id.
lang path string Yes Language tag.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Request body

application/json Schema: KbVariantPatchRequest

Field Type Required Description
body string
title string

Responses

Code Description
200 Success.
400 That language is the document's primary one (primary_language), the document is not an inline one (variants_not_supported), or the new text splits into more passages than the document may hold across all its languages (document_too_large).
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such document (document_not_found), or it has no version in that language (variant_not_found).
4XX Request error (validation, not-found, etc.).
502 The search index could not be reached (vector_store_error), or passages could not be removed from it (kb_purge_failed). The request made no change; retry the call.
5XX Server or upstream error.

Example request

curl -X PATCH "https://api.voiceland.ai/v1/kb/documents/{docID}/variants/{lang}" \
  -H "Authorization: Bearer VL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "body": "# Pricing\n\nBilling is per minute…"
}'

A successful response returns a KbVariantResponse.

Example response

{
  "chunk_count": 0,
  "lang": "en",
  "primary": false
}

POST /v1/kb/documents/{docID}/withdraw#

Take a document out of review. Returns a document in review to draft, the same call whether the author is withdrawing it or a reviewer is sending it back. Nothing about search changes. Only a document **in review** can be withdrawn. Repeating the call on a draft succeeds and changes nothing. A published document is not withdrawn but archived.

Parameters

Name In Type Required Description
docID path string Yes Document id.
collection_id query string The document's collection. Optional: document ids are unique across your whole knowledge base, so we can always find the document without it. Supplying it, every listing and every create response gives it to you, saves one lookup.

Responses

Code Description
200 Success.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
403 Signed in as a console user without the account_admin role (role_required). API keys are unaffected.
404 No such document on your account (document_not_found).
409 The document is not in a state that can be withdrawn from review (invalid_transition), the message names the state it is in and the way out of it; or an ingestion job is still running on it (kb_job_in_flight), in which case the move would make that job throw its own work away, so wait for it to finish or cancel it. Repeating a move the document has already made is never refused, job or no job.
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl -X POST "https://api.voiceland.ai/v1/kb/documents/{docID}/withdraw" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KnowledgeDocument.

Example response

{
  "id": "doc_4b1e77a0",
  "status": "draft",
  "version": "1.0.0"
}

POST /v1/kb/import-inline#

Import an agent's inline knowledge. Turns the free-text knowledge you gave an agent into a knowledge-base document, so it can be searched, versioned, cited and shared with other agents instead of being re-sent to the model on every single turn. **Your agent is not changed.** The inline text stays exactly where it is and keeps working, so you can import, publish, attach, listen to the agent answer from search, and only then remove the inline copy by hand. **Safe to repeat.** The document id is derived from the agent name, so calling it again updates the same document, and if the text has not changed, nothing at all is written (changed: false). Set attach: true to also add the collection to the agent's knowledge sources. audience and source_authority set the standardised taxonomy (see the create call). Changing either one counts as a change, so a re-import that only re-labels the document is written, and, since it changes a label rather than the text, it does not take a published document out of search.

Request body

application/json Schema: KbImportInlineRequest

Field Type Required Description
agent string Yes The agent whose inline knowledge is read. The agent is NOT modified.
attach boolean Also add the collection to the agent's knowledge sources. Default false: attaching changes what a LIVE agent retrieves from.
audience array of string
categories array of string
collection_id string Yes
document_id string Overrides the id derived from the agent name, how you deliberately import one agent's knowledge into more than one document.
knowledge string Text to import instead of reading the agent's prompt. For a console that has the text before it has ever been saved, and for agents whose prompt was not composed here.
lang string
source_authority string
tags array of string
title string

Responses

Code Description
200 Success.
400 The agent has no inline knowledge to import (no_inline_knowledge), agent or collection_id is missing, or the document_id you supplied is not a valid id (bad_request); or the import would change the primary language of an existing document to one its translations already hold (primary_language_conflict).
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such agent (agent_not_found) or collection (collection_not_found).
409 Your plan's document allowance is used up (kb_document_limit), or the derived document id is taken by another collection (document_exists).
413 The inline text is larger than one document may be (body_too_large).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl -X POST "https://api.voiceland.ai/v1/kb/import-inline" \
  -H "Authorization: Bearer VL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "agent": "front-desk",
  "attach": true,
  "collection_id": "col_9f2a1c7d"
}'

A successful response returns a KbImportInlineResponse.

Example response

{
  "agent": "front-desk",
  "attached": true,
  "changed": true,
  "created": true,
  "document": {
    "id": "inline_front-desk",
    "status": "draft",
    "version": "1.0.0"
  },
  "source": "system_prompt"
}

GET /v1/kb/jobs#

List ingestion jobs. One page of your ingestion jobs, what is queued, running, indexed or failed. A failed job carries a one-line reason. Jobs are kept for 30 days; the document row is the lasting record. **Paging.** Pass the next_cursor from the previous response back as ?cursor= to get the page after it. **An empty next_cursor is the only signal that you have seen everything**, a short page, including an empty one, is normal and means "ask again": status is applied after each read, so a page can legitimately match nothing and still have more behind it. On this listing that is the ordinary case rather than the edge one, because asking for the failed jobs filters a history that is mostly queued ones. Follow the cursor until it comes back empty and your whole job history is covered, however much you have ingested. Ordering is by key, collection, then document, then job id, not by when a job was created: a page cannot sort jobs it has not read, and a listing that claimed an order it could not keep across pages would repeat and skip jobs. Sort a page yourself for an activity feed, or narrow to one document_id, whose whole history is a handful of rows.

Parameters

Name In Type Required Description
status query string Filter by status: queued, processing, indexed, failed, cancelled.
collection_id query string Only jobs for documents in this collection.
document_id query string Only jobs for this document. Requires collection_id, a job is filed under its document's collection, so jobs are listed within one collection rather than across your account. Every 202 that starts a job returns both ids.
limit query integer Jobs per page, 1 to 100 (default 50). Larger values are capped rather than refused, keep following next_cursor for the rest.
cursor query string The next_cursor from the previous page. Omit for the first page. A cursor belongs to the scope that issued it, one taken from a collection- or document-scoped listing is not valid on a wider one.

Responses

Code Description
200 Success.
400 status is not one of the five job states, document_id was sent without collection_id, or limit is not a positive whole number (bad_request); or cursor was not issued by this listing for this scope (invalid_cursor), start again without it.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl "https://api.voiceland.ai/v1/kb/jobs" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KbJobsResponse.

Example response

{
  "items": [
    {
      "attempts": 1,
      "document_id": "doc_4b1e77a0",
      "id": "job_1c4f9ab2",
      "kind": "sitemap",
      "progress": {
        "chunks_written": 96,
        "pages_done": 17,
        "pages_found": 42
      },
      "status": "processing"
    }
  ],
  "next_cursor": "Y29sXzlmMmExYzdkI2RvY180YjFlNzdhMCNqb2JfMWM0ZjlhYjI"
}

DELETE /v1/kb/jobs/{jobID}#

Cancel an ingestion job. Asks a queued or running job to stop. Best-effort: a job already writing its last passages may still finish, so the response returns the job so you can see which happened. A job that has already finished returns 409.

Parameters

Name In Type Required Description
jobID path string Yes Job id.
collection_id query string The job's collection. Give it together with document_id for a direct lookup.
document_id query string The job's document id.

Responses

Code Description
200 Success.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such job on your account (job_not_found).
409 The job has already finished and cannot be cancelled (job_already_finished).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl -X DELETE "https://api.voiceland.ai/v1/kb/jobs/{jobID}" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KnowledgeJob.

Example response

{
  "id": "job_1c4f9ab2",
  "status": "cancelled"
}

GET /v1/kb/jobs/{jobID}#

Get an ingestion job. One job with its live progress counters. Pass collection_id and document_id together when you have them (the 202 that created the job returned both) to make this a direct lookup instead of a walk.

Parameters

Name In Type Required Description
jobID path string Yes Job id.
collection_id query string The job's collection. Give it together with document_id for a direct lookup.
document_id query string The job's document id.

Responses

Code Description
200 Success.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such job on your account (job_not_found).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl "https://api.voiceland.ai/v1/kb/jobs/{jobID}" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KnowledgeJob.

Example response

{
  "document_id": "doc_4b1e77a0",
  "id": "job_1c4f9ab2",
  "kind": "upload",
  "progress": {
    "chunks_written": 12,
    "pages_done": 1,
    "pages_found": 1
  },
  "status": "indexed"
}

GET /v1/kb/object-sources#

List your object-storage sources. Every bucket you have registered. Secrets are never included, has_secret says one is stored, nothing returns its value.

Responses

Code Description
200 Success.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl "https://api.voiceland.ai/v1/kb/object-sources" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KbObjectSourcesResponse.

Example response

{
  "items": [
    {
      "access_key_id": "AKIA…",
      "bucket": "acme-docs",
      "endpoint": "s3.eu-central-1.amazonaws.com",
      "has_secret": true,
      "id": "objsrc_a1b2c3d4",
      "prefix": "handbook/"
    }
  ]
}

POST /v1/kb/object-sources#

Register your own object storage. Registers a bucket of yours, AWS S3 or anything that speaks the S3 API, so you can import documents straight out of it instead of uploading them one at a time. **`secret_access_key` is write-only.** It is stored encrypted and is never returned by this call, by the list, by the GET, or by the secrets API, there is no request that reads it back. `has_secret` is how you confirm one is stored. To change it, PATCH this source with a new `secret_access_key`; that is the only way to rotate it. **`endpoint` is a host, not a URL** (`s3.eu-central-1.amazonaws.com`, `objects.example.gr`). We always connect over https on port 443, because anything else would put your access key on the wire in the clear. An `https://example.com prefix is accepted and stripped; a path, a port we do not reach, credentials in the endpoint, or an address on a private network are refused (`endpoint_not_allowed`), and the same check runs again, against the address we actually connect to, every time an import runs. **Give us the narrowest credentials that work.** A read-only key scoped to `prefix` is all an import needs: we only ever LIST and GET. `prefix` is also a boundary, an import may narrow it, never widen it, so a source registered at `handbook/` can never be made to read the rest of the bucket.

Request body

application/json Schema: KbObjectSourceRequest

Field Type Required Description
access_key_id string Yes
bucket string Yes
endpoint string Yes
id string
name string
prefix string
region string
secret_access_key string Yes

Responses

Code Description
201 Registered.
400 bucket, access_key_id or secret_access_key is missing, or the id is not a valid id (bad_request); or the endpoint is not one we will connect to (endpoint_not_allowed), a private or link-local address, an internal-only name, a non-https scheme, a port other than 443, or the platform's own storage.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
409 A source with that id already exists (object_source_exists).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl -X POST "https://api.voiceland.ai/v1/kb/object-sources" \
  -H "Authorization: Bearer VL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "access_key_id": "AKIA…",
  "bucket": "acme-docs",
  "endpoint": "s3.eu-central-1.amazonaws.com",
  "name": "Handbook bucket",
  "prefix": "handbook/",
  "region": "eu-central-1",
  "secret_access_key": "…"
}'

A successful response returns a KbObjectSourceView.

Example response

{
  "access_key_id": "AKIA…",
  "bucket": "acme-docs",
  "endpoint": "s3.eu-central-1.amazonaws.com",
  "has_secret": true,
  "id": "objsrc_a1b2c3d4",
  "name": "Handbook bucket",
  "prefix": "handbook/",
  "region": "eu-central-1"
}

DELETE /v1/kb/object-sources/{sourceID}#

Remove an object-storage source. Removes the registration and erases the stored credential. **It refuses while documents imported from this source still exist** (409 object_source_in_use, naming one of them). That is deliberate: those documents are your indexed content, and deleting a piece of configuration must not destroy them. Delete the documents you no longer want first, deleting a document removes its passages and the copies of your files we kept, and then remove the source. Nothing in your own bucket is touched, ever. We only ever read from it.

Parameters

Name In Type Required Description
sourceID path string Yes Source id.

Responses

Code Description
204 Removed.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such source on your account (object_source_not_found).
409 A document imported from this source still exists (object_source_in_use).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl -X DELETE "https://api.voiceland.ai/v1/kb/object-sources/{sourceID}" \
  -H "Authorization: Bearer VL_API_KEY"

GET /v1/kb/object-sources/{sourceID}#

Get one object-storage source. The registration as stored. has_secret is checked against the stored credential on this call, so it is the honest answer to "is this source usable"; the secret itself is never returned.

Parameters

Name In Type Required Description
sourceID path string Yes Source id.

Responses

Code Description
200 Success.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such source on your account (object_source_not_found).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl "https://api.voiceland.ai/v1/kb/object-sources/{sourceID}" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KbObjectSourceView.

Example response

{
  "bucket": "acme-docs",
  "has_secret": true,
  "id": "objsrc_a1b2c3d4"
}

PATCH /v1/kb/object-sources/{sourceID}#

Update an object-storage source. Changes what is registered. Omitted fields are left alone. **This is how you rotate the credential**: send a new secret_access_key and it replaces the stored one. There is no way to clear it, a source with no credential cannot be imported from, and no way to read the old one back. Documents already imported from this source keep their content. The next import (or re-index that has to re-read the bucket) uses the new settings, so changing the endpoint or bucket redirects future reads without touching what is already indexed.

Parameters

Name In Type Required Description
sourceID path string Yes Source id.

Request body

application/json Schema: KbObjectSourcePatchRequest

Field Type Required Description
access_key_id string
bucket string
endpoint string
name string
prefix string
region string
secret_access_key string

Responses

Code Description
200 Success.
400 The new endpoint is not one we will connect to (endpoint_not_allowed), or access_key_id was sent empty (bad_request).
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
404 No such source on your account (object_source_not_found).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl -X PATCH "https://api.voiceland.ai/v1/kb/object-sources/{sourceID}" \
  -H "Authorization: Bearer VL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "secret_access_key": "…"
}'

A successful response returns a KbObjectSourceView.

Example response

{
  "bucket": "acme-docs",
  "has_secret": true,
  "id": "objsrc_a1b2c3d4"
}

POST /v1/kb/object-sources/{sourceID}/import#

Import from your object storage. Reads every object under the prefix and indexes them, through exactly the same extraction and chunking an upload goes through, PDFs, Word files, Markdown, HTML, CSV and plain text. **One import is one document**, made of every file under the prefix, the same way a sitemap crawl produces one document made of many pages. Each passage still carries the file it came from (s3://bucket/key) so an answer cites the exact file. If you want one document per file, import a narrower prefix per document. Responds **202** with {document, job}: the document exists immediately as a draft, and the files become searchable once the job reports indexed and you publish. Poll GET /kb/jobs/{id}. **Re-importing is cheap and safe, and it is how you pick up changes.** Call this again with the id of a document this same source already produced and it REFRESHES that document rather than making a new one: every file whose ETag has not changed is served from the copy we kept, not downloaded from your bucket, and not re-embedded, and when nothing under the prefix has changed at all the job finishes having written nothing. A refresh is not an edit: the title, the labels and the published status you set are left alone, and the prefix stays whatever that document was imported at (the request's prefix is ignored). Poll or schedule this as often as you like. POST /kb/documents/{id}/reindex is a different thing, it rebuilds passages from the copies we already hold and never looks at your bucket, so it will not see a new or edited file. Your plan's limits are the upload limits: the same per-file size cap, and a per-import ceiling on how many files one import reads. Hitting the ceiling is reported as a warning on the job rather than a failure, narrow the prefix and import the rest separately. audience and source_authority are the standardised taxonomy and mean exactly what they mean on the create call, a bucket of files is one document, so the labels are the document's, not the files'.

Parameters

Name In Type Required Description
sourceID path string Yes Source id.

Request body

application/json Schema: KbObjectImportRequest

Field Type Required Description
audience array of string
categories array of string
collection_id string Yes
id string
lang string
metadata map of string
prefix string
source_authority string
tags array of string
title string

Responses

Code Description
202 Accepted, the document exists as a draft and the job will fill it.
400 collection_id is missing or the document id is not a valid id (bad_request); the prefix is outside the source's own prefix (prefix_outside_source); a platform-managed metadata key was supplied (reserved_metadata_key); or the source's endpoint is no longer one we will connect to (endpoint_not_allowed).
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
403 Signed in as a console user without the account_admin role (role_required). API keys are unaffected.
404 No such source (object_source_not_found) or collection (collection_not_found).
409 Your plan's document allowance is used up (kb_document_limit); the document id belongs to something this source did not produce, another source's import, or a document you wrote or uploaded (document_exists), and re-importing would overwrite it; or that document already has an ingestion job in flight (kb_job_in_flight).
4XX Request error (validation, not-found, etc.).
501 Importing needs a search index and this deployment has none configured (vector_store_unavailable).
5XX Server or upstream error.

Example request

curl -X POST "https://api.voiceland.ai/v1/kb/object-sources/{sourceID}/import" \
  -H "Authorization: Bearer VL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "collection_id": "col_9f2a1c7d",
  "prefix": "handbook/policies/",
  "tags": [
    "hr"
  ],
  "title": "Employee handbook"
}'

A successful response returns a KbIngestResponse.

Example response

{
  "document": {
    "id": "doc_4b1e77a0",
    "source": {
      "kind": "object",
      "object_prefix": "handbook/policies/",
      "object_source_id": "objsrc_a1b2c3d4",
      "url": "s3://acme-docs/handbook/policies/"
    },
    "status": "draft"
  },
  "job": {
    "id": "job_7c1d",
    "kind": "object",
    "status": "queued"
  }
}

GET /v1/kb/review-flags#

List documents flagged for review. One page of the review queue: documents a human traced a wrong answer back to. A flag is raised when somebody resolves an inaccuracy report as confirmed_wrong and names the document that caused it (POST /feedback/reports/{id}/resolve), and it carries the ids of the reports behind it so an author can read why the document is suspect. **Ordering is by how many reports point at each document**, most first, then by collection and document id. That is a ranking rather than a key order, so the read is bounded: truncated: true means the ranking is over the flags examined and not over every flag on your account. Use collection_id to narrow it, or the analytics route for the whole picture. **Paging.** Pass the next_cursor from the previous response back as ?cursor=. **An empty next_cursor is the only signal that you have seen everything**: state is applied after each read, so a short page, including an empty one, is normal and means "ask again". A flag leaves the queue in one of two ways: publish the document again (the fix speaks for itself, and the flag closes as resolved), or dismiss it with a reason. A new confirmed-wrong report on a closed flag reopens it.

Parameters

Name In Type Required Description
state query string Filter by review state: open, resolved or dismissed. Anything else is a 400 rather than an empty page, so a typo cannot read as "nothing to review".
collection_id query string Only flags on documents in this collection. Also narrows the read, so a collection-scoped queue gets much further before it truncates.
limit query integer Flags per page, 1 to 100 (default 50). Larger values are capped rather than refused.
cursor query string The next_cursor from the previous page. Omit for the first page.

Responses

Code Description
200 Success.
400 state is not one of open/resolved/dismissed or limit is not a positive whole number (bad_request); or the cursor was not issued by this listing (invalid_cursor).
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl "https://api.voiceland.ai/v1/kb/review-flags" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KbReviewFlagsResponse.

Example response

{
  "items": [
    {
      "agent_name": "front-desk",
      "collection_id": "col_9f2a1c7d",
      "document_id": "doc_4b1e77a0",
      "reason": "confirmed_wrong_answer",
      "report_count": 3,
      "report_ids": [
        "fbr_9d8c7b6a",
        "fbr_5e4d3c2b",
        "fbr_1a2b3c4d"
      ],
      "state": "open"
    }
  ],
  "next_cursor": "",
  "truncated": false
}

GET /v1/kb/review-flags/analytics#

Which documents cause wrong answers. Ranks your documents by how many answers a human confirmed wrong and traced back to them. This is the number that says where your knowledge is actually wrong, as opposed to where the agent is. knowledge_fault_rate is the share of confirmed-wrong answers a reviewer traced to a document at all; linked_to_document beside confirmed_wrong is what makes that honest: a rate of zero means "nobody filled in which document", not "your knowledge is fine". It counts REPORTS, not open flags: a document you fixed last week still shows the answers it broke, because it broke them. The definition field ships the exact arithmetic. The read is bounded; truncated: true means there were more confirmed-wrong reports than one request folds.

Responses

Code Description
200 Success.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl "https://api.voiceland.ai/v1/kb/review-flags/analytics" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KbReviewAnalyticsResponse.

Example response

{
  "confirmed_wrong": 7,
  "documents": 2,
  "items": [
    {
      "agents": [
        "front-desk"
      ],
      "categories": [
        "outdated"
      ],
      "confirmed_wrong": 3,
      "document_id": "doc_4b1e77a0",
      "share": 0.6
    }
  ],
  "knowledge_fault_rate": 0.7142857142857143,
  "linked_to_document": 5,
  "scanned": 7,
  "truncated": false
}

POST /v1/kb/search#

Search your knowledge base. Searches your **published** documents and returns ranked passages with the matched words marked in a snippet. Natural-language questions work, the query is matched both by meaning and by word, so a Greek question finds a Greek document written with or without accents. filters.lang is the language of the person searching, not a hard filter: a document with no version in that language still answers, in the language it has. filters.collections, filters.tags, filters.categories, filters.audience and filters.source_authority narrow; nothing here can widen a search beyond your own published content, and a document outside its validity window never appears. filters.audience and filters.source_authority are the standardised taxonomy (see the create call). Both are lists and both match ANY of the values you send, so "source_authority": ["official", "verified"] is "trustworthy material only". **A document that declares neither is not returned by either filter**, absence is not a wildcard, and "nobody labelled this" must not be answerable as "this is official". An authority outside the four is a 400, not an empty result set. One thing to know about timing: the labels are stamped onto a document's passages when you PUBLISH it. Re-labelling an already-published document does not take it out of search, and it does not change what these filters match until you publish again. **This call is rate limited**, it embeds your query, which is a per-request cost, and the allowance comes from your plan. A refusal is 429 with retry-after; the current allowance is on every response as x-ratelimit-limit. A search that matches nothing is a normal 200 with outcome: "zero" and no results. Those queries are what the zero-result list in GET /me/search-stats is built from, they are the questions your customers asked that your content does not answer yet.

Request body

application/json Schema: KbSearchRequest

Field Type Required Description
filters KbSearchFilters
limit integer How many passages to return.
query string Yes What to search for. Natural-language questions work, the query is matched both by meaning and by word.
snippet_runes integer How long each snippet may be, in characters.

Responses

Code Description
200 Success.
400 query is empty, filters.source_authority names something outside official / verified / community / unverified (bad_request), or a filter list is longer than we accept.
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
413 The query is longer than we accept (query_too_large).
429 Too many searches for your plan's per-minute allowance (search_rate_limit). Wait retry-after seconds; the allowance itself is on every response as x-ratelimit-limit.
4XX Request error (validation, not-found, etc.).
501 This deployment has no search index configured (vector_store_unavailable).
502 The text could not be turned into vectors by the embedding provider (embed_failed). The search index could not be reached (vector_store_error). The request made no change; retry the call.
5XX Server or upstream error.

Example request

curl -X POST "https://api.voiceland.ai/v1/kb/search" \
  -H "Authorization: Bearer VL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "filters": {
    "audience": [
      "customers"
    ],
    "collections": [
      "col_9f2a1c7d"
    ],
    "lang": "el",
    "source_authority": [
      "official",
      "verified"
    ]
  },
  "limit": 5,
  "query": "πόσο κοστίζει το λεπτό ομιλίας;"
}'

A successful response returns a KbSearchResponse.

Example response

{
  "count": 1,
  "outcome": "hit",
  "query": "πόσο κοστίζει το λεπτό ομιλίας;",
  "results": [
    {
      "chunk_id": "doc_4b1e77a0#0",
      "collection_id": "col_9f2a1c7d",
      "document_id": "doc_4b1e77a0",
      "highlights": [
        {
          "end": 51,
          "start": 41
        },
        {
          "end": 66,
          "start": 52
        }
      ],
      "lang": "el",
      "rank": 1,
      "score": 0.0328,
      "section": "Τιμολόγηση",
      "snippet": "…Η χρέωση γίνεται ανά λεπτό ομιλίας…",
      "title": "Τιμολόγηση 2026"
    }
  ],
  "took_ms": 41
}

POST /v1/kb/search/selection#

Record that a result was opened. Counts a search result the person actually opened. It is what turns the search report's selection rate into a real number, without it, "how often does a search lead anywhere" cannot be answered. Send the query exactly as it was searched; we key the count by the query, not by the document.

Request body

application/json Schema: KbSelectionRequest

Field Type Required Description
document_id string The result that was opened. Recorded for context; the selection rate is per query.
query string Yes The query exactly as it was searched. The count is keyed by the query, not by the document.

Responses

Code Description
204 Counted.
400 query is empty (bad_request).
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
429 Too many calls for your plan's per-minute allowance (search_rate_limit). Wait retry-after seconds; the allowance itself is on every response as x-ratelimit-limit.
4XX Request error (validation, not-found, etc.).
501 This deployment has no search index configured (vector_store_unavailable).
5XX Server or upstream error.

Example request

curl -X POST "https://api.voiceland.ai/v1/kb/search/selection" \
  -H "Authorization: Bearer VL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "document_id": "doc_4b1e77a0",
  "query": "πόσο κοστίζει το λεπτό ομιλίας;"
}'

GET /v1/kb/suggest#

Autocomplete and did-you-mean. Completions for what someone is typing, drawn from your own documents' titles and headings, your tags and categories, and the searches that actually found something. When nothing completes the prefix, did_you_mean offers the nearest known word, accent-insensitive, in Greek and English. It answers from memory and never searches, so it is safe to call as the user types. The first call after a deployment can legitimately have nothing to say: ready: false means the word list for your account is still being built, and it is not an error.

Parameters

Name In Type Required Description
q query string What the user has typed so far.
lang query string The language they are typing in.
limit query integer How many suggestions to return.

Responses

Code Description
200 Success.
400 limit is not a positive whole number (bad_request).
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
413 q is longer than we accept (query_too_large).
429 Too many calls for your plan's per-minute allowance (search_rate_limit). Wait retry-after seconds; the allowance itself is on every response as x-ratelimit-limit.
4XX Request error (validation, not-found, etc.).
501 This deployment has no search index configured (vector_store_unavailable), or suggestions are switched off (suggest_unavailable).
5XX Server or upstream error.

Example request

curl "https://api.voiceland.ai/v1/kb/suggest" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KbSuggestResponse.

Example response

{
  "ready": true,
  "suggestions": [
    {
      "kind": "title",
      "score": 3,
      "text": "τιμοκατάλογος 2026"
    }
  ]
}

GET /v1/me/search-stats#

Search report. What people searched for, whether they found anything, and whether they opened it, per day, plus the top queries and the **zero-result list**. The zero-result list is the content-gap report: every question your customers asked that your published content could not answer. It is the shortest route from "the agent said it doesn't know" to knowing what to write next. Defaults to the last 30 days. truncated is true when the window held more distinct queries than one request reads, narrow the window rather than raising the limit.

Parameters

Name In Type Required Description
from query string Start of the window (2026-01-31, or RFC3339). Defaults to 30 days ago.
to query string End of the window. Defaults to now.
limit query integer How many queries to return in each list.

Responses

Code Description
200 Success.
400 from/to could not be parsed as a date or an RFC3339 instant, from is after to, or limit is not a positive whole number (bad_request); or the window is longer than we report on (window_too_large).
401 Missing or invalid API key.
402 Your plan does not include the knowledge base (feature_not_entitled).
4XX Request error (validation, not-found, etc.).
5XX Server or upstream error.

Example request

curl "https://api.voiceland.ai/v1/me/search-stats" \
  -H "Authorization: Bearer VL_API_KEY"

A successful response returns a KbSearchStatsResponse.

Example response

{
  "from": "2026-07-01",
  "to": "2026-07-31",
  "totals": {
    "hits": 1102,
    "queries": 1284,
    "selection_rate": 0.498,
    "selections": 640,
    "zero": 182,
    "zero_rate": 0.142
  },
  "zero_results": [
    {
      "count": 37,
      "outcome": "zero",
      "query": "επιστροφη χρηματων"
    }
  ]
}

Schemas#

Error#

Error envelope returned for non-2xx responses.

Field Type Required Description
error object Yes

KbChunkPreview#

Field Type Required Description
chunk_id string Yes
index integer Yes
section string
text string Yes

KbChunksResponse#

Field Type Required Description
count integer Yes Passages the CURRENT text would produce.
document_id string Yes
indexed_count integer Yes Passages the search index actually holds. The two disagree exactly when the text was edited after the last publish.
items array of KbChunkPreview Yes
status string Yes
version string

KbCollectionPatchRequest#

Field Type Required Description
default_lang string
description string
name string

KbCollectionRequest#

Field Type Required Description
default_lang string
description string
id string
name string Yes

KbCrawlScope#

Field Type Required Description
allow array of string
deny array of string
max_pages integer
recrawl_interval string
respect_robots boolean

KbDiscoveryResult#

Field Type Required Description
checked array of string
sitemap string
strategy string Yes
via string

KbDocumentPatchRequest#

Field Type Required Description
audience array of string
body string
categories array of string
effective_from string
effective_until string
lang string
metadata map of string
recrawl_interval string
source_authority string
tags array of string
title string

KbDocumentRequest#

Field Type Required Description
audience array of string
body string
categories array of string
effective_from string
effective_until string
id string
lang string
metadata map of string
scope KbCrawlScope
sitemap string
source_authority string
tags array of string
title string Yes
url string
website string

KbDocumentsResponse#

Field Type Required Description
items array of KnowledgeDocument Yes
next_cursor string Yes

KbImportInlineRequest#

Field Type Required Description
agent string Yes The agent whose inline knowledge is read. The agent is NOT modified.
attach boolean Also add the collection to the agent's knowledge sources. Default false: attaching changes what a LIVE agent retrieves from.
audience array of string
categories array of string
collection_id string Yes
document_id string Overrides the id derived from the agent name, how you deliberately import one agent's knowledge into more than one document.
knowledge string Text to import instead of reading the agent's prompt. For a console that has the text before it has ever been saved, and for agents whose prompt was not composed here.
lang string
source_authority string
tags array of string
title string

KbImportInlineResponse#

Field Type Required Description
agent string Yes
attached boolean Yes
changed boolean Yes False when the text was identical to what the document already held, in which case nothing was written at all, so polling this endpoint cannot take a published document offline.
created boolean Yes
document KnowledgeDocument One piece of knowledge. Only a published document inside its validity window is searchable.
source string Yes Where the text came from: the agent's system prompt, or the request.

KbIngestResponse#

Field Type Required Description
discovery KbDiscoveryResult
document KnowledgeDocument One piece of knowledge. Only a published document inside its validity window is searchable.
job KnowledgeJob

KbJobsResponse#

Field Type Required Description
items array of KnowledgeJob Yes
next_cursor string Yes

KbObjectImportRequest#

Field Type Required Description
audience array of string
categories array of string
collection_id string Yes
id string
lang string
metadata map of string
prefix string
source_authority string
tags array of string
title string

KbObjectSourcePatchRequest#

Field Type Required Description
access_key_id string
bucket string
endpoint string
name string
prefix string
region string
secret_access_key string

KbObjectSourceRequest#

Field Type Required Description
access_key_id string Yes
bucket string Yes
endpoint string Yes
id string
name string
prefix string
region string
secret_access_key string Yes

KbObjectSourceView#

Field Type Required Description
access_key_id string Yes
bucket string Yes
created_at string
endpoint string Yes
has_secret boolean Yes
id string Yes
name string
prefix string
region string
updated_at string
updated_by string

KbObjectSourcesResponse#

Field Type Required Description
items array of KbObjectSourceView Yes

KbReviewAnalyticsResponse#

Field Type Required Description
confirmed_wrong integer Yes
definition string Yes
documents integer Yes
items array of KnowledgeDocumentFaultRow Yes
knowledge_fault_rate number Yes
linked_to_document integer Yes
scanned integer Yes
truncated boolean Yes

KbReviewDismissRequest#

Field Type Required Description
dismissed_by string
reason string Yes

KbReviewFlagsResponse#

Field Type Required Description
items array of KnowledgeReviewFlag Yes
next_cursor string Yes
truncated boolean Yes

KbSearchFilters#

Field Type Required Description
audience array of string
categories array of string
collections array of string Narrow to these collections. Empty searches everything published on your account.
lang string The language of the person searching. NOT a hard filter: a document with no version in that language still answers, in the language it has. Omitting it means no preference.
source_authority array of string
tags array of string

KbSearchHighlight#

Field Type Required Description
end integer Yes
start integer Yes

KbSearchRequest#

Search your published knowledge.

Field Type Required Description
filters KbSearchFilters
limit integer How many passages to return.
query string Yes What to search for. Natural-language questions work, the query is matched both by meaning and by word.
snippet_runes integer How long each snippet may be, in characters.

KbSearchResponse#

Field Type Required Description
count integer Yes
outcome string Yes hit when something matched, zero when nothing did. A zero is a normal 200, those queries are what the zero-result report is built from.
query string Yes
results array of KbSearchResult Yes
took_ms integer Yes Server-side time for the search, in milliseconds.

KbSearchResult#

Field Type Required Description
chunk_id string Yes
collection_id string Yes
document_id string Yes
highlights array of KbSearchHighlight Byte ranges into snippet (not into text) where the query matched.
lang string
rank integer Yes
score number Yes Fused relevance. Comparable WITHIN one response and not across responses, do not build a threshold on it.
section string
snippet string Yes The passage trimmed around the match, for display.
text string The whole passage, as the model would see it.
title string
url string
version string The document revision this passage was indexed from.

KbSearchStatsDay#

Field Type Required Description
day string Yes
hits integer Yes
queries integer Yes
results integer Yes
selection_rate number Yes Selections divided by queries, for that day. Zero when nothing reports selections.
selections integer Yes
zero integer Yes
zero_rate number Yes

KbSearchStatsQuery#

Field Type Required Description
count integer Yes
last_seen_at string (date-time)
outcome string Yes
query string Yes
selections integer Yes

KbSearchStatsResponse#

Field Type Required Description
days array of KbSearchStatsDay Yes
from string Yes
to string Yes
top_queries array of KbSearchStatsQuery Yes
totals KbSearchStatsDay Yes The whole window summed.
truncated boolean Yes True when the window held more distinct queries than one request reads. Narrow the window rather than raising the limit.
zero_results array of KbSearchStatsQuery Yes The content-gap report: questions your customers asked that your published content could not answer.

KbSelectionRequest#

Field Type Required Description
document_id string The result that was opened. Recorded for context; the selection rate is per query.
query string Yes The query exactly as it was searched. The count is keyed by the query, not by the document.

KbSuggestResponse#

Field Type Required Description
built_at string (date-time)
did_you_mean string The nearest known word when nothing completed the prefix.
partial boolean True when the word list was built from a bounded scan rather than your whole corpus.
ready boolean Yes False while the word list for your account is still being built. Not an error, call again shortly.
stale boolean True when the word list is past its refresh age and a rebuild is under way.
suggestions array of Suggestion Yes

KbVariantPatchRequest#

Field Type Required Description
body string
title string

KbVariantRequest#

Field Type Required Description
body string Yes
lang string Yes Language tag for this version. It may not repeat the document's primary language.
title string

KbVariantResponse#

Field Type Required Description
body string
chunk_count integer Yes
lang string Yes
primary boolean Yes True for the document's own language, which lives on the document rather than in the variant map.
title string

KbVariantsResponse#

Field Type Required Description
document_id string Yes
items array of KbVariantResponse Yes
primary_lang string

KnowledgeCollection#

A shared container of documents. Agents attach collections by id; many agents may hold the same one, and attaching never writes to it.

Field Type Required Description
created_at string (date-time)
default_lang string Language assumed for documents in this collection that do not state one.
description string
doc_count integer Yes Documents in the collection, all lifecycle states included.
id string Yes
name string Yes
tenant_slug string Yes
updated_at string (date-time)
vector_count integer Yes Passages currently in the search index for this collection. It trails a publish or an archive by the length of that operation and is recounted on success, so a value that disagrees with the documents' chunk counts means the last write did not finish.

KnowledgeDocument#

One piece of knowledge. Only a published document inside its validity window is searchable.

Field Type Required Description
audience array of string
body string The text, for a document typed in directly. Empty for uploaded and crawled documents, their content of record is the stored file or the crawled pages, not this field.
categories array of string
chunk_count integer Yes Passages the last successful index wrote for the PRIMARY language. A multilingual document's total is the sum of this and each variant's own count.
collection_id string Yes
created_at string (date-time)
effective_from string (date-time) The document is not searchable before this instant, even while published. Absent means no lower bound.
effective_until string (date-time) The document stops being searchable at this instant, even while published, a price list that expires, a policy that lapses. Absent means no upper bound. Nothing writes when the moment arrives; every search checks the window, so an expired document simply stops appearing.
id string Yes
lang string
metadata map of string Your own key/value map. Keys the platform manages, anything starting crawl_ or vl_, plus filename and content_type, cannot be written and survive a replacement that omits them.
recrawl_interval integer How often the source is re-fetched, in NANOSECONDS. You set it as a duration string ("24h") and read it back as a number, 86400000000000 is one day. Zero means no schedule. Only a published document with an address is ever re-fetched, the re-fetch is conditional, and a source that has not changed costs nothing.
recrawl_state KnowledgeRecrawlState
review_flag KnowledgeReviewFlag
source KnowledgeSource Yes Where the content came from: typed in (inline), uploaded (upload) or fetched (url). Kept as provenance for citations after a redaction, even though nothing re-fetches it then.
source_authority string
stats KnowledgeUsageStats Retrieval counters for this document. Read-only, and present only where it was asked for: single-document reads and the document listing hydrate it, everything else leaves it out. Absent therefore means "not requested", never "never retrieved".
status string Yes draft, in_review, published or archived. Publishing indexes the text; archiving removes it from search while keeping it.
tags array of string
tenant_slug string Yes
title string Yes
updated_at string (date-time)
updated_by string
variants map of KnowledgeDocumentVariant Other languages of the same document, keyed by language tag. A variant carries content only, status, version, tags, categories and the validity window stay here on the document, so there is one thing to review and publish rather than one per language.
version string Semantic version, bumped on each publish so an answer can name the exact revision it used.

KnowledgeDocumentFaultRow#

Field Type Required Description
agents array of string
categories array of string
confirmed_wrong integer Yes
document_id string Yes
first_reported_at string (date-time)
last_reported_at string (date-time)
share number Yes

KnowledgeDocumentVariant#

One language of a document: title and text only.

Field Type Required Description
body string
chunk_count integer Yes Passages the last index wrote for THIS language.
lang string Yes
source KnowledgeSource Where the content came from: typed in (inline), uploaded (upload) or fetched (url). Kept as provenance for citations after a redaction, even though nothing re-fetches it then.
title string

KnowledgeJob#

Field Type Required Description
attempts integer Yes How many times this job has been picked up. A retryable failure schedules another attempt; the ladder is what bounds total work, not the page cap.
collection_id string Yes
created_at string (date-time)
document_id string
error string
finished_at string (date-time)
id string Yes
kind string Yes
lang string
lease_until string (date-time)
next_attempt_at string (date-time)
progress KnowledgeJobProgress Yes Live counters, pages found, pages done, pages skipped, passages written. They only move forward within one attempt and reset when an attempt restarts.
requested_by string
source KnowledgeSource Yes Where the content came from: typed in (inline), uploaded (upload) or fetched (url). Kept as provenance for citations after a redaction, even though nothing re-fetches it then.
started_at string (date-time)
status string Yes
tenant_slug string Yes

KnowledgeJobProgress#

Field Type Required Description
chunks_written integer Yes
pages_done integer Yes
pages_found integer Yes
pages_skipped integer Yes

KnowledgeRecrawlState#

Field Type Required Description
last_crawled_at string (date-time)
next_due_at string (date-time)

KnowledgeReviewFlag#

Field Type Required Description
agent_name string
collection_id string Yes
created_at string (date-time)
document_id string Yes
first_flagged_at string (date-time)
last_flagged_at string (date-time)
reason string Yes
reopened_at string (date-time)
report_count integer Yes
report_ids array of string
resolution_note string
resolved_at string (date-time)
resolved_by string
state string Yes
tenant_slug string Yes
updated_at string (date-time)

KnowledgeSource#

Where the content came from: typed in (inline), uploaded (upload) or fetched (url). Kept as provenance for citations after a redaction, even though nothing re-fetches it then.

Field Type Required Description
kind string Yes
object_prefix string
object_source_id string
s3_key string
url string

KnowledgeUsageStats#

Field Type Required Description
cited_count integer Yes How many answers actually cited it. Retrieved but never cited, over a long window, is a document search keeps finding and the model keeps not using.
cited_wrong_count integer Yes Reserved for explicit negative feedback. Nothing writes it yet, so it is always zero.
collection_id string Yes
document_id string
last_cited_at string (date-time)
last_retrieved_at string (date-time)
retrieved_count integer Yes How many searches and agent turns this document's passages were returned for.
tenant_slug string Yes
updated_at string (date-time)

Suggestion#

Field Type Required Description
kind string Yes
score number Yes
text string Yes

The console

These pages are read only. The test call, the API keys and the live API reference are in the console, where your account is signed in.

Open the console