> ## Documentation Index
> Fetch the complete documentation index at: https://solo-09d10f60.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`, `GET /control/dashboard`, and `GET /control/dashboard-v2` 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 keeps the bearer token in memory for API calls. Current deployments can also use a backend-mediated HttpOnly dashboard session cookie for refresh continuity. 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 now prefers bounded `/control/api/v1/*` read models, while older broad status surfaces remain available for compatibility and debug.
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 `/omx/event` remains available as an event-ingest compatibility hook; new docs should describe the product as Enoch Control Plane.

## Authentication

<AccordionGroup>
  <Accordion title="Authentication">
    Unauthenticated routes include `GET /healthz`, `GET /dashboard`, `GET /control/dashboard`, `GET /control/dashboard-v2`, and `GET /favicon.ico`. Dashboard data APIs and most control-plane routes require a bearer token or a scoped dashboard session 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 keeps the operator bearer token in memory and scrubs legacy browser storage/cookies when the token is entered. A backend-mediated HttpOnly session cookie can preserve dashboard refresh continuity without exposing the bearer token to JavaScript. Do not pass bearer tokens in dashboard URLs or persist them in localStorage/sessionStorage.
  </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"
    }
    ```

    Current deployments may set `store_backend` to `supabase` while using Postgres-backed storage; treat that value as a compatibility adapter label, not a Supabase Cloud claim.

    ***

    ### `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"`, `"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`        | `""`          | Raw compatibility/detail filter. Values include `triage_ready`, `unreviewed`, `in_review`, `changes_requested`, `blocked`, `approved_for_finalization`, `finalized`, and `rejected`. Do not present these as the operator workflow. |
    | `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.

    ### `POST /omx/event`

    Receives legacy OMX-compatible/Codex lifecycle events from the agent runtime. The worker gate uses these events to track process state and determine when a quiet window has been reached.

    ```bash theme={null}
    curl -fsS \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "event": "session-end",
        "run_id": "my-project-20250501T120000Z",
        "session_id": "sess-abc123",
        "project_id": "my-project",
        "project_name": "My Project"
      }' \
      http://worker.example:8787/omx/event
    ```

    Request body (`ControlPlaneEvent`; `OmxEvent` remains a deprecated compatibility alias) key fields:

    ```json theme={null}
    {
      "event": "session-end",
      "run_id": "...",
      "session_id": "...",
      "project_id": null,
      "project_name": null,
      "question": null,
      "root_pid": null,
      "process_group_id": null,
      "timestamp": "2025-05-01T12:00:00+00:00",
      "tmux_session": null,
      "message": null
    }
    ```

    Valid `event` values: `session-start`, `session-idle`, `session-stop`, `session-end`, `ask-user-question`.

    ***

    ### `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-v2`

    Returns the canonical 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 keeps it in memory for API calls. When the backend session endpoints are enabled, the dashboard can use an HttpOnly session cookie to recover after refresh without storing the bearer token in JavaScript-readable storage.

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

    ***

    ### `GET /control/dashboard`

    Legacy dashboard URL. Current deployments redirect to `/control/dashboard-v2`.

    ***

    ### `GET /dashboard`

    Legacy worker-gate dashboard URL. Current deployments redirect to `/control/dashboard-v2`.

    ***

    ### `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>
