-
Notifications
You must be signed in to change notification settings - Fork 0
API Reference
Mailroom exposes a FastAPI server on port 8000 by default.
PYTHONPATH=src python -m api.main
# or
PYTHONPATH=src uvicorn api.main:app --host 0.0.0.0 --port 8000GET /health
Checks the API plus best-effort dependency health: LLM provider connectivity (resolves the sorter agent's provider, pings the models endpoint — no completion tokens spent) and database reachability (SELECT 1).
Response:
{
"status": "ok",
"service": "mailroom",
"checks": {
"llm_provider": {
"status": "ok",
"detail": "openrouter:qwen/qwen3.7-flash",
"provider": "openrouter"
},
"database": {
"status": "ok",
"detail": "database reachable"
},
"ingestion_paused": false,
"pause_info": null,
"inbox_pending": 3,
"watcher_heartbeat_seconds_ago": 2
}
}status is "ok" when all checks pass, "degraded" when any dependency is unreachable (e.g. provider resolution fails, missing API key, or the models endpoint is down). Dependency checks are best-effort and never block the response.
watcher_heartbeat_seconds_ago is the age of the watcher's liveness beacon — how recently the watcher process touched its heartbeat file. Uploads only drain (leave the inbox) while the watcher is running, so a null/growing value here means files will pile up in the inbox.
POST /upload
Upload a document to the pipeline inbox. The watcher picks it up automatically and runs the pipeline — no new pipeline run needs to be initialized per upload; the inbox is the queue.
The uploaded file is written to the inbox and a small <file>.meta sidecar persists the upload metadata (the submitted matter_id, a tracking upload_id, upload time, size). The watcher reads the sidecar so the document is filed under the matter you submitted. matter_id is honored directly — it does not fall back to the filename heuristic when provided.
Form Data:
| Field | Type | Required | Description |
|---|---|---|---|
file |
file | Yes | Document file to upload |
matter_id |
string | No | Matter ID (default: "DEFAULT") |
Response (202 Accepted):
{
"status": "accepted",
"file": "contract.pdf",
"upload_id": "1f2a3b4c5d6e",
"matter_id": "MATTER-001",
"message": "File queued for processing — watcher will pick it up."
}upload_id is the tracking id for this upload — it appears in the GET /queue listing until the file is claimed. The pipeline's doc_id (for GET /status/{doc_id}) is minted when the watcher starts processing, so poll GET /queue or watch the watcher logs for it.
Example:
curl -X POST http://localhost:8000/upload \
-F "file=@contract.pdf" \
-F "matter_id=MATTER-001"GET /queue
Live view of the inbox → processing queue: files currently queued in the inbox (with their /upload metadata, including the upload_id), files currently being processed by watcher workers, and the most recently updated documents from the catalog.
Response:
{
"queued": [
{
"file": "contract.pdf",
"size": 2048,
"upload_id": "1f2a3b4c5d6e",
"matter_id": "MATTER-001",
"uploaded_at": "2026-08-15T17:00:00+00:00"
}
],
"queued_count": 1,
"processing": [{"file": "other.pdf", "worker": "a1b2c3d4"}],
"processing_count": 1,
"recent": [
{
"doc_id": "550e8400-e29b-41d4-a716-446655440000",
"file": "done.pdf",
"matter_id": "MATTER-001",
"stage": "archived",
"doc_type": "contract",
"updated_at": "2026-08-15T17:05:00+00:00"
}
],
"timestamp": "2026-08-15T17:06:00+00:00"
}Auth-gated like the other management endpoints.
POST /review/{doc_id}/resolve
Resolve a document that's been routed to human review.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
doc_id |
string | Document ID from the manifest |
Form Data:
| Field | Type | Required | Description |
|---|---|---|---|
decision |
string | Yes |
approved or rejected
|
notes |
string | No | Reviewer notes |
Response:
{
"status": "ok",
"doc_id": "550e8400-e29b-41d4-a716-446655440000",
"decision": "approved",
"notes": "Classification confirmed — proceed"
}Errors:
-
400: Document not in review stage -
400: Invalid decision value -
404: Manifest not found
GET /status/{doc_id}
Retrieve the current pipeline status of a document.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
doc_id |
string | Document ID |
Response:
{
"doc_id": "550e8400-e29b-41d4-a716-446655440000",
"matter_id": "MATTER-001",
"stage": "archived",
"doc_type": "contract",
"classification_confidence": 0.95,
"extraction_confidence": 0.91,
"escalation_reason": null,
"created_at": "2024-01-15T10:30:00.000Z",
"updated_at": "2024-01-15T10:30:15.000Z"
}Possible stages: inbox, processing, classified, review, failed, archived
GET /matters/{matter_id}
List all documents associated with a matter.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
matter_id |
string | Matter ID |
Response:
{
"matter_id": "MATTER-001",
"document_count": 3,
"documents": [
{
"doc_id": "550e8400-...",
"original_filename": "msa.pdf",
"doc_type": "contract",
"stage": "archived",
"classification_confidence": 0.95,
"extraction_confidence": 0.91
}
]
}GET /audit/{doc_id}
Retrieve the full hash-chained audit trail for a document, including a validity check.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
doc_id |
string | Document ID |
Response:
{
"doc_id": "550e8400-...",
"chain_length": 5,
"chain_valid": true,
"entries": [
{
"entry_id": "...",
"event": "classified",
"actor": "sorter",
"detail": {"doc_type": "contract", "confidence": 0.95},
"prev_hash": "",
"entry_hash": "a1b2c3...",
"timestamp": "2024-01-15T10:30:01.000Z"
},
{
"entry_id": "...",
"event": "extracted",
"actor": "contracts_specialist",
"detail": {"confidence": 0.91},
"prev_hash": "a1b2c3...",
"entry_hash": "d4e5f6...",
"timestamp": "2024-01-15T10:30:05.000Z"
}
]
}The chain_valid field is true if all hash links are intact. false indicates tampering or corruption.
GET /ops/status
Get pipeline-wide operational metrics.
Response:
{
"stuck_documents": 0,
"review_queue": 2,
"error_rates": {
"contract": {"total": 45, "failed": 1, "review": 3},
"corporate_record": {"total": 12, "failed": 0, "review": 0}
},
"timestamp": "2024-01-15T10:35:00.000Z"
}| Field | Description |
|---|---|
stuck_documents |
Documents in processing or inbox state for >15 minutes |
review_queue |
Documents awaiting human review |
error_rates |
Per-doc-type breakdown: total, failed, and review counts |
POST /ops/sweep
Run a one-off Boss ops-monitor sweep on demand (same logic as the scheduled pipeline/ops_monitor.py, without waiting for the interval). Gathers system metrics, runs the Boss agent's analysis, and — if the Boss recommends pause_ingestion — writes the ops_monitor_paused flag (which the watcher honors). Use this to inspect system health interactively or to trigger a pause without touching the running monitor.
Response:
{
"status": "ok",
"findings": ["review backlog growing: 12 documents waiting"],
"severity": "warning",
"recommended_action": "alert",
"paused_ingestion": false,
"timestamp": "2024-01-15T10:35:00.000Z"
}Errors:
-
500: Metrics gathering or Boss analysis failed
POST /ops/resume
Clear the ops_monitor_paused flag so the watcher resumes processing new files. The watcher honors the flag on every file event, so this takes effect without a restart.
Response:
{
"status": "ok",
"was_paused": true,
"paused_ingestion": false
}was_paused is true if the pause flag existed and was cleared; false if ingestion was not paused.
All errors follow a consistent format:
{
"detail": "Error message description"
}| Status | Meaning |
|---|---|
400 |
Bad request — invalid input |
404 |
Resource not found |
500 |
Internal server error / database unavailable |
When the API is running, visit:
-
Swagger UI:
http://localhost:8000/docs -
ReDoc:
http://localhost:8000/redoc
The Mailroom API is currently unversioned. All endpoints are served from the root path (/) without a version prefix. This is acceptable while the API is internal and pre-1.0, but the following conventions apply:
| Concern | Policy |
|---|---|
| Current status | Unversioned (pre-1.0), internal use only |
| Version prefix | Planned: /v1/ when the first breaking change ships |
| Backwards compatibility | Breaking changes are grouped into a single release; the old route set is deprecated for one minor release before removal |
| Response evolution | Additive fields in JSON responses are allowed within a version (consumers must ignore unknown fields) |
| Removal of fields | Always a breaking change → new version |
| Content type |
application/json only |
- Treat the API as unstable: pin to the Mailroom release you integrate against (see
CHANGELOG.md). - Do not depend on undocumented response fields — only fields documented in this reference are stable.
- Breaking changes are announced in
CHANGELOG.mdunder the "Breaking changes" section of the release.
When versioning ships, routes will move under a prefix:
GET /v1/health
POST /v1/upload
GET /v1/queue
POST /v1/review/{doc_id}/resolve
GET /v1/status/{doc_id}
GET /v1/matters/{matter_id}
GET /v1/audit/{doc_id}
GET /v1/ops/status
The unversioned routes will continue to work during the deprecation window, then be removed.
Mailroom — Multi-Agent Legal Document Processing Pipeline. Built with LangGraph and OpenRouter; SQLite by default, Postgres optional.
- Repo docs/ — canonical docs (architecture, agents, configuration, API, deployment, local models)
- Sister Repositories — the llm-mailroom umbrella map