Knowledge

Upload documents, get hybrid semantic and keyword search with citations, and give your agents grounded answers over your own content.

How It Works

A knowledge base is a searchable index over documents you upload. 2kw.ai parses each document, splits it into chunks, embeds the chunks with the embedding model you chose, and stores both the vectors and a keyword index. A search runs dense and lexical retrieval side by side and fuses the two rankings.

The workflow is: create a knowledge base → upload documents → search, or attach it to an agent → resolve citations.

Everything on this page is also available in the console under Knowledge in the sidebar: a list, a create form, and a detail page with Documents, Playground and Settings tabs.

Concepts

TermMeaning
Knowledge baseThe index: an embedding provider and model, a chunking configuration, and the documents in it. Configuration changes are versioned.
DocumentOne logical source, identified by its filename. Uploading a file with the same name again creates a new revision of the same document.
RevisionOne ingested version of a document, with its own status. Retrieval serves the newest READY revision.
ChunkA passage of a revision, with page range and heading path where the source has them. Search results and citations point at chunks.
CitationA pointer from an agent's answer to the exact chunk it used. Citations stay resolvable after re-ingests and deletions.

Creating a Knowledge Base

POST /v1/knowledge-bases

Request

curl -X POST https://api.2kw.ai/v1/knowledge-bases \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk_your_api_key" \
  -d '{
    "name": "Product manuals",
    "slug": "product-manuals",
    "description": "Installation and service manuals, current models",
    "embeddingProviderId": "your-provider-id",
    "embeddingModel": "text-embedding-3-small",
    "embeddingDim": 1536,
    "chunkingStrategy": "HIERARCHICAL",
    "hybridSearchEnabled": true
  }'

Returns 201 Created with the knowledge base.

FieldTypeRequiredDescription
namestringYesDisplay name
slugstringYesLowercase letters, digits and hyphens. Unique within the organization; a collision returns 409
descriptionstringNoFree text, up to 4000 characters
embeddingProviderIdstringYesThe id of one of your configured AI providers, or builtin for the platform's built-in embedding models
embeddingModelstringYesAn embedding model that provider serves. See the catalog below
embeddingDimintegerYesVector width. 1536 is the provisioned width; a model that cannot serve it is refused with 400
chunkingStrategystringNoHIERARCHICAL (default), FIXED, AUTO or CUSTOM
chunkSizeintegerNoTokens per chunk, 50 to 4000. Default 400
chunkOverlapintegerNoOverlap between neighbouring chunks, 0 to 2000. Default 50
parentChunkSizeintegerNoHierarchical only. Size of the parent passage returned around a matching child, 100 to 8000. Default 1500
hybridSearchEnabledbooleanNoRun keyword retrieval next to vector retrieval. Default true
rerankerProviderIdstringNoReserved for a reranking provider. Leave empty. builtin is refused with 400: there is no built-in reranker
textSearchLanguagestringNoText-search language for the keyword leg — stemming and stop words. Default simple. See Text-search language below

Which embedding models fit

GET /v1/providers/{providerId}/embedding-models

Lists the embedding models a provider serves at a provisioned width. The console's create form uses this to fill the model dropdown. Pass builtin as providerId to list the built-in embedding models; the list is empty when the platform offers none.

{
  "models": [
    { "id": "text-embedding-3-small", "dimensions": 1536 },
    { "id": "text-embedding-3-large", "dimensions": 1536 }
  ],
  "supportedDimensions": [1536]
}

A model appears once per width it can serve, so a model with a larger native width is offered at 1536. Providers without a catalogued embedding model return an empty list, never an error; you can still pass a model id by hand. Creating the knowledge base tries that model on the provider first and answers 400 when the provider does not serve it at the requested width, so nothing is saved that cannot embed.

Chunking strategies

StrategyWhat it doesUse it when
HIERARCHICALIndexes small child chunks for precise matching and returns the surrounding parent passage (parentChunkSize) as the result.Default. Manuals, contracts, reports: anything where a match needs its context.
FIXEDFixed-window chunks of chunkSize tokens with chunkOverlap. The chunk itself is the result.Short, uniform records: tickets, FAQ entries, product rows.
AUTOToday identical to HIERARCHICAL. Reserved for a per-document choice later.Only if you want the platform's future default without changing configuration.
CUSTOMNot supported yet. A knowledge base with this strategy fails ingestion.Do not use.

Chunking settings apply to documents ingested after the change. Existing revisions keep the chunks they were indexed with.

Text-search language

The keyword half of hybrid search matches with a Postgres text-search configuration — stemming and a stop-word list, so a search for "invoices" also matches "invoice", and common words like "the" or "die" do not drown out the terms that actually distinguish a passage. textSearchLanguage picks that configuration; the default, simple, applies neither: no stemming, no stop words, which is the safe choice for a mixed-language corpus but the weakest match for a single-language one.

Stemming joins inflected forms of the same word ("invoices" → "invoice", plural and case endings collapsing onto a shared root). It does not join a word with an unrelated derived form — a German knowledge base set to german still treats "klassifiziert" and "Klassifizierung" as different words, since they are not the same inflection of one root. Matching across word families like that is the job of the vector half of hybrid search, not the keyword one.

Any Postgres text-search configuration works: english, german, french, spanish, and every other configuration Postgres 17 ships. Pick the language most of the knowledge base's content is written in.

Changing textSearchLanguage does not rewrite anything inline. A background process rebuilds the knowledge base's keyword index in the new language, and search keeps answering from the current language and its existing rows until that finishes — never a mix of both. In the console, the knowledge base's Settings tab shows "Reindexing to <language>…" under the field while this is in progress; the console achieves this by leaving textSearchLanguage out of the request entirely on a save that did not touch it — never by sending the value back.

Do the same from the API: omit textSearchLanguage from a PATCH to leave an in-progress reindex untouched. Sending it back explicitly is not equivalent, even with the value GET just returned — GET reports the active language as textSearchLanguage, and sending that value back while a different one is pendingTextSearchLanguage is the request's cancellation case: it abandons the pending reindex and discards the rows it already built.

Built-in embedding models

A knowledge base does not need a provider of your own. Set embeddingProviderId to builtin and it embeds through the platform's own models:

{
  "name": "Product manuals",
  "slug": "product-manuals",
  "embeddingProviderId": "builtin",
  "embeddingModel": "text-embedding-3-large",
  "embeddingDim": 1536
}
ModelStored widthPrice per million input tokens
text-embedding-3-small1536€0.0825
text-embedding-3-large1536€0.53625

Both models store 1536-wide vectors: text-embedding-3-small emits that width natively, and text-embedding-3-large is asked for 1536 dimensions on every call. GET /v1/providers/builtin/embedding-models lists the models the platform offers right now. Prices are net of VAT; see the rate card.

Documents and search queries are embedded on the platform's Azure OpenAI deployments in the EU Data Zone, the same processor and region as the built-in chat models. Creating the knowledge base checks the model against the platform's catalogue without calling it, so the check costs nothing.

The same models are callable on their own through the gateway's OpenAI-compatible POST /v1/embeddings, charged at the same rate.

Every embedding call, at ingestion and for each search query, counts toward your plan's usage and is charged for the tokens it used. When a call is refused, for example because your plan's usage is used up and no extra usage is available:

  • Ingestion: the revision ends in ERROR with the refusal in its error, and the revision retrieval already serves stays as it was. Nothing resumes by itself; once usage is available again, retry the document with POST /v1/knowledge-bases/{knowledgeBaseId}/documents/{documentId}/retry, which embeds it again from the start.
  • Search: the request answers with the refusal's status, 402 when usage is used up, instead of falling back to keyword results with lexical_only.

Uploading Documents

POST /v1/knowledge-bases/{knowledgeBaseId}/documents

Multipart upload. The form field is files and takes one or more files.

Request

curl -X POST https://api.2kw.ai/v1/knowledge-bases/kb_123/documents \
  -H "Authorization: Bearer sk_your_api_key" \
  -F "files=@service-manual-2026.pdf" \
  -F "files=@installation-guide.docx"

The response is always 202 Accepted, with one entry per file:

[
  {
    "filename": "service-manual-2026.pdf",
    "upload": {
      "document": { "id": "doc_…", "name": "service-manual-2026.pdf", "knowledgeBaseId": "kb_123" },
      "acceptedVersion": { "id": "ver_…", "versionNo": 1, "status": "PENDING", "mime": "application/pdf", "byteSize": 1834022 }
    }
  },
  {
    "filename": "notes.xyz",
    "error": "Unsupported file type"
  }
]

Files are accepted independently. One rejected file does not discard the others.

RuleValue
FormatsThe same 17 input formats as Document Conversion: PDF, DOCX, XLSX, PPTX, CSV, MSG, EML, HTML, Markdown, TXT, AsciiDoc, ZIP, images, DXF, DWG, STEP, GEO
Size25 MB per file, 104 MB per request. A file over the per-file cap is rejected with 413 file_too_large; split larger batches into several requests
Same filenameCreates a new revision of the existing document. The previous revision keeps serving retrieval until the new one is READY, then the switch is atomic
StorageDocument bytes are kept for as long as the document exists, so a revision can be re-served and its citations resolved

Ingestion status

Ingestion is asynchronous. Poll the accepted revision until it reaches a terminal state:

GET /v1/knowledge-bases/{knowledgeBaseId}/documents/{documentId}/versions/{versionId}

StatusMeaning
PENDINGAccepted, waiting for a worker
PARSINGConverting the file to text and structure
CHUNKINGSplitting into chunks
EMBEDDINGCalling the embedding model
READYSearchable. Terminal
ERRORFailed. Terminal; error on the revision says why

GET /v1/knowledge-bases/{knowledgeBaseId}/documents lists documents with the revision retrieval is serving, newest first. Filter with ?status=READY (or any other status); the filter applies to the current revision.

Searching

POST /v1/knowledge-bases/{knowledgeBaseId}/search

Request

curl -X POST https://api.2kw.ai/v1/knowledge-bases/kb_123/search \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk_your_api_key" \
  -d '{
    "query": "torque for the M8 mounting bolts",
    "topK": 5
  }'
FieldTypeRequiredDescription
querystringYesNatural-language question or keywords
topKintegerNoNumber of hits. Default 8, clamped to 1 to 100
metadataFilterobjectNoKey/value filter on document metadata

Response

{
  "query": "torque for the M8 mounting bolts",
  "hits": [
    {
      "hitId": "h_0",
      "knowledgeBaseId": "kb_123",
      "documentId": "doc_…",
      "documentVersionId": "ver_…",
      "documentName": "service-manual-2026.pdf",
      "chunkId": "chk_…",
      "content": "Mounting. Tighten the four M8 bolts to 25 Nm in a cross pattern …",
      "matchedSpan": "M8 bolts to 25 Nm",
      "pageStart": 14,
      "pageEnd": 14,
      "headingPath": ["Installation", "Mounting"],
      "score": 0.0325
    }
  ]
}
FieldDescription
contentThe passage. Under hierarchical chunking this is the parent passage around the matching child
matchedSpanThe part of content that matched most closely, when available
pageStart, pageEndPage range in the source, absent for sources without pages
headingPathThe heading hierarchy above the passage
scoreReciprocal Rank Fusion score. Useful for ordering, not comparable across queries
chunkId, documentVersionIdStable ids you can store and later resolve as a citation

Search is top-k and not paginated. Every request re-embeds the query and re-fuses both rankings, so there is no stable ordering to page through. Raise topK instead.

An empty hits array is a normal result, not an error.

Degraded results

When the embedding provider does not answer, the search falls back to keyword retrieval and says so:

degradedMeaning
absentBoth retrievers ran
lexical_onlyKeyword retrieval only. The embedding call for the query failed
partial_lexical_onlySome of the searched knowledge bases fell back to keyword retrieval

Results are still returned with HTTP 200. Check the field if recall matters to you.

A refused call to a built-in embedding model is not a degraded result: the search answers with the refusal's status (402 when usage is used up) instead of lexical_only.

Citations

An agent that answers from a knowledge base cites the passages it used. Each citation carries the chunkId of the passage, and you can turn it back into text at any time:

GET /v1/knowledge-bases/{knowledgeBaseId}/citations/{chunkId}

{
  "chunkId": "chk_…",
  "documentVersionId": "ver_…",
  "text": "Mounting. Tighten the four M8 bolts to 25 Nm in a cross pattern …",
  "textStatus": "AVAILABLE",
  "pageStart": 14,
  "pageEnd": 14,
  "headings": ["Installation", "Mounting"],
  "isCurrentVersion": true
}

Citations are durable on purpose:

  • After a re-ingest, a citation issued against the old revision still returns the full text the model saw, with isCurrentVersion: false.
  • After the document is deleted, the citation still resolves with its ids, page range and headings, but the passage is withheld: text is null and textStatus is WITHDRAWN.
  • A chunk id that does not exist, or belongs to another knowledge base or organization, is a 404.

How citations are produced, and the backbone:citation items an agent emits, are described on the Agents page.

Using a Knowledge Base with an Agent

Attach one or more knowledge bases to an agent by adding the built-in file_search tool to the agent's tools:

{
  "type": "file_search",
  "config": { "knowledgeBaseIds": ["kb_123", "kb_456"] }
}

The model can then search those knowledge bases during a turn. It cannot name a knowledge base itself: which ones it may search is your configuration on the agent version, not a request parameter. Searching across several knowledge bases fuses their rankings into one list.

In the console, the agent form's Tools section offers the same setting with a knowledge base picker. Details of the tool, its result shape and citation handles are on the Agents page.

Managing Knowledge Bases

OperationEndpointNotes
ListGET /v1/knowledge-basesPaginated, newest first
GetGET /v1/knowledge-bases/{id}
UpdatePATCH /v1/knowledge-bases/{id}Takes the same body as create. Mutable fields are replaced and the result is snapshotted as the next configuration version. embeddingDim cannot change
DeleteDELETE /v1/knowledge-bases/{id}Soft delete; releases the slug, keeps version history. Admin only
Get documentGET /v1/knowledge-bases/{id}/documents/{documentId}The document with the revision retrieval serves
Delete documentDELETE /v1/knowledge-bases/{id}/documents/{documentId}Takes every revision out of retrieval and withdraws the passage text from previously issued citations. Admin only

The console

Open Knowledge in the sidebar.

  • List shows name, status, embedding model, chunking strategy and creation date.
  • Create derives the slug from the name, offers Built-in as the first embedding provider whenever the platform offers built-in embedding models (preselected when your organization has no embedding-capable provider of its own), offers the embedding model catalog for the chosen provider, and hides chunking under an advanced section.
  • Documents tab: a dropzone that enforces the size limits and splits large batches, a table with status badges, and polling while any revision is still ingesting. A revision that has not moved for fifteen minutes is marked as possibly stalled.
  • Playground tab: run a search against this knowledge base and see ranked passages with page, headings and the matched span. Use it to tell "the agent is misconfigured" apart from "the knowledge base does not contain the answer".
  • Settings tab: edit name, slug, description, hybrid search, chunking parameters and the text-search language; provider (Built-in for a knowledge base on the built-in models), model and dimension are read-only.

Permissions

RoleCan
ViewerList, read, search, resolve citations
MemberCreate and update knowledge bases, upload documents
AdminDelete knowledge bases and documents

Search results are restricted to passages the calling principal may see.

Errors

StatusWhen
400Embedding dimension does not match the model, a model the provider does not serve (the built-in provider included), builtin as rerankerProviderId, invalid chunking parameters, attach with a file whose purpose is not knowledge
402A search on a built-in knowledge base whose embedding call was refused because usage is used up
404Unknown knowledge base, document, revision, chunk or file
409Slug already in use
410Attach with a file that has been deleted
413A file or request over the size limit

Uploads answer 202 even when every file was rejected; read each entry's error.

Was this page helpful?