# `POST /delivery/parse` Protected. Requires a valid session cookie (`credentials: 'include'`). Parse document (SSE) Accepts an invoice or delivery note as PDF or text file (`multipart/form-data`, field `file`) and streams extraction results as Server-Sent Events until parsing is complete. PDFs are read via `pdftotext -layout`; text files are decoded as UTF-8. Pages are separated by the form feed character (`\f`). If no form feed is present, the input is treated as a single page. The extracted pages are fed to an LLM (OpenRouter) which fills a structured manifest of header data and line items. The OpenRouter API key is read from the authenticated user's company setting `keys.openrouter` (or the server's `OPENROUTER_FREE_KEY` if not configured); the endpoint returns `400` if the key is not configured. The stream ends after the final `result` event (or an `error` event on failure). ## Body (`multipart/form-data`) | Field | Required | Type | Description | |-------|----------|------|-------------| | `file` | yes | file | Document to parse (max 50 MB, application/pdf or text file) | ## SSE Events The response is a `text/event-stream`. Events are emitted in order until the stream closes. | Event | Description | Payload | |-------|-------------|---------| | `status` | Document parse confirmation and LLM status messages | `{ "message": "…" }` | | `llm_call` | Fired before each LLM request | `{ "model": "…", "promptSize": 0, "messages": 0 }` | | `tool_call` | Fired when the output_manifest tool is invoked | `{ "name": "output_manifest" }` | | `usage` | Token usage after each LLM response | `{ "input": 0, "cached": 0, "output": 0, "cost": 0 }` | | `progress` | Intermediate accumulated extraction state (basedata / notes / items) | `ManifestResult` | | `result` | Final accumulated extraction — emitted just before the stream closes | `ManifestResult` | | `error` | Emitted on document parse or LLM failure; stream closes immediately after | `{ "message": "…" }` | ## Response `200` `text/event-stream` — emits `status`, `llm_call`, `tool_call`, `usage`, `progress`, `result` (and `error` on failure). See SSE Events table below. **ManifestResult shape** (used by `progress` and `result`): ```json { "basedata": { "date": "2026-01-15", "lieferant_id": 42, "lieferantName": "Muster GmbH", "manifestNr": "LS-2026-001", "manifestType": "lieferschein" }, "notes": ["Pos. 3: VPE-Angabe fehlte, Standardwert 1 angenommen."], "items": { "pagesFrom": 1, "pagesTo": 2, "items": [ { "position": 1, "count": 10, "vpe": 1, "unit": "Stk.", "articleName": "Einhängesieb Stahl", "liefArtNr": "ES-100", "mwst": "19", "eknetto": 450, "page": 1 } ] } } ``` ## Errors | Status | Description | |--------|-------------| | 400 | No file uploaded, unsupported file type, or OpenRouter key not configured — `{"error":"…"}` | | 401 | Unauthorized — `{"error":"…"}` | --- _Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._