/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 requires a project-scoped key. Keys are created and revoked under Projects → your project → API keys or via the API:
Endpoint map
Standard REST resources follow Django REST Framework conventions:GET list with query-param filtering, 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, and an interrupted trace with no root span lands like any
other). 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. POST /api/datasets/split/
takes the same body plus eval_percent and position and returns {train, eval}. The response is
the dataset; landing runs in the background, state moves from landing to diagnosing
while the agent runs its preparation turn, then idle. 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 queues every cell after it; 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. GET .../events/ streams
cell_started, cell_done, cell_failed, run_done, run_failed, chat_turn,
chat_step, chat_delta and chat_cell as server-sent events.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=&diff=1 pages a frame with
marks (rows added, values changed with the old value).4
Use it
Eval runs, optimiser experiments and finetuning jobs 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}.
The response carries X-Overmind-Cell, X-Overmind-Version and X-Overmind-Fingerprint;
the SDK 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).
- Errors:
401unauthenticated,402insufficient credits for compute-consuming actions when the credit gate is on (hosted; trace ingest is never credit-gated),403plan limits,404wrong project or missing resource. - IDs are UUIDs except spans (16-hex
span_id) and traces (32-hextrace_id).