Skip to content

REST API

Secretary exposes a JSON REST API used by the Web UI and available for programmatic control.


Authentication

Routes are split into three authentication tiers:

Tier Auth required Routes
Public None GET /api/health, POST /api/auth, POST /api/logout
UI (session) Session cookie (secretary_session JWT) GET /api/ui/*
Shared Session cookie or Authorization: Bearer <token> POST /api/run, POST /api/reload, GET /api/traces
Webhook Authorization: Bearer <SECRETARY_API_TOKEN> POST /api/webhook/paperless

Public routes

GET /api/health

Health check. Returns HTTP 200 when the service is running.

{ "status": "healthy" }

POST /api/auth

Exchange admin credentials for an HttpOnly session cookie.

Request body:

{ "username": "admin", "password": "your-password" }

Response: Sets secretary_session cookie on success. Returns HTTP 403 for invalid credentials or if no admin account is configured.


POST /api/logout

Clears the session cookie.


UI routes (/api/ui/*)

All UI routes require a valid session cookie.

GET /api/ui/me

Returns the authenticated admin username.

{ "username": "admin" }

GET /api/ui/config

Returns non-secret configuration values needed by the frontend.

{ "paperless_url": "https://paperless.example.com" }

GET /api/ui/schema

Returns the Rule JSON schema and the editor vocabulary used for the YAML editor autocomplete.


GET /api/ui/rules

Lists all rule files in the rules.d directory with their metadata.

{
  "files": [
    {
      "id": "<file-id>",
      "name": "invoices.yml",
      "path": "invoices.yml",
      "package": null,
      "is_vars": false,
      "has_draft": false
    }
  ],
  "loaded": {
    "invoice_acme_title": "Sets the title for ACME invoices.",
    "tag_inbox": null
  }
}

GET /api/ui/rules/{file_id}

Returns the raw YAML content of a rule file.

{
  "id": "<file-id>",
  "name": "invoices.yml",
  "path": "invoices.yml",
  "content": "---\n- id: ..."
}

PUT /api/ui/rules/{file_id}

Validates and saves the content of a rule file. The request body must be raw UTF-8 YAML. Max 512 KB.

On validation error:

{ "ok": false, "error": "YAML/Pydantic error message" }

On success:

{ "ok": true }

DELETE /api/ui/rules/{file_id}

Deletes the rule file.

{ "ok": true }

POST /api/ui/rules

Creates a new empty rule file.

Request body:

{ "filename": "my-new-rule.yml" }

Response:

{ "ok": true, "id": "<file-id>", "name": "my-new-rule.yml", "path": "my-new-rule.yml" }

POST /api/ui/rules/{file_id}/preview

Renders a _vars.yml file and returns the resulting YAML. Only available for _vars.yml files.

Request body: raw YAML content to preview.

Response:

{ "ok": true, "result": "rendered YAML string" }

Shared routes

POST /api/reload

Forces an immediate reload of all rule files from disk.

{ "ok": true }

POST /api/run

Runs rules against documents. Accepts query parameters:

Parameter Type Description
document int Paperless document ID
rule_id string Rule ID to execute
dry_run bool Default true

Dispatch table:

document rule_id Behaviour
✅ — All rules against one document (synchronous, returns traces)
— ✅ One rule against all documents (background task, returns event ID)
✅ ✅ One rule against one document (synchronous, returns traces)
— — HTTP 400

Example — dry-run a single document:

POST /api/run?document=42&dry_run=true

Response (synchronous):

{
  "ok": true,
  "queued": false,
  "document": 42,
  "rule_id": null,
  "dry_run": true,
  "traces": [ ... ]
}

Response (background task):

{
  "ok": true,
  "queued": true,
  "rule_id": "invoice_acme_title",
  "dry_run": false,
  "event_id": 7
}

GET /api/traces

Returns recent execution records from the SQLite history.

Parameter Default Description
limit 100 Maximum number of records to return
{
  "traces": [ ... ]
}