---
title: "Enqueue a document for the extraction pipeline."
method: POST
path: "/v1/documents"
tags: ["documents"]
---

# Enqueue a document for the extraction pipeline.

`POST /v1/documents`

## Request

- Base URL: `https://api.andish.ru` (the document declares no server; this is the origin it was published from)
- URL: `https://api.andish.ru/v1/documents`
- Auth: none declared

Creates a ``Document`` row with ``status='queued'`` and returns the ``document_id`` together with a polling URL.  The actual extraction worker (Taskiq) is wired in US-5 (E5.3).

**Limits:** content must be non-empty and ≤ 1 000 000 bytes (UTF-8). Documents above 15 000 words are split into 4 parts (XL path) while ANDISH_XL_SPLIT_ENABLED is on (the default since ADR-037 A2); with the flag off (kill switch) they get 413 ``payload_too_large_xl_unavailable`` (detail: ``max_words``, ``words``). Hard cap: 50 000 words → 413 ``payload_too_large_xl_cap`` (detail includes ``words`` — actual word count — and ``parts_hint`` — suggested number of parts). Splitter failure on degenerate input → 422 ``xl_split_failed``.

## Request body

- DocumentCreateRequest: Request body for POST /v1/documents. Supports two ingestion modes: **Text mode** (default, backward-compatible):: { "content": "Hello world", "metadata": {"source": "api"} } **Binary mode** (PDF / DOCX upload via base64):: { "file_content_b64": "<base64-encoded bytes>", "file_format": "pdf", "metadata": {"source": "upload"} } Exactly one of ``content`` or ``file_content_b64`` must be provided. ``file_format`` defaults to ``"text"`` — omit it when sending plain text.
  - `content` string, nullable: Raw text to index (max 1 MB UTF-8). Documents above 15 000 words trigger automatic split into 4 parts (XL path); 50 000 words is the hard cap (HTTP 413 ``payload_too_large_xl_cap``, detail includes ``words`` — the actual word count — and ``parts_hint`` — the suggested number of parts to split into). Required when file_format='text' (default). Omit when providing file_content_b64 for binary uploads.
  - `file_content_b64` string, nullable: Base64-encoded bytes of a binary document file. Required when file_format is one of: 'pdf', 'docx', 'pptx', 'xlsx', 'csv', 'html', 'rtf', 'odt'. The raw byte length (after decoding) is subject to the tenant's file_size_cap_mb limit. CSV and HTML files must be sent via this field (not 'content') so the server can handle RU cp1251 encoding correctly.
  - `file_format` 'pdf' | 'docx' | 'text' | 'pptx' | 'xlsx' | 'csv' | 'html' | 'rtf' | 'odt': 'text' (default) — send content as plain UTF-8 string. 'pdf' — base64 PDF, extracted via pypdf. 'docx' — base64 DOCX, extracted via python-docx. 'pptx' — base64 PPTX, extracted via python-pptx. 'xlsx' — base64 XLSX, extracted via openpyxl. 'csv' — base64 CSV bytes, decoded server-side (UTF-8 / cp1251). 'html' — base64 HTML bytes, decoded and stripped server-side. 'rtf' — base64 RTF, extracted via striprtf. 'odt' — base64 ODT, extracted via zip+lxml (no odfpy).
  - `metadata` object: Arbitrary key-value metadata stored alongside the document.
  - `document_date` string, nullable: Optional human-readable validity date. Accepted formats: ISO date ('2024-12-15'), quarter ('2024-Q3'), year ('2024'), or any label ≤32 characters. Stored as-is; not parsed or validated as a date.

## Response `202`

Successful Response

- DocumentCreateResponse: 202 response for POST /v1/documents. ADR-037 §2.8: ``is_split=True`` when the upload triggered the XL parent/part path. User-facing UI ignores this field; internal/admin consumers may branch on it. Defaults to False for backward-compatible single-doc responses.
  - `document_id` string, required
  - `status` string, required
  - `status_url` string, required
  - `is_split` boolean

## Other responses

- `402`: ``cost_cap_exceeded`` — the tenant's own 24-hour spend already reached its tier ceiling (body: ``error``, ``current_spend``, ``cap``, plus ``msg`` — a plain-language sentence an editor model can relay; no sums in it); ``cost_cap_monthly_exceeded`` — the calendar-month ceiling of the free tier is used up (same body plus ``retry_after`` seconds and the ``Retry-After`` header: the allowance comes back on the 1st at 00:00 UTC; when both ceilings are used up this one is returned); or ``platform_budget_paused`` — the platform-wide daily budget is used up (``Retry-After`` header).
- `413`: The document is refused before anything is stored. Detail ``error`` is one of: ``file_size_exceeded`` (tier file cap; ``max_mb``, ``size_mb``), ``payload_too_large`` (global byte guard; ``max_bytes``), ``payload_too_large_xl_cap`` (more than 50 000 words; ``max_words``, ``words``, ``parts_hint``), ``payload_too_large_xl_unavailable`` (between 15 000 and 50 000 words while the ANDISH_XL_SPLIT_ENABLED kill switch is off — large documents are temporarily not accepted, split the text to at most ``max_words`` words per upload; detail: ``max_words``, ``words``), ``storage_cap_exceeded`` or ``documents_cap_exceeded``.
- `422`: Validation Error

---

[API](https://skmtc.dev/andish/apis/mnemo-api.md) · [All operations](https://skmtc.dev/andish/apis/mnemo-api/llms.txt) · [OpenAPI document](https://skmtc.dev/andish/apis/mnemo-api/revisions/d5449f23414c?raw)
