Skip to main content
The Console is built on the same REST API you can call directly. Base URL:
An OpenAPI schema and interactive explorers are served by the API itself at /api/schema/, /api/docs/ (Swagger UI), and /api/redoc/. Coding agents get the same surface as typed tools over MCP.

Authentication

Three ways to authenticate; the first is right for almost all programmatic use: A key has a scope: project pins it to one project, account reaches every project the user is a member of. MCP and .overmind/credentials.toml use a project-scoped key; of the two, only an account-scoped key can POST /api/projects/. Keys are created and revoked under Projects → your project → API keys or via the API:
The create body takes name (optional) and either project (a project id → project-scoped) or scope ({"scope": "account"}, or {"scope": "project", "resourceIds": ["<project-uuid>"]}); the response carries the plaintext key once. The list returns metadata only: id, name, prefix, project, scope, is_active, expires_at, last_used_at, created_at. current/ returns the calling key’s scope document. Keys do not expire unless expires_at is set on the row; revoke what you no longer need.

Endpoint map

Standard REST resources follow Django REST Framework conventions: GET list with query-param filtering (paged, 25 per page), POST create, GET/PATCH/DELETE on /{id}/, plus the custom actions listed here.

Core resources

Evaluation

Optimisation, training, serving

Special endpoints

Building a dataset

A dataset lands its rows as-is and its agent shapes them cell by cell; a run uses one version. The same sequence backs the Console, the CLI, and the MCP dataset tools.
1

Land a source

A file goes up in chunks (POST /api/uploads/, then PUT /api/uploads/{id}/chunk/?offset=). POST /api/datasets/ with project, name, optional intent (train or eval) and capability, and exactly one source: upload_id (+ filename), rows or text for pasted data, or traces (trace_ids, or a trace-list selection of filters and/or search; one row lands per trace). The selection is validated and counted first: an unknown filter key or a selection that matches no trace is a 400, so a dataset never lands empty. The response is the dataset; landing runs in the background and state moves from landing to diagnosing while the agent runs its preparation turn, then idle (error on failure). Landing proposes capability_rank and the intent when they were left out.
2

Shape it

POST /api/datasets/{id}/cells/ adds a cell (title, script, note) at the end of the chain; PATCH .../cells/{id}/ edits it and a script change queues it and every cell after it; DELETE .../cells/{id}/ removes it unless it is frozen; POST .../run/ executes the queued cells in order (an unchanged cell keeps its frame). POST .../chat/ sends a message to the dataset’s agent, which lands cells itself; POST .../cells/{id}/accept/ runs a cell the agent proposed. Writes are refused with 409 while the agent is working. GET .../events/ streams land_started, land_progress, land_done, land_failed, run_started, cell_started, cell_done, cell_failed, run_done, run_failed, chat_turn, chat_thinking, chat_step, chat_delta, chat_cell, chat_failed, cells_changed and dataset_changed as server-sent events, replaying what you missed first.
3

Read a version

The dataset’s cells carry every version: version, state, rows, columns, intent_report and capability_report, fits against the intent, stats, and frozen. GET .../rows/?cell=&offset=&limit=&sort=&dir=&filters=&search=&diff=1 pages a frame (up to 500 rows) with marks (rows added, values changed with the old value) against the cell before it; GET .../columns/?cell= returns per-column stats.
4

Use it

Eval runs, optimisation runs and training runs take dataset and, optionally, cell; without it the active version is used. A version whose contracts do not fit the intent, a dataset of the other intent, or a cell of another dataset, is refused with the reason. A use freezes the version and starts a new major number.
5

Pull it

A local loop reads the used version, never the dataset: GET .../export/?fmt=jsonl&cell={id} (fmt=csv for CSV; a raw stream, not a use). The response carries X-Overmind-Cell, X-Overmind-Version and X-Overmind-Fingerprint; the CLI caches the file as .overmind/datasets/{cell}.jsonl with the fingerprint beside it and fetches again when it no longer matches.

Conventions

  • Filtering uses django-filter query parameters on list endpoints (the Console’s filter bar builds exactly these URLs — copy them from your browser). List responses are paged: count, next, previous, results.
  • Errors are JSON with detail and, when the client needs to tell cases apart, code: 401 unauthenticated, 402 insufficient_credits on launching paid work when the credit gate is on (hosted; a positive balance passes), 403 plan_limit_exceeded / seat_limit_exceeded on Free-plan caps, 403 guest_upgrade_required when a guest writes, 404 wrong project or missing resource, 409 conflict on a uniqueness race, 500 internal_error with an error_id to quote.
  • IDs are UUIDs except spans (16-hex span_id) and traces (32-hex trace_id).