> ## Documentation Index
> Fetch the complete documentation index at: https://solo-09d10f60-docs-audit-implementation-alignment.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# API endpoints

> A source-derived map of the main Enoch API surfaces.

Enoch serves control-plane and worker APIs from FastAPI apps that commonly listen on port `8787`. All endpoints except root `GET /healthz`, `GET /dashboard`, and `GET /control/dashboard` require a bearer token. The control-plane health endpoint is `GET /control/health` and does require authentication. The dashboard shell is unauthenticated for the initial page load; it prompts for a bearer token and stores it in `localStorage` for subsequent API calls. Read the Authentication section below before making your first request.

This page maps the main endpoint groups present in the local source snapshot. It is not a generated OpenAPI reference; verify request and response models against the code before building clients. The operator dashboard prefers bounded `/control/api/v1/*` read models, while older broad status surfaces remain available for compatibility and debug. Major groups not fully documented here include `POST /control/dispatch-one`, `POST /control/api/alerts/queue-check`, Research Facility routes under `/control/api/research/*`, and legacy dashboard routes such as `/dashboard/api`.
For current runtime topology and compatibility boundaries, see
[current runtime snapshot](/current-runtime-snapshot).

## API versioning

* `/control/api/v1/*` is the preferred bounded read API for dashboards, scripts, and external integrations.
* Older broad status and dashboard endpoints are compatibility/debug surfaces.
* Command endpoints are behavior-stable, but clients should verify request and response models against source until generated OpenAPI docs are published.
* Legacy event model alias `OmxEvent` (`ControlPlaneEvent`) remains for historical payloads; the HTTP route `POST /omx/event` was removed. Worker completion callbacks use `POST /control/api/worker-callback`.

## Authentication

<AccordionGroup>
  <Accordion title="Authentication">
    Unauthenticated routes include `GET /healthz`, `GET /dashboard`, `GET /control/dashboard`, and `GET /favicon.ico`. Dashboard data APIs and most control-plane routes require a bearer token or an explicit token bootstrap path where the dashboard code supports it.

    ```http theme={null}
    Authorization: Bearer <control_api_bearer_token>
    ```

    Pass the token in every request:

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      http://127.0.0.1:8787/control/api/status
    ```

    The dashboard stores the token in browser `localStorage` after the operator enters and saves it. Do not pass bearer tokens in dashboard URLs.
  </Accordion>

  <Accordion title="Health & Status">
    ### `GET /healthz`

    No authentication required. Returns a minimal app-level liveness check. Use authenticated `GET /control/health` when you need the control-plane database path and control-plane service timestamp.

    ```bash theme={null}
    curl -fsS http://127.0.0.1:8787/healthz
    ```

    Response:

    ```json theme={null}
    {"ok": true, "service": "enoch_worker_gate", "timestamp": "2025-05-01T12:00:00+00:00"}
    ```

    ### `GET /control/health`

    Requires bearer authentication. Returns control-plane health, the active store backend, and the configured database path (SQLite file path for local dev; Postgres adapter URL or label for production).

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      http://127.0.0.1:8787/control/health
    ```

    Response:

    ```json theme={null}
    {
      "ok": true,
      "service": "enoch-langgraph-control-plane",
      "db_path": "/var/lib/enoch-control-plane/state/control_plane.sqlite3",
      "store_backend": "sqlite",
      "timestamp": "2025-05-01T12:00:00+00:00"
    }
    ```

    Production on `enoch-core` sets `store_backend` to `supabase` with local Postgres; treat that value as a compatibility adapter label, not Supabase Cloud.

    ***

    ### `GET /control/api/status`

    Returns a compatibility dispatch-safety snapshot aggregated from the control-plane database. Add `?refresh_worker=true` to trigger a fresh worker preflight before responding. For bounded operator cards, prefer `GET /control/api/v1/overview`.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      "http://127.0.0.1:8787/control/api/status?refresh_worker=true" \
      | python3 -m json.tool
    ```

    Key response fields:

    | Field                    | Description                                                         |
    | ------------------------ | ------------------------------------------------------------------- |
    | `dispatch_safe`          | `true` when no blockers exist and no critical conflicts are present |
    | `dispatch_blockers`      | List of human-readable blocker strings                              |
    | `flags.queue_paused`     | `true` when the queue is paused                                     |
    | `flags.maintenance_mode` | `true` when maintenance mode is active                              |
    | `counts`                 | Per-status item counts plus `papers` and `queue_total`              |
    | `active_items`           | Queue rows currently in an active dispatch/run state                |
    | `source_freshness`       | Staleness of each evidence source                                   |
    | `warnings` / `conflicts` | Structured findings with severity, source, and suggested actions    |

    ***

    ### `GET /control/api/queue-health`

    Returns queue health with worker freshness details, active run detail, latest alert check results, and recent worker callbacks. Add `?refresh_worker=true` to refresh the worker preflight observation first.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      "http://127.0.0.1:8787/control/api/queue-health?refresh_worker=true" \
      | python3 -m json.tool
    ```
  </Accordion>

  <Accordion title="Bounded operator read models">
    The v1 read models are the preferred dashboard and integration surface. They answer operator questions with pagination, hard caps, and bounded SQL reads.

    ### `GET /control/api/v1/overview`

    Returns the operator overview cards, the top actionable items, and recent events. This is the main bounded dashboard snapshot.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      "http://127.0.0.1:8787/control/api/v1/overview?active_limit=5&event_limit=10" \
      | python3 -m json.tool
    ```

    Key overview fields include:

    | Field                                              | Meaning                                                                           |
    | -------------------------------------------------- | --------------------------------------------------------------------------------- |
    | `operator_counts`                                  | Canonical operator lanes such as attention, running, write, publish, and imported |
    | `operator_detail_counts`                           | Drill-down compatibility/detail counts                                            |
    | `paper_pipeline.write_needed`                      | Actionable positive paper work only                                               |
    | `paper_pipeline.raw_completed_no_paper_candidates` | Informational/debug rows before the decision gate                                 |
    | `paper_pipeline.not_writable_by_decision_gate`     | Rows rejected by the decision gate                                                |
    | `paper_pipeline.publish_ready`                     | Finalized drafts missing corpus import                                            |
    | `paper_pipeline.published_imported`                | Papers already represented in the corpus-import ledger                            |

    ### `GET /control/api/v1/lanes`

    Returns the active lane view and next candidate, with bounded active-item and count data.

    ### `GET /control/api/v1/queue`

    Returns a bounded queue list with cursor pagination, `page_size`, `status`, `search`, and `sort` controls.

    ### `GET /control/api/v1/runs`

    Returns a bounded run list with cursor pagination and filters for state, project, and search.

    ### `GET /control/api/v1/runs/{run_id}`

    Returns one run, its project, a bounded paper list, and a bounded event list.

    ### `GET /control/api/v1/projects/{project_id}`

    Returns one project, the current queue item, bounded run and paper lists, bounded events, and worker observations.

    ### `GET /control/api/v1/papers`

    Returns a bounded paper list with cursor pagination, `status`, `search`, and `sort` controls.

    ### `GET /control/api/v1/papers/{paper_id}`

    Returns one paper, its project, its run, and a bounded event list.

    ### `GET /control/api/v1/events`

    Returns a bounded audit/event list with optional entity, type, search, and payload filters.

    ### `GET /control/api/v1/observability/health`

    Returns route-observability health, if enabled.

    ### `GET /control/api/v1/observability/memory`

    Returns the controller process RSS and memory warning status.

    ### `GET /control/api/v1/automation-readiness`

    Returns long-haul automation readiness (`READY` or `BLOCKED`) with the first failing criterion when blocked.

    ### `GET /control/api/v1/research-quality`

    Returns Research Facility quality signals and admission diagnostics for the operator dashboard.

    ### `GET /control/api/v1/source-lineage`

    Returns source-lineage and dedupe context for intake and queue rows.

    ### `GET /control/api/v1/projects`

    Returns a bounded project list with cursor pagination and search filters.

    ### `POST /control/api/v1/followups/launch-next`

    Dry-runs or launches one bounded follow-up investigation from existing no-paper evidence. Used by queue-pump follow-up automation when enabled.
  </Accordion>

  <Accordion title="Queue Operations">
    ### `GET /control/api/queues/{queue}`

    Returns a paginated list of queue rows filtered to one logical queue group.

    | `queue` value | Description                                                                                                   |
    | ------------- | ------------------------------------------------------------------------------------------------------------- |
    | `active`      | Items in `dispatching`, `running`, `awaiting_wake`, `wake_received`, or `reconciling` status                  |
    | `queued`      | Items waiting to be dispatched                                                                                |
    | `blocked`     | Items in `blocked`, `needs_review`, or `dispatch_error` status, or any item with `manual_review_required` set |
    | `paused`      | Items in `paused` status                                                                                      |
    | `completed`   | Items in `completed` or `canceled` status                                                                     |
    | `all`         | All rows regardless of status                                                                                 |

    Query parameters:

    | Parameter   | Default             | Description                                                      |
    | ----------- | ------------------- | ---------------------------------------------------------------- |
    | `page`      | `1`                 | Page number (≥ 1)                                                |
    | `page_size` | `50`                | Rows per page (1–500)                                            |
    | `search`    | `""`                | Full-text filter across all string fields                        |
    | `status`    | `""`                | Exact `QueueStatus` value filter                                 |
    | `sort`      | `dispatch_priority` | Sort column; prefix with `-` for descending (e.g. `-updated_at`) |

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      "http://127.0.0.1:8787/control/api/queues/queued?page_size=20&search=my-project"
    ```

    ***

    ### `POST /control/dispatch-next`

    Selects and dispatches the next eligible candidate from the queue. Always use `dry_run: true` first to verify the candidate before committing a live dispatch.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"dry_run": true, "requested_by": "operator"}' \
      http://127.0.0.1:8787/control/dispatch-next \
      | python3 -m json.tool
    ```

    Request body (`DispatchNextRequest`):

    ```json theme={null}
    {
      "dry_run": true,
      "requested_by": "operator",
      "force_preflight": true
    }
    ```

    Response `action` values: `"paused"`, `"noop"`, `"dry_run_dispatch"`, `"live_dispatch"`. Live dispatch failures return HTTP `409`/`501`/`502` rather than an `action` of `dispatch_blocked`.

    ***

    ### `POST /control/api/preflight`

    Runs an explicit worker preflight check without committing a dispatch. Use this to verify worker connectivity, bearer token validity, and GPU/memory headroom before live dispatch.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "wake_gate_url": "http://worker.example:8787",
        "bearer_token": "worker-api-token",
        "require_paused": false,
        "strict": false
      }' \
      http://127.0.0.1:8787/control/api/preflight \
      | python3 -m json.tool
    ```

    Request body (`WorkerPreflightRequest`):

    ```json theme={null}
    {
      "wake_gate_url": "http://worker.example:8787",
      "bearer_token": "",
      "require_paused": true,
      "strict": false,
      "max_gpu_pct": 5.0,
      "min_memory_available_mib": 16384
    }
    ```

    Response (`WorkerPreflightResponse`):

    ```json theme={null}
    {
      "ok": true,
      "target": "http://worker.example:8787",
      "summary": "...",
      "checks": [
        {"name": "wake_gate_healthz", "ok": true, "detail": "...", "data": {}},
        {"name": "worker_no_live_runs", "ok": true, "detail": "...", "data": {}}
      ]
    }
    ```

    Optional request field `machine_target` selects a named entry from `worker_targets`. When set, the control plane resolves it to that target's `wake_gate_url`, bearer token, and memory floor.

    ***

    ### `POST /control/papers/draft-next`

    Drafts the next decision-gated positive paper candidate. Use `dry_run: true` first to preview eligibility without writing artifacts.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"dry_run": true, "requested_by": "operator"}' \
      http://127.0.0.1:8787/control/papers/draft-next \
      | python3 -m json.tool
    ```

    Request body (`DraftNextRequest`):

    ```json theme={null}
    {
      "dry_run": true,
      "requested_by": "operator",
      "force": false
    }
    ```

    Response `action` values: `"noop"`, `"dry_run_draft"`, `"drafted"`. Live calls skip candidates with missing evidence and return `action: "noop"` with skipped candidate details; they do not return HTTP 424.
  </Accordion>

  <Accordion title="Project Operations">
    ### `GET /control/api/projects/{project_id}`

    Returns project detail aggregating the project record, current queue item, all run records, associated papers, recent events, and cached worker observations.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      http://127.0.0.1:8787/control/api/projects/my-project-id \
      | python3 -m json.tool
    ```

    Returns `404` if neither a project record nor a queue item is found for `{project_id}`.
  </Accordion>

  <Accordion title="Paper Operations">
    Paper automation routes are mounted at both `/control/api/publication-automation/*` and `/control/api/paper-reviews/*`. Prefer `publication-automation` in new integrations.

    ### `GET /control/api/papers`

    Returns the full paper queue as a paginated list.

    Query parameters:

    | Parameter   | Default       | Description                                                       |
    | ----------- | ------------- | ----------------------------------------------------------------- |
    | `page`      | `1`           | Page number                                                       |
    | `page_size` | `50`          | Rows per page (1–500)                                             |
    | `search`    | `""`          | Full-text filter                                                  |
    | `status`    | `""`          | Exact `paper_status` filter                                       |
    | `sort`      | `-updated_at` | Sort column; prefix with `-` for descending (e.g. `paper_status`) |

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      "http://127.0.0.1:8787/control/api/papers?page_size=100"
    ```

    ***

    ### `GET /control/api/paper-reviews`

    Returns publication automation detail rows sorted by `rank_score` descending by default. Raw review-like values are compatibility/detail states for APIs and historical rows; the primary operator workflow is write -> finalize -> publish/import, with publish/import gated by the corpus-import ledger.

    Query parameters:

    | Parameter              | Default       | Description                                                                                                                                                                                                                                                        |
    | ---------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
    | `page`                 | `1`           | Page number                                                                                                                                                                                                                                                        |
    | `page_size`            | `50`          | Rows per page (1–500)                                                                                                                                                                                                                                              |
    | `search`               | `""`          | Full-text filter                                                                                                                                                                                                                                                   |
    | `review_status`        | `""`          | Automation state filter. Canonical values: `queued`, `claimed`, `blocked`, `finalized`, `deferred`, `rejected`. Legacy imported rows may still use values such as `triage_ready`, `unreviewed`, `in_review`, `changes_requested`, and `approved_for_finalization`. |
    | `paper_status`         | `""`          | One of `eligible`, `draft_generating`, `draft_review`, `publication_generating`, `publication_draft`, `human_review_required`, `archived`                                                                                                                          |
    | `include_rank_reasons` | `true`        | Include per-item rank reason arrays                                                                                                                                                                                                                                |
    | `sort`                 | `-rank_score` | Sort column; prefix with `-` for descending (e.g. `rank_score`)                                                                                                                                                                                                    |

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      "http://127.0.0.1:8787/control/api/paper-reviews?paper_status=publication_draft&include_rank_reasons=true"
    ```

    ***

    ### `GET /control/api/paper-reviews/next`

    Returns the highest-ranked publication automation detail item matching the given filters. Defaults to `paper_status=publication_draft`, excluding finalized and rejected items.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      "http://127.0.0.1:8787/control/api/paper-reviews/next?paper_status=publication_draft"
    ```

    Returns `404` if no matching item exists.

    ***

    ### `POST /control/api/paper-reviews/{paper_id}/rewrite-draft`

    Rewrites the paper draft artifacts for the given paper using the configured AI writer (e.g. GLM-5.1 via Synthetic.new). If `paper_evidence_sync_enabled` is `true`, the control plane syncs evidence from the worker before rewriting. When sync is enabled and local evidence is still missing, live calls return HTTP 424 with an `evidence_sync` detail object.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "idempotency_key": "rewrite-my-paper-001",
        "requested_by": "operator",
        "force": true
      }' \
      http://127.0.0.1:8787/control/api/paper-reviews/my-paper-id/rewrite-draft
    ```

    Request body (`PaperReviewRewriteDraftRequest`):

    ```json theme={null}
    {
      "idempotency_key": "paper-review-rewrite:<timestamp>",
      "requested_by": "operator",
      "force": true
    }
    ```

    ***

    ### `POST /control/api/paper-reviews/rewrite-batch`

    Rewrites up to `limit` papers in a single synchronous batch call. The request stays open until all papers are processed; a 10-paper batch may take several minutes.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "idempotency_key": "bulk-rewrite-001",
        "requested_by": "operator",
        "paper_status": "publication_draft",
        "limit": 10,
        "force": true,
        "dry_run": false,
        "skip_rewritten": true
      }' \
      http://127.0.0.1:8787/control/api/paper-reviews/rewrite-batch
    ```

    Request body (`PaperReviewBulkRewriteRequest`):

    ```json theme={null}
    {
      "idempotency_key": "paper-review-bulk-rewrite:<timestamp>",
      "requested_by": "ai-publication-pipeline",
      "paper_status": "publication_draft",
      "review_status": "",
      "search": "",
      "limit": 10,
      "force": true,
      "dry_run": false,
      "skip_rewritten": true
    }
    ```

    <Note>
      `limit` is capped at 200 by the model, but the dashboard uses a default of 10. Set `dry_run: true` to preview which papers would be selected without triggering any rewrites.
    </Note>

    ***

    ### `POST /control/api/paper-reviews/{paper_id}/prepare-finalization-package`

    Assembles the finalization package for a publication automation item. Use `dry_run: true` to preview the package manifest without writing the package. Use after the automated packaging checklist has passed.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "idempotency_key": "package-my-paper-001",
        "requested_by": "operator",
        "target_label": "public-release",
        "dry_run": true
      }' \
      http://127.0.0.1:8787/control/api/paper-reviews/my-paper-id/prepare-finalization-package
    ```

    ***

    ### `POST /control/api/paper-reviews/{paper_id}/approve-finalization`

    Compatibility endpoint with a legacy path name. It marks a paper automation item as ready for finalization; it is not the normal operator gate. Requires `idempotency_key` and `requested_by`.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "idempotency_key": "approve-my-paper-001",
        "requested_by": "operator",
        "note": ""
      }' \
      http://127.0.0.1:8787/control/api/paper-reviews/my-paper-id/approve-finalization
    ```

    ***

    ### `POST /control/api/paper-reviews/{paper_id}/status`

    Updates the raw `review_status` automation state for a paper item. This endpoint is retained for compatibility/detail workflows; it should not be presented as the public or operator paper path.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "idempotency_key": "status-my-paper-001",
        "requested_by": "operator",
        "review_status": "in_review",
        "note": "",
        "blocker": ""
      }' \
      http://127.0.0.1:8787/control/api/paper-reviews/my-paper-id/status
    ```

    Valid raw `review_status` values: `unreviewed`, `triage_ready`, `in_review`, `changes_requested`, `blocked`, `approved_for_finalization`, `finalized`, `rejected`. Public/operator copy should describe these as automation/detail states, not the operator lane labels. The primary operator model is write -> finalize -> publish/import.

    ***

    ### `POST /control/api/paper-reviews/{paper_id}/checklist/{item_id}`

    Updates the status of a single packaging/finalization checklist item.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "idempotency_key": "checklist-my-paper-item1-001",
        "requested_by": "operator",
        "status": "pass",
        "note": ""
      }' \
      http://127.0.0.1:8787/control/api/paper-reviews/my-paper-id/checklist/item1
    ```

    Valid `status` values: `pending`, `pass`, `fail`, `accepted_risk`, `not_applicable`.
  </Accordion>

  <Accordion title="Events & Audit">
    ### `GET /control/api/events`

    Returns the append-only audit event log. Supports filtering by `entity_type`, `entity_id`, `event_type`, and full-text `search`.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      "http://127.0.0.1:8787/control/api/events?page_size=200"
    ```

    Query parameters:

    | Parameter     | Default | Description                                                |
    | ------------- | ------- | ---------------------------------------------------------- |
    | `page`        | `1`     | Page number                                                |
    | `page_size`   | `100`   | Rows per page (1–500)                                      |
    | `entity_type` | `""`    | Filter by entity type (e.g. `paper_review`)                |
    | `entity_id`   | `""`    | Filter by entity ID                                        |
    | `event_type`  | `""`    | Filter by event type (e.g. `paper_review.draft_rewritten`) |
    | `search`      | `""`    | Full-text filter                                           |

    ***

    ### `GET /control/api/intake/notion`

    Returns the latest intake observation for the legacy Notion sync path, the current queued-project projection, skipped-row reason counts, and recent intake events. Current control-plane intake should normally be verified through the idea workbench and queue tables instead of this compatibility endpoint.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      http://127.0.0.1:8787/control/api/intake/notion
    ```

    ***

    ### `POST /control/api/worker-callback`

    Receives idempotent worker completion callbacks from the worker gate. Callbacks use an `idempotency_key`; duplicate keys with the same payload are accepted, conflicting payloads return `409`.
  </Accordion>

  <Accordion title="Worker Gate">
    These endpoints are served by the worker process and are called by the agent runtime and by the control plane during live dispatch.

    ### Legacy `POST /omx/event` (removed)

    The `ControlPlaneEvent` model remains in code (`OmxEvent` is a deprecated alias), but current releases do **not** register an HTTP route at `/omx/event`. Worker completion callbacks should target `POST /control/api/worker-callback` on the control plane with a bearer token that matches `control_api_bearer_token`.

    ***

    ### `POST /prepare-project`

    Prepares the project workspace on the worker before dispatch. Called automatically by the control plane's live dispatch path.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "run_id": "my-project-20250501T120000Z",
        "project_id": "my-project",
        "project_name": "My Project",
        "project_dir": "my-project",
        "prompt_file": "my-project/prompts/initial.md",
        "prompt_text": "# Enoch Research Action...",
        "overwrite": true
      }' \
      http://worker.example:8787/prepare-project
    ```

    ***

    ### `POST /dispatch`

    Dispatches an agent run to the worker. Called automatically by the control plane's live dispatch path after a successful prepare.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "run_id": "my-project-20250501T120000Z",
        "project_id": "my-project",
        "project_dir": "my-project",
        "prompt_file": "my-project/prompts/initial.md",
        "mode": "exec",
        "model": "gpt-5.5",
        "reasoning_effort": "medium",
        "sandbox": "danger-full-access"
      }' \
      http://worker.example:8787/dispatch
    ```

    Valid `mode` values: `exec`, `resume`. Valid `reasoning_effort` values: `low`, `medium`, `high`, `xhigh`.
  </Accordion>

  <Accordion title="Dashboard">
    ### `GET /control/dashboard`

    Returns the browser-based Enoch control dashboard shell as an HTML page. No authentication is required to load the page; the dashboard prompts for a bearer token and stores it in `localStorage`.

    ```bash theme={null}
    open http://127.0.0.1:8787/control/dashboard
    ```

    ***

    ### `GET /dashboard`

    Redirects to `/control/dashboard`.

    ***

    ### `GET /dashboard/api`

    Returns the full legacy worker dashboard snapshot as JSON. Use bounded `/control/api/v1/*` endpoints for primary operator dashboards and integrations.

    Query parameters:

    | Parameter     | Default | Description                   |
    | ------------- | ------- | ----------------------------- |
    | `limit`       | —       | Maximum run rows to include   |
    | `event_limit` | —       | Maximum event rows to include |

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      "http://worker.example:8787/dashboard/api?limit=50&event_limit=100"
    ```
  </Accordion>

  <Accordion title="Enoch Core">
    Enoch Core endpoints are prefixed `/enoch-core` and all require authentication.

    ### `GET /enoch-core/health`

    Returns the current Enoch Core mode and database path.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      http://127.0.0.1:8787/enoch-core/health
    ```

    Response (`HealthResponse`):

    ```json theme={null}
    {
      "ok": true,
      "service": "enoch_core",
      "mode": "shadow",
      "db_path": "/var/lib/enoch-control-plane/state/enoch_core.sqlite3",
      "timestamp": "2025-05-01T12:00:00+00:00"
    }
    ```

    Valid `mode` values: `off`, `shadow`, `compare`, `enforce`.

    ***

    ### `POST /enoch-core/snapshots/n8n-queue`

    <Warning>This endpoint retains its legacy `n8n-queue` name for backward compatibility, but n8n is no longer used. Any source can post queue snapshots.</Warning>

    Ingests a queue snapshot from any compatible source. The `idempotency_key` is required; the same key with the same payload is accepted idempotently, but the same key with a different payload returns `409`.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "idempotency_key": "snapshot-2025-05-01T120000Z",
        "source": "n8n",
        "mode": "shadow",
        "queue_rows": [],
        "paper_rows": [],
        "captured_at": "2025-05-01T12:00:00+00:00"
      }' \
      http://127.0.0.1:8787/enoch-core/snapshots/n8n-queue
    ```

    Response (`SnapshotIngestResponse`):

    ```json theme={null}
    {
      "ok": true,
      "mode": "shadow",
      "inserted": true,
      "event_id": 42,
      "snapshot_id": 7,
      "queue_rows": 0,
      "paper_rows": 0,
      "would_apply": false,
      "message": "snapshot recorded locally only; no external side effects performed"
    }
    ```

    ***

    ### `GET /enoch-core/projections/queue`

    Returns the queue projection computed from the latest ingested snapshot.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      http://127.0.0.1:8787/enoch-core/projections/queue
    ```

    Response (`QueueProjection`) key fields:

    ```json theme={null}
    {
      "ok": true,
      "mode": "shadow",
      "projection_version": "enoch-core.queue.v1",
      "total_queue_rows": 0,
      "total_paper_rows": 0,
      "status_counts": {},
      "active_rows": [],
      "draft_candidate_count": 0,
      "polish_candidate_count": 0,
      "warnings": []
    }
    ```
  </Accordion>
</AccordionGroup>
