---
title: "Ingest a normalized document"
method: POST
path: "/v1/ingest/document"
tags: ["Documents"]
---

# Ingest a normalized document

`POST /v1/ingest/document`

## Request

- Base URL: `https://brain.inite.ai`
- URL: `https://brain.inite.ai/v1/ingest/document`
- Auth: HTTP bearer

Source → Indexer → Candidates → Brain: stores + chunks the text, runs the selected indexers, commits grounded candidates as facts. `mode: "async"` fans out per-indexer queue jobs instead (requires DOCUMENT_MULTI_INDEXER_ENABLED). Throttled to 10 requests/min per credential. Part of the document pipeline — answers `503 feature_disabled` until `DOCUMENT_INGEST_ENABLED=1`. Source: src/documents/documents-ingest.controller.ts.

Required scope: `brain:write`.

## Request body

- IngestDocumentRequest
  - `contextRef` DocumentContextRef, required
    - `recorder` string
    - `vertical` string, required
  - `indexers` union: 'general' (union pass), 'auto' (router-selected), or explicit pack ids.
    - 'general'
    - 'auto'
    - string[]
  - `kind` string, required: Container kind the connector normalized from (pdf/markdown/email/…). Open string — provenance metadata only.
  - `meta` object: Operator-supplied metadata, projected onto derived facts' source.meta.
  - `mode` 'sync' | 'async': 'async' requires DOCUMENT_MULTI_INDEXER_ENABLED and storeContent.
  - `occurredAt` string, date-time, required: ISO 8601 — becomes the derived facts' validFrom.
  - `originUri` string: Pointer back to the raw container (URL, path, message id…).
  - `storeContent` boolean: Store the normalized chunks server-side. false keeps only contentHash + metadata (no re-index, no span re-validation).
  - `text` string, required: Normalized document text (DOC_MAX_CHARS may lower the cap).
  - `title` string
  - `toolObservationRef` string: Provenance hop to the tool result this document derives from (tool_observation:<id>, migration 0111). Honored only under TOOL_OBSERVATIONS_ENABLED; folded into committed facts' source.evidence[].
  - `userId` string: Per-user memory scope (0128): stamps the stored document, every committed fact and any projected scenes with this end-user; dedupe is scope-local. M2M-asserted; a user-bound token pins it to its own user (mismatch = 403).

## Response `201`

Ingest outcome — shape follows the requested `mode`.

- union
  - IngestDocumentSyncResponse
    - `chunkCount` integer, required
    - `committed` object, required
      - `edgeIds` string[], required
      - `entityIds` string[], required
      - `factIds` string[], required
    - `counts` CommitCounts, required
      - `committed` integer, required
      - `merged` integer, required
      - `pending` integer, required
      - `rejected` integer, required
    - `deduplicated` boolean, required
    - `documentId` string, required
    - `mode` 'sync', required
    - `runs` object[], required
      - `packId` string, required
      - `runId` string, required
      - `status` string, required
  - IngestDocumentAsyncResponse
    - `chunkCount` integer, required
    - `deduplicated` boolean, required
    - `documentId` string, required
    - `mode` 'async', required
    - `runs` object[], required
      - `packId` string, required
      - `status` 'enqueued' | 'already_processed' | 'planned', required

## Other responses

- `400`: Validation failed.
- `401`: Missing or unknown bearer key.
- `403`: The key lacks the required scope.
- `429`: Per-credential throttle exceeded.
- `503`: The document pipeline is dark (DOCUMENT_INGEST_ENABLED off).

---

[API](https://skmtc.dev/inite/apis/inite-brain-platform-api.md) · [All operations](https://skmtc.dev/inite/apis/inite-brain-platform-api/llms.txt) · [OpenAPI document](https://skmtc.dev/inite/apis/inite-brain-platform-api/revisions/c7c3eb5e304b?raw)
