Knowledge
The library agents work from — versioned documents, playbooks, and RAG collections.
Overview
Upload files to a collection — PDFs, docs, spreadsheets, markdown — and the
platform ingests them (text extraction, chunking, embedding), versions every
update, and lets a collection be queried directly (:query) to preview
retrieval before an Agent is bound to it. Collections are
project-scoped, like every other resource (see Projects).
See the OpenAPI spec for the full endpoint and schema reference, or browse it rendered under API Reference → Knowledge.
Data Model
KnowledgeCollection
| Field | Type | Description |
|---|---|---|
id | string | Public collection ID. |
project_id | string | The owning project. |
name | string | Human-readable label. |
description | string, nullable | |
document_count | integer | Number of documents currently in the collection. |
created_at | string (date-time) | |
updated_at | string (date-time) |
KnowledgeDocument
| Field | Type | Description |
|---|---|---|
id | string | Public document ID. |
collection_id | string | The collection it belongs to. |
project_id | string | The owning project. |
filename | string, nullable | |
content_type | string, nullable | Media type of the source file; null for inline text. |
status | string | pending, indexed or failed. |
error | string, nullable | Why ingestion failed; null otherwise. |
size | integer, nullable | Source size in bytes. |
chunk_count | integer, nullable | Embedded chunks produced; null until ingestion finishes. |
content | string, nullable | Extracted text — returned on get, null on list. |
created_at | string (date-time) | |
updated_at | string (date-time) |
KnowledgeConverter
| Field | Type | Description |
|---|---|---|
id | string | Public converter ID. |
project_id | string | The owning project. |
content_type | string | The media-type glob it claims (image/*, audio/mpeg). |
agent_id | string, nullable | The agent that converts the file. |
tool_id | string, nullable | The tool that converts the file. |
preset_parameters | object, nullable | Fixed arguments merged into every tool-converter call. |
native_extraction | string | first (convert only when native extraction finds no text) or skip (always convert). |
chunk_strategy | string, nullable | Default chunking for documents this converter produces. |
chunk_size | integer, nullable | |
chunk_overlap | integer, nullable | |
created_at | string (date-time) | |
updated_at | string (date-time) |
Key Concepts
Inline text or an uploaded file
A document is created from content (inline text) or from file — the bytes,
base64-encoded, with a content_type and a filename. Exactly one of the two.
application/pdf, text/plain and text/markdown are read directly;
everything else needs a converter (below). Files are capped at 8 MiB of
decoded bytes; over that the call is a 413 file_too_large.
Chunking
Ingestion splits the extracted text before embedding it, and chunk_strategy
picks how:
| Strategy | Splits into | Use it when |
|---|---|---|
page (default) | one chunk per page | You want retrieved passages to cite a page number. |
size | fixed-width character windows (chunk_size / chunk_overlap) | Pages are dense and whole-page retrieval is too fuzzy. Windows are not page-aligned, so they carry no page number. |
whole | one chunk for the document | The document is already short — an OCR'd receipt, a single note. |
Start with page, and move to size only if recall is poor on dense
documents.
Converters
A converter makes a media type the platform cannot read natively — an image, an
audio file — ingestable, by mapping a content_type glob onto an
agent or a tool:
- Agent converter (
agent_id) — the file goes to a multimodal model with a fixed "extract all the text" instruction and its answer becomes the document text. Nothing to map; the shortest path for images and scanned PDFs. - Tool converter (
tool_id) — the file is passed to anhttptool as{ content_type, filename, data_base64 }and whatever string the tool returns becomes the document text. The path for a dedicated non-chat API — a speech-to-text endpoint, a specialist OCR engine — which no LLM can call. Use the tool'sexecute.body_mode: "multipart"for form-data endpoints and itsoutput_mappingto reduce a JSON response to the bare string.
Ingestion never names a converter:
POST /v1/projects/{project_id}/knowledge/collections/{collection_id}/documents
resolves the matching one from the file's content_type every time. A media
type with no converter is a
400 unsupported_content_type and no document is created.
A converter matching a natively-readable type (application/pdf) is consulted
only when native extraction yields no text — which is exactly what makes it a
scanned-PDF fallback, leaving born-digital PDFs on the fast path. Set
native_extraction: "skip" to OCR every PDF regardless.
Ingestion status
Documents are ingested asynchronously; a failed or unsupported file is an
explicit failure state rather than a silent drop. status moves from pending
to indexed or failed, and the transition is announced by the
knowledge.document_ingested / knowledge.ingest_failed
webhook events. A failed document carries the reason in
error;
POST /v1/projects/{project_id}/knowledge/collections/{collection_id}/documents/{document_id}:reingest
retries it.
Retrieval preview
POST /v1/projects/{project_id}/knowledge/collections/{collection_id}:query
answers "which chunks would an agent retrieve for this question?" without
requiring an agent to be bound to the collection first.
Examples
- CLI
- SDK
- curl
naturali create-knowledge-collection \
--project-id proj_V1StGXR8Z5jdHi6B \
--name product-docs
const { data: collection } =
await naturali.knowledge.createKnowledgeCollection({
path: { project_id: 'proj_V1StGXR8Z5jdHi6B' },
body: { name: 'product-docs' },
});
curl -X POST https://api.naturali.ai/v1/projects/proj_V1StGXR8Z5jdHi6B/knowledge/collections \
-H "Authorization: Bearer $NATURALI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "product-docs" }'
Add a PDF to it — the bytes base64-encoded, with the media type that drives routing:
- CLI
- SDK
- curl
naturali create-knowledge-document \
--project-id proj_V1StGXR8Z5jdHi6B \
--collection-id kcol_V1StGXR8Z5jdHi6B \
--filename printer-x1000.pdf \
--content-type application/pdf \
--file "$(base64 -w0 printer-x1000.pdf)"
const { data: document } = await naturali.knowledge.createKnowledgeDocument({
path: {
project_id: 'proj_V1StGXR8Z5jdHi6B',
collection_id: 'kcol_V1StGXR8Z5jdHi6B',
},
body: {
filename: 'printer-x1000.pdf',
content_type: 'application/pdf',
file: readFileSync('printer-x1000.pdf').toString('base64'),
},
});
curl -X POST https://api.naturali.ai/v1/projects/proj_V1StGXR8Z5jdHi6B/knowledge/collections/kcol_V1StGXR8Z5jdHi6B/documents \
-H "Authorization: Bearer $NATURALI_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"filename\": \"printer-x1000.pdf\",
\"content_type\": \"application/pdf\",
\"file\": \"$(base64 -w0 printer-x1000.pdf)\"
}"
Register a converter so images become ingestable too:
- CLI
- SDK
- curl
naturali create-knowledge-converter \
--project-id proj_V1StGXR8Z5jdHi6B \
--content-type 'image/*' \
--agent-id agent_V1StGXR8Z5jdHi6B \
--chunk-strategy whole
const { data: converter } = await naturali.knowledge.createKnowledgeConverter({
path: { project_id: 'proj_V1StGXR8Z5jdHi6B' },
body: {
content_type: 'image/*',
agent_id: 'agent_V1StGXR8Z5jdHi6B',
chunk_strategy: 'whole',
},
});
curl -X POST https://api.naturali.ai/v1/projects/proj_V1StGXR8Z5jdHi6B/knowledge/converters \
-H "Authorization: Bearer $NATURALI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content_type": "image/*",
"agent_id": "agent_V1StGXR8Z5jdHi6B",
"chunk_strategy": "whole"
}'
For the walkthroughs, see Search a library of PDFs, Ingest images and audio, and Split a library between agents for organizing a knowledge base into topic collections, each with its own agent.