What changed in v2.1. The knowledge base used to be a per-tenant CRUD entity; each KB had its own Qdrant collection and a case study pointed at one KB. The model is simpler now:
- A collection is the unit. Create as many as you need (
case-studies,assessments, etc.). Documents live in a collection. - A document has a stable
source_id(its document id). Other domains reference documents by id, not by KB. - The legacy KB CRUD endpoints (
POST /v1/console/knowledge-bases,GET /…/{kb_id}, etc.) are gone. The base path is now/v1/console/knowledge-base(singular). Document upload moved under/collections/{name}/documents. - Embeddings use
gemini-embedding-2. The v1 embedding space is incompatible — existing vectors must be re-ingested.
source_id. Domains that need retrieval — case studies today, others later — attach documents by id.
There is one Qdrant collection per named collection, not per case study. Retrieval scopes by source_id, so a single collection happily holds many overlapping document sets.
Collections
Create Collection
created_by and created_at.
Requires KNOWLEDGE_BASES.can_create permission.
Request body
Lowercase letters, digits,
- or _; 3–64 characters; cannot start or end with separators. Examples: case-studies, assessments, training_v2.Optional. Free-text description for humans. Max 500 characters.
Example request
Response
List Collections
KNOWLEDGE_BASES.can_view.
Collections are global within the instance (one Qdrant index per name). The size_bytes and document_count columns are tenant-scoped — they only count documents the caller’s tenant uploaded into each collection. Two tenants viewing the same collection will see different counts.
The first call to this endpoint after deployment lazily creates Mongo metadata rows for any Qdrant collection that existed before v2.2; see the breaking-changes note above for the implications.
Query parameters
Case-insensitive substring filter on collection name. Example:
search=case matches case-studies and case-archive.Records to skip.
Max records to return (1–100).
Example request
Example response
Field reference
Stable Mongo
_id of the collection’s metadata row. Use this when other endpoints ask for collection_id.Sum of source file bytes (
KBDocument.file_size) for documents in this collection that belong to the caller’s tenant. Returns 0 when the collection holds nothing for this tenant.Count of
KBDocument rows in this collection that belong to the caller’s tenant. Returns 0 when the collection is empty for this tenant.The
user_id of the admin who created the collection. "system" for collections that pre-date v2.2 (backfilled on first list).ISO 8601 timestamp. For backfilled (legacy) collections, this is the time of backfill — Qdrant doesn’t track real creation time.
Errors
| Status | Code | Condition |
|---|---|---|
401 | UNAUTHORIZED | Missing or invalid bearer token. |
403 | FORBIDDEN | Caller lacks KNOWLEDGE_BASES.can_view. |
400 | VALIDATION_ERROR | skip/limit out of bounds or search longer than 100 chars. |
Delete Collection
KBDocument recorded in it, and $pulls those document ids out of every case study’s document_ids. Requires KNOWLEDGE_BASES.can_delete.
This is destructive. Vectors are gone after this call; the documents would have to be re-uploaded and re-ingested to restore retrieval.
Example response
Documents
A document is a file (PDF, DOCX, PPT(X), TXT, MD) stored in S3 and indexed in a single collection. Each upload creates oneKBDocument row whose id doubles as the source_id carried in every Qdrant vector belonging to that document.
Upload Document
KNOWLEDGE_BASES.can_create. The collection must exist (create it first if needed). The upload stores the file in S3 and queues a Celery ingestion task; the response is returned immediately with status: "pending".
Path parameters
The target collection name.
Request body
Content-Type: multipart/form-data
The document file to upload.
Example request
Example response
Ingestion status
| Status | Meaning |
|---|---|
pending | Uploaded, awaiting the worker. |
processing | Extraction, chunking, embedding in progress. |
completed | Vectors indexed, document is searchable. |
failed | Ingestion failed — check error_message. |
List Collection Documents
KNOWLEDGE_BASES.can_view.
Path parameters
The collection name.
Query parameters
Records to skip.
Max records (1–100).
Example response
data shares the same collection_id (the parent collection — same as the one returned by List Collections for this name). collection_id is null only in the rare case where Qdrant has the collection but the Mongo metadata row hasn’t been backfilled yet — see the breaking-changes note at the top.
Get Document
KNOWLEDGE_BASES.can_view.
The id alone identifies the document; you don’t need to know its collection.
Delete Document
$pulls the id out of every case study that referenced it. Requires KNOWLEDGE_BASES.can_delete.
Example response
Re-ingest Document
pending, and re-queues the ingestion task. Use after the source file has been replaced in-place, or after switching embedding models. Requires KNOWLEDGE_BASES.can_edit.
How retrieval works
Each ingested chunk is stored with a payload that includes:document_ids from the case study (or whichever domain owns retrieval). On every user turn, the agent embeds the question with gemini-embedding-2 and queries the relevant collection with payload.source_id IN [document_ids]. Empty list = no RAG, no fallback to the whole collection.
This means: one collection can hold documents for many case studies safely; cross-pollination only happens when an admin explicitly attaches a document to multiple case studies.
Settings
| Variable | Default | Purpose |
|---|---|---|
KB_COLLECTION_NAME | case-studies | Default collection for case-study uploads. |
KB_EMBEDDING_MODEL | gemini-embedding-2 | Embedding model used for ingestion + queries. |
KB_EMBEDDING_DIMENSION | 1536 | Output dimension for new collections. Don’t change without a re-ingest plan. |