The API stores the original binary in object storage and inserts a documents row.
A processing_jobs record is inserted and published to pg-boss.
The worker selects the configured parse provider from the registry and optionally a fallback provider.
The active parse provider converts the source file into a normalized parsed-document model with text, pages, lines, blocks, optional tables, optional key-value pairs, optional chunk hints, and provider metadata.
Metadata extraction applies shared normalization for dates, currencies, amounts, correspondents, document types, confidence scoring, and review evidence.
Chunk generation derives deterministic stored chunks from normalized parse output and provider chunk hints when available.
If semantic indexing is configured, the worker enqueues a document.embed job after successful chunk persistence.
The embedding worker upserts chunk-level vectors keyed by document id, chunk index, provider, and model.
The document becomes searchable through PostgreSQL full-text search, structured filters, and the hybrid semantic endpoint.
Semantic indexing stores embeddings per chunk, not per document.
Embeddings are stored in document_chunk_embeddings with provider, model, dimensions, content hash, and vector payload.
The active embedding provider is global in v1 and selected through config.
POST /api/search/semantic applies structured filters first, then combines:
PostgreSQL full-text ranking
vector similarity over chunk embeddings
weighted reciprocal rank fusion
Semantic responses stay document-centric and include matched chunks as explainability data.
POST /api/search/answer and POST /api/search/answer/stream first pass through a search orchestrator.
The orchestrator can answer from structured archive state for operational queries such as:
open or overdue invoices and deadlines
documents pending review
expiring contracts
Structured answers return route: structured plus structuredData, and may legitimately return empty citations and results when the answer comes from normalized archive fields instead of chunk retrieval.
When no structured route applies, POST /api/search/answer falls back to the retrieval stack, returns grounded citations, and can explicitly decline to answer when evidence is too weak.
POST /api/search/answer/stream uses server-sent events with search-results, answer-token, done, and error events. Structured routes emit an immediate done payload after an empty search-results event.
Processing lifecycle status is limited to pending, processing, ready, and failed.
Review state is persisted separately with reviewStatus, reviewReasons, reviewedAt, and reviewNote.
Documents expose structured metadata.reviewEvidence so review callers can inspect missing invoice fields, OCR text length, thresholds, and active review reasons.
Documents also expose parseProvider, chunkCount, embedding summaries, provider-aware metadata.parse / metadata.chunking namespaces, and metadata.manual for sticky user overrides.
GET /api/documents/review returns the review queue.
GET /api/documents/:id/history returns audit history for document lifecycle and user changes.
POST /api/documents/:id/review/resolve marks manual review complete.
POST /api/documents/:id/review/requeue clears review state and publishes a fresh processing job.
GET /api/documents/:id/download/searchable returns the derived searchable PDF when one exists.
Both web and mobile clients consume the same archive-wide search SSE contract.
GET /api/archive/export, POST /api/archive/import, and POST /api/archive/watch-folder/scan expose portability and automated-ingestion primitives.
GET/POST/PATCH/DELETE plus merge endpoints under /api/taxonomies/* expose curation flows for tags, correspondents, and document types.
GET /api/health exposes provider configuration metadata including the active parse provider, active embedding provider, and available credential-backed capabilities.
GET /api/health/live, GET /api/health/ready, and GET /api/metrics expose process health and runtime metrics.
Metrics include processing outcomes, parse outcomes by provider, embedding outcomes by provider, durations, queue depth for both queues, pending-review gauges by reason, and stale-embedding gauges.
pnpm --filter @openkeep/api test:unit runs pure Node unit coverage.
pnpm --filter @openkeep/api test:integration runs the Testcontainers-backed API suite against PostgreSQL and MinIO.
pnpm --filter @openkeep/api test:ocr runs OCR acceptance coverage and should be executed in a worker-capable environment with the same OCR binaries and Tesseract language data as the production worker image.
pnpm test:e2e:google, pnpm test:e2e:google:gemini, pnpm test:e2e:aws, pnpm test:e2e:azure, and pnpm test:e2e:mistral run live parse-provider acceptance tests against configured cloud adapters.
pnpm test:e2e:openai-embeddings, pnpm test:e2e:gemini-embeddings, pnpm test:e2e:voyage, and pnpm test:e2e:mistral-embeddings run live embedding-provider acceptance tests.
Retrieval evaluation is present in test coverage but not yet exposed as a dedicated operator-facing benchmark command.
Workflow automation and richer custom fields are still future phases. (Email ingestion shipped with #253: IMAP poller, sender guard, and the status surface.)
The Electron desktop client, web app, and React Native mobile client share the backend contracts; desktop-specific native features are layered onto the Electron runtime boundary.