Skip to main content
Knowledge bases provide semantic search over your document content. They store chunked and embedded text from one or more sources, enabling retrieval-augmented generation (RAG). When you add a source to a knowledge base, the platform automatically chunks the source’s page texts, generates vector embeddings, and stores them for similarity search.

Common Patterns

Create a knowledge base, add one or more sources to trigger indexing, then use the search endpoint to query. Check indexing status by fetching the knowledge base details. Reindex when you change chunking parameters or want to re-process sources.

GET /api/knowledge-bases

List all knowledge bases.
response = requests.get(f"{BASE_URL}/api/knowledge-bases", headers=headers)

POST /api/knowledge-bases

Create a new knowledge base. Only name is required. You can also pass indexing_config (how sources are chunked/embedded) and retrieval_config (how the KB is searched) — anything you supply is merged over the strategy’s defaults rather than replacing them. The retrieval-side features — reranking, query enrichment, and multimodal retrieval — all live inside retrieval_config and are persisted on the KB, so the minimal example and the fully-configured example below differ only in how much of that object you fill in.
{
  "name": "Product Docs",
  "description": "Product documentation"
}
retrieval_config fieldTypeNotes
methodstringvector_search / full_text / hybrid / tree_search.
top_kintResults returned after any reranking.
vector_weightfloatHybrid only — balance of semantic vs keyword (default 0.5).
context_modestringtext (default) or image for multimodal retrieval. All strategies except Doc2JSON.
rerankerobject{ model, candidate_count }. Present ⇒ reranking on; omit ⇒ off.
query_enrichmentobject{ enabled, model }. LLM query rewriting; enabled defaults to false.
All of these are editable later via PATCH (see below). Reranking, query enrichment, and context_mode take effect immediately on the next search — no reindex. Changing indexing_config (chunking, embedding model, strategy) requires a reindex to take effect. See Knowledge bases & indexing for the per-strategy indexing_config fields.
response = requests.post(f"{BASE_URL}/api/knowledge-bases", headers=headers, json={"name": "Product Docs"})

GET /api/knowledge-bases/

Get a knowledge base with its indexed sources and status.
id
string
required
KB ID
response = requests.get(f"{BASE_URL}/api/knowledge-bases/{kb_id}", headers=headers)

PATCH /api/knowledge-bases/

Update knowledge base configuration or strategy. Accepts the same name, description, indexing_config, and retrieval_config fields as create. Send the full retrieval_config object you want — it is stored as-is. Reranking, query enrichment, and context_mode changes apply to the next search immediately; indexing_config changes need a reindex to take effect.
id
string
required
KB ID
{
  "retrieval_config": {
    "method": "hybrid",
    "top_k": 5,
    "reranker": { "model": "voyage/rerank-2.5", "candidate_count": 30 },
    "query_enrichment": { "enabled": true }
  }
}
response = requests.patch(f"{BASE_URL}/api/knowledge-bases/{kb_id}", headers=headers, json={"description": "Updated"})

DELETE /api/knowledge-bases/

Delete a knowledge base and all its indexed data.
id
string
required
KB ID
response = requests.delete(f"{BASE_URL}/api/knowledge-bases/{kb_id}", headers=headers)

GET /api/knowledge-bases//sources

Paginated, filterable, sortable list of the sources indexed into a knowledge base. Each item joins the indexed_sources row with its underlying sources row, so you get both index status and source metadata in one response.
id
string
required
KB ID
q
string
Case-insensitive substring match on the source’s name.
status
string
Exact match on index_status (pending, indexing, indexed, failed, cancelled).
sort
string
name or created_at. When omitted, sorts failed-first then newest-source-first — useful for surfacing problems at the top of a UI list.
order
string
asc or desc (default desc). Ignored when sort is omitted.
limit
integer
Page size. Defaults to 50, capped at 200.
offset
integer
Skip the first N rows. Defaults to 0.
response = requests.get(f"{BASE_URL}/api/knowledge-bases/{kb_id}/sources", headers=headers, params={"status": "failed", "limit": 20})
print(response.json())
Response:
{
  "items": [
    {
      "id": "indexed-source-uuid",
      "source_id": "source-uuid",
      "index_status": "failed",
      "indexed_at": null,
      "stats": {},
      "error_message": "embedding provider rate-limited",
      "source_name": "manual.pdf",
      "file_type": "application/pdf",
      "source_created_at": "2026-01-01T00:00:00Z"
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0
}
id is the indexed_sources.id (use this with the cancel, reindex, and DELETE endpoints below). source_id is the underlying ai.sources row.

POST /api/knowledge-bases//sources

Add a source to the knowledge base. Triggers asynchronous indexing.
id
string
required
KB ID
{ "source_id": "source-uuid" }
response = requests.post(f"{BASE_URL}/api/knowledge-bases/{kb_id}/sources", headers=headers, json={"source_id": source_id})

POST /api/knowledge-bases//sources//cancel

Cancel an in-progress indexing task. Only pending and indexing rows can be cancelled — anything already indexed, failed, or cancelled returns 409. Returns 404 if no indexed_sources row matches the given pair.
id
string
required
KB ID
indexed_source_id
string
required
The indexed_sources.id returned when the source was added (not the source UUID itself).
response = requests.post(f"{BASE_URL}/api/knowledge-bases/{kb_id}/sources/{indexed_source_id}/cancel", headers=headers)

DELETE /api/knowledge-bases//sources/

Remove a source from a knowledge base. The underlying ai.sources row is not touched — only its link to this KB and everything the indexing pipeline produced from it. The source stays available to re-add to other KBs. Deleting the indexed_sources row cascades through all eight derivative tables: ai.chunks, ai.embeddings, ai.full_documents, ai.doc2json_documents, ai.page_index_toc, ai.page_index_nodes, ai.graph_index_toc, ai.graph_index_nodes. After the call, the source contributes nothing to retrieval in this KB. If the source is mid-indexing (status pending or indexing with a celery_task_id), the Celery task is revoked before the row is deleted. A revoke failure logs a warning but does not block the deletion — the row goes regardless. Returns 200 with a small JSON body, or 404 if no indexed_sources row matches the given (kb_id, indexed_source_id) pair.
id
string
required
KB ID
indexed_source_id
string
required
The indexed_sources.id returned when the source was added (not the source UUID itself).
requests.delete(f"{BASE_URL}/api/knowledge-bases/{kb_id}/sources/{indexed_source_id}", headers=headers)
Response:
{
  "message": "Source removed from knowledge base",
  "deleted_indexed_source_id": "indexed-source-uuid",
  "kb_id": "kb-uuid"
}

POST /api/knowledge-bases//reindex

Re-index sources in the knowledge base. The body is optional — empty body re-indexes every source. Pass indexed_source_ids to re-index a specific subset, or failed_only: true to retry only sources currently in failed status.
id
string
required
KB ID
indexed_source_ids
string[]
Restrict to specific indexed_sources.id values. If supplied, this wins — failed_only is ignored.
failed_only
boolean
When true (and indexed_source_ids is empty), re-index only sources currently in failed status. Returns {"status": "noop"} if there are none.
{ "indexed_source_ids": ["uuid-1", "uuid-2"] }
# Re-index everything
requests.post(f"{BASE_URL}/api/knowledge-bases/{kb_id}/reindex", headers=headers)

# Retry only failed sources
requests.post(f"{BASE_URL}/api/knowledge-bases/{kb_id}/reindex", headers=headers, json={"failed_only": True})

POST /api/knowledge-bases//build-bm25

Dispatch a one-shot BM25 rebuild for this KB. Re-tokenizes the entire item table for the KB’s strategy (chunks / full_documents / graph_index_nodes) and writes a fresh BM25 index, replacing whatever was there. Operator path — most KBs never need this, but it’s useful after changing retrieval_config tuning knobs or recovering from a partial index.
id
string
required
KB ID
Only valid for KBs whose retrieval_config.method is hybrid or full_text. Vector-only KBs return 400 (BM25 isn’t part of their retrieval path).
response = requests.post(f"{BASE_URL}/api/knowledge-bases/{kb_id}/build-bm25", headers=headers)
print(response.json())  # {"task_id": "...", "knowledge_base_id": "..."}
Returns 202 + {"task_id": "celery-task-uuid", "knowledge_base_id": "kb-uuid"}. Poll the KB’s bm25_status field via GET /api/knowledge-bases/{id} to observe completion. Returns 503 if the Celery worker can’t be reached.

POST /api/knowledge-bases//items

Fetch the indexed content items (chunks, nodes, or extracted JSON) for one or more source documents. The response shape depends on the KB’s indexing_config.strategy — every item carries id, source_id, text, and meta, plus strategy-specific extras. This is the right endpoint when you want to enumerate everything the platform produced from a source — for export, debugging, or to consume Doc2JSON extracted_json directly without going through retrieval.
id
string
required
KB ID
source_ids
string[]
required
Non-empty list of source UUIDs (sources.id). Items from sources that were never added to this KB return zero rows for that ID rather than an error.
limit
integer
Max items to return. Default 1000, capped at 10000.
offset
integer
Pagination offset. Default 0.
{
  "source_ids": ["src-uuid-1", "src-uuid-2"],
  "limit": 1000,
  "offset": 0
}
The text field is the embeddable representation of the item (full chunk text for chunk_embed, node body for page_index/graph_index, document summary for full_document/doc2json). Strategy-specific extras:
StrategySource tabletext isExtra fields
chunk_embedai.chunksfull chunk textchunk_index, start_char, end_char, tokens
page_indexai.page_index_nodesnode textnode_id, title, depth, parent_node_id
graph_indexai.graph_index_nodesnode textnode_id, title, depth, parent_node_id
full_documentai.full_documentsdocument summaryfull_text_path
doc2jsonai.doc2json_documentsdocument summaryextracted_json
{
  "items": [
    {
      "id": "doc-uuid",
      "source_id": "src-uuid-1",
      "text": "Q3 2025 financials summary...",
      "meta": { "page_count": 42 },
      "extracted_json": {
        "revenue_usd": 12345678,
        "fiscal_period": "Q3-2025",
        "key_risks": ["supply chain", "fx"]
      }
    }
  ],
  "total": 1,
  "strategy": "doc2json",
  "source_ids": ["src-uuid-1"]
}
response = requests.post(
    f"{BASE_URL}/api/knowledge-bases/{kb_id}/items",
    headers=headers,
    json={"source_ids": [source_id], "limit": 1000},
)
data = response.json()
for item in data["items"]:
    if data["strategy"] == "doc2json":
        print(item["extracted_json"])
    else:
        print(item["text"])

POST /api/knowledge-bases//search

Run a search against the knowledge base.
id
string
required
KB ID
{
  "query": "search text",
  "top_k": 5,
  "retrieval_method": "hybrid",
  "filter_metadata": { "topic": "billing" },
  "similarity_threshold": 0.3,
  "source_ids": ["src-uuid-1", "src-uuid-2"]
}
Body fieldTypeNotes
querystring (required)Natural-language search query
top_kintNumber of results to return. Default 5 (or KB_DEFAULT_TOP_K setting)
retrieval_methodstringvector_search / full_text / hybrid / tree_search. Omit to use the KB’s configured default — the response carries retrieval_method: "auto" to indicate this.
similarity_thresholdfloatMinimum vector score (0–1). Items below this are filtered out. Default 0.0.
filter_metadataobjectNarrow to chunks whose enrichment-metadata fields match. Keys are field names from the KB’s enrichment config (see /enrichment); values can be scalars or { "op": "...", "value": ... } shapes.
source_idsarray of UUIDsRestrict to chunks from this set of sources only. Useful for “search inside one specific document.”
Per-request overrides for retrieval tuning are also accepted: vector_weight (hybrid only), context_mode (text or image — see Multimodal Retrieval), ts_language (full-text language for stemming), and reranker fields. These override the KB’s stored retrieval_config for this one request only. The reranker, query-enrichment, and multimodal (context_mode) behaviors are all properties stored on the KB’s retrieval_config — set them at creation or change them later with PATCH. See the create body below and the knowledge bases concept guide for the full field reference.
response = requests.post(f"{BASE_URL}/api/knowledge-bases/{kb_id}/search", headers=headers, json={"query": "search text", "top_k": 5, "retrieval_method": "hybrid"})

Metadata Enrichment

Metadata enrichment runs an LLM over each indexed item to extract structured fields (text, boolean, number, enum) into a per-KB metadata table. The enriched values can then be used as filters in the /search filter_metadata parameter. PUT triggers re-enrichment only when something material changes (see the endpoint notes below).

PUT /api/knowledge-bases//enrichment

Create or replace the enrichment config. Re-enrichment behavior depends on what changed:
  • Changes to fields or llm_model — drop the metadata table, recreate it, and re-enrich every item from scratch. Returns 409 if a run is already in progress.
  • Toggle of use_multimodal — keep the table, but re-enrich because results differ with/without images. Returns 409 if a run is in progress.
  • max_tokens-only change — lightweight update; no re-enrichment.
  • Identical body — no-op.
The response body is { "config": {...}, "re_enrichment_triggered": <bool> }.
id
string
required
KB ID
fields
object[]
required
Field definitions. Each: { name, description, type: "text"|"boolean"|"number"|"enum", enum_values?: string[] }. name must be SQL-safe (alphanumeric + underscores, starts with a letter) and not in the reserved set (id, item_id, item_type, enriched_at, _enrichment_error). enum types require enum_values with at least 2 entries.
llm_model
string
Model identifier (e.g. gpt-4o).
max_tokens
integer
Max tokens per enrichment call.
use_multimodal
boolean
When true, the enricher sees page images alongside text.
{
  "fields": [
    { "name": "topic", "description": "Main topic of the chunk", "type": "text" },
    { "name": "is_legal", "description": "True if discusses legal matters", "type": "boolean" },
    { "name": "severity", "description": "Risk level", "type": "enum", "enum_values": ["low", "medium", "high"] }
  ],
  "llm_model": "gpt-4o",
  "max_tokens": 500,
  "use_multimodal": false
}
requests.put(f"{BASE_URL}/api/knowledge-bases/{kb_id}/enrichment", headers=headers, json={"fields": [...], "llm_model": "gpt-4o"})

GET /api/knowledge-bases//enrichment

Get the current enrichment config and run status. Returns {"config": null} if no config exists. The config includes status (idle, enriching, completed, completed_with_errors, failed), enriched_count, total_count, and the dynamic metadata_table_name.
id
string
required
KB ID
requests.get(f"{BASE_URL}/api/knowledge-bases/{kb_id}/enrichment", headers=headers)

DELETE /api/knowledge-bases//enrichment

Remove the enrichment config and drop its metadata table. Returns 404 if no enrichment config exists, or 409 if a run is in progress.
id
string
required
KB ID
requests.delete(f"{BASE_URL}/api/knowledge-bases/{kb_id}/enrichment", headers=headers)

POST /api/knowledge-bases//enrichment/run

Manually trigger enrichment. With incremental: true only items missing metadata are processed; with retry_failed: true items previously marked failed are retried.
id
string
required
KB ID
incremental
boolean
Skip items that already have metadata.
retry_failed
boolean
Re-enrich items currently marked failed.
{ "incremental": true, "retry_failed": false }
requests.post(f"{BASE_URL}/api/knowledge-bases/{kb_id}/enrichment/run", headers=headers, json={"incremental": True})

GET /api/knowledge-bases//enrichment/results

Fetch enriched metadata for specific items. Pass a comma-separated item_ids query parameter. Returns per-item field values plus item_errors for any items that failed enrichment.
id
string
required
KB ID
item_ids
string
required
Comma-separated item UUIDs (chunk/node IDs from /items).
{
  "results": {
    "chunk-uuid-1": { "topic": "billing", "is_legal": false, "severity": "low" }
  },
  "fields": [...],
  "item_errors": {
    "chunk-uuid-2": "LLM returned invalid JSON"
  }
}
ids = ",".join([chunk_a, chunk_b])
requests.get(f"{BASE_URL}/api/knowledge-bases/{kb_id}/enrichment/results?item_ids={ids}", headers=headers)

Graph-index re-enrichment

Specific to KBs whose indexing_config.strategy is graph_index. These endpoints re-run reference enrichment (Stages 2+3) without a full reindex.

POST /api/knowledge-bases//graph-enrichment/run

Re-run graph reference enrichment. Optionally limit to a single indexed_source_id, or set retry_failed: true to retry only the previously-failed references. Returns 400 if the KB strategy is not graph_index.
id
string
required
KB ID
indexed_source_id
string
Limit to a single source’s references.
retry_failed
boolean
Retry only previously-failed references.
requests.post(f"{BASE_URL}/api/knowledge-bases/{kb_id}/graph-enrichment/run", headers=headers, json={"retry_failed": True})

GET /api/knowledge-bases//graph-enrichment/errors

Per-source enrichment error counts. Useful for surfacing which sources need retry. Returns 400 if the KB is not graph_index.
id
string
required
KB ID
requests.get(f"{BASE_URL}/api/knowledge-bases/{kb_id}/graph-enrichment/errors", headers=headers)

Discoverable defaults

GET /api/config/kb-defaults

Returns the platform’s KB-creation defaults so a UI or self-service KB-creation flow can present valid options without hardcoding them. The Studio’s KB-create wizard uses this exact endpoint. Returns:
  • strategies — map of strategy name → { label, compatible_retrievers, retriever_labels, default_retrieval_method, supports_reranker, default_indexing_config, default_retrieval_config }. The strategies are the indexing strategies described in Knowledge bases & indexing.
  • reranker{ default_model, candidate_count, options }. options is the list of supported reranker models (Cohere v3, Jina v2, Voyage 2.5, ZeroEntropy zerank-2, etc.).
  • query_enrichment{ model } (the model used to rewrite/expand user queries).
  • enrichment{ model, max_tokens } (the LLM used for chunk metadata enrichment).
  • hybrid_vector_weight — default weight for hybrid search (vector vs. sparse contribution).
  • extraction{ default_method, fallback_chain, options }. options lists every supported extraction method (mistral, paddleocr, lighton, opendataloader, fitz, pdfplumber, plus auto) with a one-line description of each.
This is the single source of truth for what’s selectable when creating or reconfiguring a KB — if the platform adds a new strategy, reranker, or extraction method, it shows up here without any docs update.
defaults = requests.get(f"{BASE_URL}/api/config/kb-defaults", headers=headers).json()
print(list(defaults["strategies"].keys()))
print([opt["value"] for opt in defaults["extraction"]["options"]])

Error Responses

KB routes return {"error": "<message>"} (no structured error code field).
StatusDescription
400Invalid chunking, embedding, or enrichment field configuration
400Endpoint requires a specific indexing_config.strategy (e.g. graph-enrichment endpoints require graph_index)
404No knowledge base or indexed source exists with the given ID
404No enrichment config exists (DELETE /enrichment, GET /enrichment/results)
409Indexing for this source has already finished or been cancelled (/sources/{id}/cancel)
409Cannot update or delete enrichment config while a run is active
503Failed to dispatch the indexing task (worker unavailable)