Documents

Create a document

Creates a new text document and generates an embedding vector for semantic search, in the project named in the path.

post/v1/projects/{project_id}/documents

Request

  • Base URL: https://api.naturali.ai
  • URL: https://api.naturali.ai/v1/projects/{project_id}/documents
  • Auth: one of:
    • HTTP bearer
    • OAuth 2 (authorization code, authorization URL https://api.naturali.ai/authorize, token URL https://api.naturali.ai/token, scopes: mcp:access)

Request body

contentstring required
pathstring

Logical path within the project (e.g. /reports/q1.txt). Defaults to /<filename>, or to /<document_id>.txt when neither is given — a document with no path is reachable only by its id, since a prefix filter never matches null.

filenamestring
titlestring

Document title

metadataMetadataBag

Caller-owned annotations on a resource, stored as the object they were written as: the types a value was written with are the types a read returns, so a filter can ask an ordering question about a number. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.

No key is reserved, and that is the point: every piece of state the platform owns lives in its own typed column, so nothing written here reaches platform state. The platform never reads the bag — it is not an IAM context, not a policy input and not part of a prompt — which is what separates it from a tag bag.

tagsTagBag

Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with 400 VALIDATION_FAILED, never coerced. Keys are opaque and stored verbatim, so cost_center and costCenter are two different tags. Matched by JSONB containment wherever tags are read: the ?tags= filter and knowledge search.

Keys beginning system. are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with 400 RESERVED_TAG_KEY. They are read and filtered like any other tag.

The bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is 400 VALIDATION_FAILED with meta.limit naming the bound it crossed. system.* keys are the platform's and do not count against the 50.

chunk_strategy'page' | 'whole' | 'size'

How to split the content into embeddable chunks. whole (default) stores the content as a single chunk; size splits into fixed-size character windows with overlap. page is equivalent to whole for plain text.

chunk_sizeinteger

Window size in characters when chunk_strategy=size. Defaults to 1000.

chunk_overlapinteger

Overlap in characters between consecutive windows when chunk_strategy=size. Defaults to 200.

Example request

{
  "content": "The quick brown fox jumps over the lazy dog.",
  "path": "/reports/q1.txt",
  "filename": "my-doc.txt",
  "metadata": {
    "author": "John",
    "revision": 2
  },
  "tags": {
    "team": "finance",
    "env": "prod"
  },
  "chunk_size": 1000,
  "chunk_overlap": 200
}

Response

Document created

idstring

Document ID

file_idstring

Underlying file ID

project_idstring

Project ID

pathstring nullable

Logical path of the document within the project (e.g. /reports/q1.txt)

filenamestring

Original filename

content_typestring

Media type of the source file the document was ingested from. Absent when the underlying file is gone.

sizeinteger

File size in bytes

status'pending' | 'processing' | 'ready' | 'failed' | 'withdrawn'

Ingestion lifecycle state. pending — enqueued; processing — chunks being extracted and embedded; ready — fully indexed; failed — processing error (see the error field on GET /documents/{id}/status); withdrawn — taken out of listings and search, with its chunks dropped, and restorable from an earlier version.

versioninteger

The document's content version, starting at 1. Every write that changes the content or its annotations archives the state it replaced and increments this.

contentstring nullable

Text content (only present on getDocument, and only when status is ready)

chunk_strategy'page' | 'whole' | 'size'

The chunk strategy the document was last (re-)ingested with. Absent when the default (whole) was used — the mapper omits the key rather than sending null.

chunk_sizeinteger

Window size in characters used when chunk_strategy=size. Absent otherwise.

chunk_overlapinteger

Overlap in characters between consecutive windows used when chunk_strategy=size. Absent otherwise.

created_atstring date-time
updated_atstring date-time

Example response

{
  "id": "doc_V1StGXR8Z5jdHi6B",
  "file_id": "file_V1StGXR8Z5jdHi6B",
  "project_id": "proj_V1StGXR8Z5jdHi6B",
  "path": "/reports/q1.txt",
  "filename": "my-doc.txt",
  "content_type": "application/pdf",
  "size": 42,
  "status": "ready",
  "version": 1,
  "content": "The quick brown fox jumps over the lazy dog.",
  "chunk_strategy": "size",
  "chunk_size": 800,
  "chunk_overlap": 120
}

Changes

No recorded changes to this operation across all 1 revision of this API.