HTTP API
The whole HTTP surface on one page. Generated from the specification the server routes are built from, so an endpoint documented here is an endpoint that exists.
Generated file
Every endpoint section on this page is produced by tools/codegen from spec/mcpd.spec.json. Do not edit it by hand. Change the spec, run make gen, and the server, the SDKs, the OpenAPI document and this page all move together. make check fails the build when any of them drift apart.
There are 72 endpoints under specification 1.0.0. Four of them accept signals, four answer a Kubernetes probe or a Prometheus scrape, and the other sixty-four read back what was stored, attach a judgement to it, or delete it. If you would rather generate a client than read this, spec/openapi.json is emitted from the same file.
Before the endpoints
Four things are true of the API as a whole, so they are stated here once rather than repeated under every endpoint below.
Address
There is no hosted API and no base URL to look up. The API is served by your own daemon on whatever address it listens on, which is 0.0.0.0:4317 unless you set --listen. The operator interface is served from the same port, so the address in your browser is the address your client uses.
a read against a local daemon
curl -s 'http://localhost:4317/v1/api/traces?project=default&limit=5' Keys and scope
Present a key as Authorization: Bearer <key>, or as the x-mcpd-key header if a proxy has taken the first one. There are six scopes, and each endpoint below names the one it takes. ingest writes signals and reads nothing. read reads stored signals, which is where the prompts are. write changes stored data: scores, feedback, datasets, experiments and acknowledgements. execute starts, cancels and signals runs. worker claims steps and reports what happened to them. admin deletes, and reads the audit log. Handing a fleet of agents a key that can also read back every stored prompt is the mistake the split exists to prevent.
Configure them with --ingest-key, --read-key, --write-key, --admin-key, --execute-key and --worker-key. Four of them fall back: an unset write list accepts the ingest keys, an unset admin list accepts the read keys, and unset execute and worker lists accept the write keys, so a deployment configured before the narrower scopes existed keeps the reach it had. With no keys set at all, every scope is open: that is what makes the first run take five minutes, and it is also why the daemon warns at startup when it is listening on anything other than loopback with a scope open. Security has the full table.
Failures
Every non-2xx response is JSON with the same three fields: a stable error code to branch on, a message written for whoever has to fix it, and field when one parameter is at fault. A response that only says 500 costs the reader an hour, so nothing here returns one without saying what happened.
Paging
Lists page by cursor, never by offset. A response carries next_cursor until the last page, and you pass it back as cursor. Offsets skip and repeat rows when new traces arrive between two requests, which on an ingest path that is always writing is most of the time. Cursors are opaque: read them, do not build them.
What a write goes through
The four ingest endpoints differ only in what they decode. After that they share one pipeline, and its order is the part worth knowing: redaction runs before storage, not as a cleanup job afterwards. A value a rule matches is never written down, so there is no window in which it existed on disk and no job that has to find it again.
-
Decode
OTLP or native, gzip or plain
-
Sample
Decided once, at the root
-
Redact
Before anything is written
-
Price
From the configured price book
-
Store
One transaction per batch
Redaction is stage three, not a cleanup job. A value that a rule matches never reaches storage at all.
The endpoints
Grouped as the specification groups them. A method takes its colour from what the request does to your data: a read is nominal, a write asks for attention, a delete destroys.
Ingest
Receive OpenTelemetry traces
/v1/tracesThe standard OTLP/HTTP trace endpoint. Accepts protobuf (application/x-protobuf) and JSON (application/json), gzip-encoded or uncompressed. Point any OpenTelemetry SDK here and mcpd will record its spans, mapping GenAI and OpenInference semantic conventions onto mcpd’s own model. This is the endpoint that makes mcpd framework-agnostic: nothing about it requires an mcpd SDK.
Request body: Otlp.ExportTraceServiceRequest
Response: Otlp.ExportTraceServiceResponse
Authentication: an API key with ingest scope.
Receive OpenTelemetry logs
/v1/logsThe standard OTLP/HTTP log endpoint. Records correlate to spans automatically through their trace and span ids.
Request body: Otlp.ExportLogsServiceRequest
Response: Otlp.ExportLogsServiceResponse
Authentication: an API key with ingest scope.
Receive OpenTelemetry metrics
/v1/metricsThe standard OTLP/HTTP metric endpoint, including exemplars, which mcpd turns into links back to the exact span that produced a data point.
Request body: Otlp.ExportMetricsServiceRequest
Response: Otlp.ExportMetricsServiceResponse
Authentication: an API key with ingest scope.
Receive mcpd signals directly
/v1/ingestAccepts mcpd’s own shapes for all five signals in one batch, posted as a single IngestBatch object, gzip-encoded or uncompressed. Use this when you want the full model without translating through OTLP: token detail, cost, scores and feedback all survive intact. Supply ingest_key on a batch to make retries idempotent.
Request body: IngestBatch
Response: IngestResult
Authentication: an API key with ingest scope.
Traces
List traces
/v1/api/tracesThe trace list. Filter on status, kind, name, tags, session, thread, user, environment, model, time window, duration, cost and score, and sort by start time, duration, cost, span count or score. Unlike a plain span dump, this reads the materialised trace projection rather than aggregating spans on every request.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
q |
string |
· | Full-text search across span names, prompts and completions. This is the field that answers ‘find the run where the model said that’. |
status |
SpanStatus |
· | Only traces with this outcome. |
kind |
SpanKind |
· | Only traces whose root is this kind. |
name |
string |
· | Exact root span name. |
tags |
string[] |
· | Traces carrying all of these tags. |
session_id |
string |
· | Every trace in one conversation. |
thread_id |
string |
· | Every trace in one thread. |
user_id |
string |
· | Every trace for one end user. Stored ids are hashed, and this value is hashed the same way before matching, so pass the plain id you sent. |
environment |
string |
· | Restrict to one environment. |
model |
string |
· | Traces that used this model. |
from |
timestamp |
· | Start of the time window, inclusive. |
to |
timestamp |
· | End of the time window, exclusive. |
min_duration_ms |
u64 |
· | Only traces at least this slow. |
min_cost |
f64 |
· | Only traces at least this expensive. |
has_score |
string |
· | Only traces carrying a score from this scorer. |
score_below |
f64 |
· | Combined with has_score: only traces where that scorer came in under this value. This is the query that finds bad answers. |
sort |
string |
start_time |
One of start_time, duration, cost, span_count, score. Defaults to start_time. |
order |
string |
desc |
asc or desc. Defaults to desc. |
limit |
u32 |
50 |
Page size, 1 to 500. |
cursor |
string |
· | Opaque cursor from a previous page. |
Response: TraceListResponse
Authentication: an API key with read scope.
Get one trace with every span
/v1/api/traces/{trace_id}Returns the trace projection and the full span tree in one response, ordered so a client can render the waterfall without a second request or a client-side sort.
Path parameters
| Name | Type | Description |
|---|---|---|
trace_id |
string |
The trace to fetch. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
include |
string |
· | Comma-separated extras: logs, scores, feedback, metrics. Fetching a run and everything attached to it is one request. |
Response: TraceDetail
Authentication: an API key with read scope.
Delete a trace and everything attached to it
/v1/api/traces/{trace_id}Removes the trace, its spans, logs, metrics, scores and feedback. Used to satisfy an erasure request. The deletion is recorded in the audit log.
Path parameters
| Name | Type | Description |
|---|---|---|
trace_id |
string |
The trace to delete. |
Response: DeleteResult
Authentication: an API key with admin scope.
List the projects that have data
/v1/api/projectsEvery project mcpd has stored a signal for. Projects are not created up front: a project exists as soon as something arrives carrying its name, which is why this reads the data rather than a registry.
Response: String
Authentication: an API key with read scope.
Discovery
The values a filter dimension takes
/v1/api/discoveryWhat to offer in a filter box. A filter is only usable if you know what to type into it, and the values are not a fixed list: they are whatever the services reporting to this project happen to send. This reads the distinct-value list the daemon maintains as signals arrive, scoped to one project, prefix-matched for typeahead and ordered by value. Ask for one dimension at a time. One limit is worth knowing before you build against it: metric_label_key lists every label key seen anywhere in the project, not the keys of one named metric, because the list is keyed by the label and not by the metric that carried it.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Every dimension is scoped to one. |
facet |
Facet |
required | Which dimension to list. Required. |
key |
string |
· | For metric_label_value, the label key whose values you want. Required for that facet, because the answer would otherwise be every label’s values mixed together. Ignored by every other facet. |
prefix |
string |
· | Offer only values starting with this. Matched literally and case sensitively, because these values are what a filter will be compared against and folding here would offer one that then matches nothing. |
limit |
u32 |
100 |
How many values to return, up to 1000. A filter box shows a list somebody reads; ten thousand environments is not a longer list, it is a slower request that renders the same first page. |
Response: DiscoveryResponse
Other responses
| Status | Body | When |
|---|---|---|
501 |
· | This deployment’s storage backend does not implement what this endpoint needs. The body names the capability in its capability field, using the same word GET /v1/api/capabilities publishes, so a caller can read the capability set once and stop asking rather than retrying a refusal that will never change. |
Authentication: an API key with read scope.
What has arrived since you last looked
/v1/api/deltasRefresh a list in place instead of running its query again. Hold the cursor from the last poll, send it back, and get the rows that have arrived since, oldest first. The watermark is a row’s arrival time and its id together, because a batch writes every row in it with one timestamp and a watermark that could only say ‘after this nanosecond’ would either replay the whole batch or skip the rest of it. A row written with an arrival time behind the watermark, which a sender can cause by setting received_at itself, is not returned; that is the cost of a watermark and no cursor shape avoids it.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to poll. |
signal |
DeltaSignal |
traces |
Which signal to poll. Defaults to traces. |
cursor |
string |
· | The cursor the previous poll returned, unchanged. Absent starts from since, or from the oldest row the project holds when that is absent too. A cursor this daemon did not issue is refused rather than guessed at. |
since |
timestamp |
· | Where to start when there is no cursor yet. Ignored once cursor is set. |
limit |
u32 |
100 |
Page size, 1 to 1000. |
Response: DeltaResponse
Other responses
| Status | Body | When |
|---|---|---|
501 |
· | This deployment’s storage backend does not implement what this endpoint needs. The body names the capability in its capability field, using the same word GET /v1/api/capabilities publishes, so a caller can read the capability set once and stop asking rather than retrying a refusal that will never change. |
Authentication: an API key with read scope.
Spans
Search spans across traces
/v1/api/spansSearch individual steps rather than whole runs. This is how you answer ‘which tool call is slow’ or ‘show me every call to this model that errored’, across every trace at once.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
q |
string |
· | Full-text search across name, input and output. |
kind |
SpanKind |
· | Only this kind of step. |
status |
SpanStatus |
· | Only this outcome. |
name |
string |
· | Exact span name. |
model |
string |
· | Only spans against this model. |
trace_id |
string |
· | Restrict to one trace. |
from |
timestamp |
· | Start of the window. |
to |
timestamp |
· | End of the window. |
min_duration_ms |
u64 |
· | Only spans at least this slow. |
sort |
string |
start_time |
start_time, duration or cost. |
order |
string |
desc |
asc or desc. |
limit |
u32 |
50 |
Page size, 1 to 500. |
cursor |
string |
· | Opaque cursor. |
Response: SpanListResponse
Authentication: an API key with read scope.
Logs
Search logs
/v1/api/logsLog search, filtered by minimum severity, text, time window, trace and span. Because logs carry trace ids, every result links straight to the run that produced it.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
q |
string |
· | Full-text search across the message and structured fields. |
level |
LogLevel |
· | Minimum severity, inclusive. |
trace_id |
string |
· | Only logs from this run. |
span_id |
string |
· | Only logs from this step. |
from |
timestamp |
· | Start of the window. |
to |
timestamp |
· | End of the window. |
order |
string |
desc |
asc or desc. Defaults to desc, newest first, which is where an investigation starts. |
limit |
u32 |
100 |
Page size, 1 to 1000. |
cursor |
string |
· | Opaque cursor. |
Response: LogListResponse
Authentication: an API key with read scope.
Metrics
Query a metric as a time series
/v1/api/metricsAggregates raw points into time buckets and returns a series per label combination. Supports sum, count, mean, min, max and quantiles, so a chart is one request rather than a client-side fold over raw points.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
name |
string |
required | Metric name. Required. |
from |
timestamp |
required | Start of the window. Required. |
to |
timestamp |
required | End of the window. Required. |
bucket |
string |
· | Bucket width, such as 1m, 5m, 1h. Chosen automatically when omitted. |
agg |
string |
mean |
sum, count, mean, min, max, p50, p90, p95 or p99. |
group_by |
string[] |
· | Label keys to split the series on. |
filter |
string |
· | Label equality filters, as key:value pairs joined by commas: model:gpt-4o,status:ok. A point matches when every named label has that value. A colon, not =, so the value needs no escaping in a query string. A key cannot contain : or ,, and a value cannot contain ,. A JSON object here matches no point, and the answer is an empty series. |
Response: MetricSeriesResponse
Authentication: an API key with read scope.
Evaluation
List scores
/v1/api/scoresEvery machine evaluation, filterable by scorer, source, run, experiment, value range and time.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
scorer_id |
string |
· | Only this scorer. |
source |
ScoreSource |
· | Only scores from this source. |
trace_id |
string |
· | Only scores for this run. |
experiment_id |
string |
· | Only scores recorded by this experiment. |
min_value |
f64 |
· | Lower bound, inclusive. |
max_value |
f64 |
· | Upper bound, inclusive. |
from |
timestamp |
· | Start of the window. |
to |
timestamp |
· | End of the window. |
limit |
u32 |
50 |
Page size. |
cursor |
string |
· | Opaque cursor. |
Response: ScoreListResponse
Authentication: an API key with read scope.
Attach a score
/v1/api/scoresRecords a machine evaluation against a span or a trace. Supply min and max: a bare number with no declared scale cannot be compared to anything.
Request body: Score
Response: Score
Authentication: an API key with write scope.
Attach human feedback
/v1/api/feedbackRecords a person’s judgement of a run or a step. Call this from your own product’s thumbs-up control and the judgement lands on the exact trace that produced the answer.
Request body: Feedback
Response: Feedback
Authentication: an API key with write scope.
List human feedback
/v1/api/feedbackThe human judgements on one run, read from the trace detail. trace_id is required and is refused when absent: mcpd cannot yet list a whole project’s feedback, and answering an empty list instead would read as though nobody had judged anything. Every other parameter is refused by name as well, so a filter this endpoint does not apply cannot come back as a plausible wrong answer.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
trace_id |
string |
required | The run whose judgements to read. Required. |
limit |
u32 |
50 |
Page size. |
Response: FeedbackListResponse
Authentication: an API key with read scope.
List the built-in scorers
/v1/api/scorersEvery scorer mcpd ships, with what it measures and whether it needs configuration. Scorers are deterministic code, not model calls, so they cost nothing to run and give the same answer twice.
Response: ScorerInfo
Authentication: an API key with read scope.
Run scorers over stored runs
/v1/api/scoreScores runs that are already stored, and records the results. Naming a scorer that does not exist fails the request before any work happens; a scorer that cannot answer on a given run is reported in skipped rather than failing it.
Request body: ScoreRunRequest
Response: ScoreRunResult
Authentication: an API key with write scope.
Compare two experiments
/v1/api/experiments/compareReports the per-scorer delta between two experiments beside the sample size on each side, and refuses to call a comparison decidable below twenty observations.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
baseline |
string |
required | The experiment to compare against. |
candidate |
string |
required | The experiment being judged. |
project |
string |
· | Which project to read. |
Response: ExperimentComparison
Authentication: an API key with read scope.
Analytics
Headline numbers for a time window
/v1/api/analytics/summaryRun count, error rate, latency quantiles, token totals and cost for a window, optionally split by a dimension. One request backs the overview screen.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
from |
timestamp |
· | Start of the window. Defaults to 24 hours before to. |
to |
timestamp |
· | End of the window. Defaults to now. |
group_by |
string |
· | Split by model, entity_name, environment, status or session_id. |
Response: AnalyticsSummary
Authentication: an API key with read scope.
Cost broken down
/v1/api/analytics/costSpend over a window, split by model, provider, entity, environment, status, session, user, service or run name, with token detail behind each figure. Answers ‘where is the money going’ without exporting to a spreadsheet.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
from |
timestamp |
· | Start of the window. Defaults to seven days before to. |
to |
timestamp |
· | End of the window. Defaults to now. |
group_by |
string |
model |
model, provider, entity_name, environment, status, session_id, user_id, service_name or name. |
bucket |
string |
· | Time bucket for a series, such as 1h or 1d. Omit for totals only. |
Response: CostBreakdown
Authentication: an API key with read scope.
Datasets
List datasets
/v1/api/datasetsEvery dataset in the project, with its case count.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
· | Which project to read. |
Response: Dataset
Authentication: an API key with read scope.
Create a dataset
/v1/api/datasetsA dataset is a named set of cases to run a version of an agent against. Leave dataset_id empty and mcpd assigns one.
Request body: Dataset
Response: Dataset
Authentication: an API key with write scope.
List the cases in a dataset
/v1/api/datasets/{dataset_id}/itemsThe cases themselves, each with its input, its expected answer when it has one, and the run it was captured from.
Path parameters
| Name | Type | Description |
|---|---|---|
dataset_id |
string |
Which dataset. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
limit |
u32 |
500 |
Page size. |
Response: DatasetItem
Authentication: an API key with read scope.
Add cases to a dataset
/v1/api/datasets/{dataset_id}/itemsAdds cases written by hand or exported from somewhere else. Leave each item_id empty and mcpd assigns one.
Path parameters
| Name | Type | Description |
|---|---|---|
dataset_id |
string |
Which dataset. |
Request body: DatasetItem
Response: ItemsAdded
Authentication: an API key with write scope.
Capture production runs as cases
/v1/api/datasets/{dataset_id}/captureTurns runs that already happened into permanent cases. This is what datasets are for: the run that went wrong in production on Tuesday becomes a regression test that every later version has to pass, and it keeps its trace id, so the case always points back at the run it came from.
Path parameters
| Name | Type | Description |
|---|---|---|
dataset_id |
string |
The dataset to add to. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
· | Which project the runs are in. |
Request body: CaptureRequest
Response: CaptureResult
Authentication: an API key with write scope.
List experiments
/v1/api/experimentsEvery recorded run of a dataset against a version of an agent.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
· | Which project to read. |
Response: Experiment
Authentication: an API key with read scope.
Record an experiment
/v1/api/experimentsRegisters a run of a dataset so its scores can be compared against another version later. Scores recorded with this experiment’s id are what compare reads.
Request body: Experiment
Response: Experiment
Authentication: an API key with write scope.
Operations
Liveness
/healthzReturns 200 as long as the process is running. Wire this to a Kubernetes liveness probe. It never touches storage, so a slow database cannot cause a restart loop.
Response: HealthStatus
Authentication: none; this endpoint is public.
Readiness
/readyzReturns 200 once storage is reachable and migrations have completed. Wire this to a Kubernetes readiness probe so traffic is not sent to an instance that cannot serve it.
Response: HealthStatus
Authentication: none; this endpoint is public.
Prometheus metrics about mcpd itself
/metricsmcpd’s own counters in Prometheus text format: signals accepted, rejected, over quota and unbounded, duplicate batches, traces sampled out, ingest and query requests handled, request errors, audit rows that could not be written, and uptime. An observability tool that cannot be observed is a liability.
Response: String
Authentication: none; this endpoint is public.
What this deployment’s storage backend can do
/v1/api/capabilitiesThe capability set of the store behind this daemon. mcpd has one storage trait and three backends, and they differ: discovery, delta polling, per-signal retention and case folding outside ASCII are not on all three. Read this once and offer only what the deployment answers, rather than discovering a difference from an empty list. An endpoint whose capability is false refuses with 501 and names the same field, so nothing here can disagree with what the daemon actually does.
Response: StoreCapabilities
Authentication: an API key with read scope.
Audit
Read the audit log
/v1/api/auditEvery recorded read of stored payloads and every deletion, newest first. Writing an audit row never fails the request it describes: a row that cannot be written is logged and counted as mcpd_audit_write_failures_total on /metrics, and the read still returns.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
· | Which project to read. Defaults to the account’s own project for an account credential, else the daemon’s default project. |
action |
string |
· | Only rows recording this action, such as delete_trace. |
from |
timestamp |
· | Start of the window. |
to |
timestamp |
· | End of the window. |
limit |
u32 |
100 |
Page size, 1 to 1000. |
Response: AuditEvent
Authentication: an API key with admin scope.
Privacy
Erase everything recorded about one person
/v1/api/subjects/{user_id}Removes every run in the project attributed to this user, with the spans, logs, metrics, scores and feedback attached to those runs, and any log line recorded against the user on its own. On a daemon that serves durable runs, it also removes every ended run whose correlation carries this user_id, with its whole tree of subworkflow runs and everything stored for them; a run that has not ended is counted in runs_in_flight and left whole. This answers a deletion request in one call, instead of finding each run by hand and deleting it trace by trace. Only the project in the request is touched, and only if the key reaches it: the same user id in a project the key cannot open is left alone and is not counted, so a count can never disclose that the subject exists elsewhere. Returns a count per signal, so the reply to the person can say what went. The erasure is recorded in the audit log.
Path parameters
| Name | Type | Description |
|---|---|---|
user_id |
string |
The person to erase, written as the user_id your instrumentation set on the run. Send it as you sent it at ingest; the daemon applies the same hashing it applied then. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
· | Which project to erase within. Defaults to the account’s own project for an account credential, else the daemon’s default project. |
Response: ErasureResult
Authentication: an API key with admin scope.
Runs
Store a definition
/v1/api/definitionsStores one version of a definition. The canonical body is the document with sorted keys and no insignificant whitespace, minus version. An identical body already stored is confirmed at its version rather than stored again, so posting the same code twice is a no-op, and a version is immutable once stored. A body the definition schema refuses is a 400 naming the field.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
· | Which project. A selector: refused with 403 on a mismatch with the key’s grant, filled in for a single-project key, and a 400 when a multi-project key names none. |
Request body: DefinitionDocument
Response: DefinitionVersion
Other responses
| Status | Body | When |
|---|---|---|
409 |
· | A stored version holds a different body under the number you named. A stored version never changes: post the body under a new version, or leave version absent and let the daemon assign one. |
Authentication: an API key with write scope.
List definitions
/v1/api/definitionsEvery stored version of every definition in the project, with its body.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
· | Which project. A selector: refused with 403 on a mismatch with the key’s grant, filled in for a single-project key, and a 400 when a multi-project key names none. |
Response: Definition
Authentication: an API key with read scope.
Get one definition
/v1/api/definitions/{id}One definition. Without version, the highest stored version, which is the one a run started now would pin.
Path parameters
| Name | Type | Description |
|---|---|---|
id |
string |
The definition’s identifier. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
· | Which project. A selector: refused with 403 on a mismatch with the key’s grant, filled in for a single-project key, and a 400 when a multi-project key names none. |
version |
u32 |
· | Which version. Absent means the highest stored. |
Response: Definition
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No definition with that id in the project, or none at the named version. |
Authentication: an API key with read scope.
Start a run
/v1/api/runsStarts a run and answers it as stored. The run row, its trace id, its first cursor and everything that cursor requires commit in one transaction, so a 200 means the first step is already on disk for a worker to claim. With idempotency_key, a repost within 24 hours answers the same run and starts nothing.
Request body: StartRunRequest
Response: Run
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No definition with that id in the project, or none at the named version. |
Authentication: an API key with execute scope.
List runs
/v1/api/runsRuns in the project, newest first.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
· | Which project. A selector: refused with 403 on a mismatch with the key’s grant, filled in for a single-project key, and a 400 when a multi-project key names none. |
status |
RunStatus |
· | Only runs in this status. |
definition_id |
string |
· | Only runs of this definition. |
limit |
u32 |
50 |
Page size, 1 to 500. |
cursor |
string |
· | Opaque cursor from a previous page. |
Response: RunListResponse
Authentication: an API key with read scope.
Get one run with its journal
/v1/api/runs/{run_id}One run with every attempt at every step, and the deadlines and waits it currently holds.
Path parameters
| Name | Type | Description |
|---|---|---|
run_id |
string |
The run to fetch. |
Response: RunDetail
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No run with that id in a project the key reaches. |
Authentication: an API key with read scope.
Cancel a run
/v1/api/runs/{run_id}/cancelEnds the run and every child under it as cancelled: their deadlines and waits are deleted and every live attempt is marked failed. A worker holding a lease learns on its next call, with a 409 run_terminal. A parent of a cancelled child sees the child’s step fail and follows its on_error, unless the parent itself was the run cancelled. Cancelling a run that has already ended changes nothing and still answers 200. The body is empty.
Path parameters
| Name | Type | Description |
|---|---|---|
run_id |
string |
The run to cancel. |
Response: Run
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No run with that id in a project the key reaches. |
Authentication: an API key with execute scope.
Read a run’s event log
/v1/api/runs/{run_id}/eventsThe run’s event log after a sequence number. Send Accept: text/event-stream and the same route answers server-sent events instead: one frame per row with id set to seq, held open until the run ends or you disconnect. after is the reattach point in both forms.
Path parameters
| Name | Type | Description |
|---|---|---|
run_id |
string |
The run to read. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
after |
i64 |
-1 |
Return rows with a sequence number greater than this. Exclusive; the default returns the log from its first row. |
limit |
u32 |
500 |
The most rows to return in one page. |
Response: RunEventPage
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No run with that id in a project the key reaches. |
Authentication: an API key with read scope.
Explain why a step failed
/v1/api/runs/{run_id}/causeWalks backwards from one step of a run along the data dependencies its definition declares, and returns the ancestors whose output that step consumed.
A workflow template such as ${steps.classify.output.kind} is an explicit edge from one step to another, written in the document the run pinned at start, so the chain is read out of the definition rather than guessed from timing or from what happened to run first. The answer names the case a status code cannot: an ancestor that ended succeeded, with every step between it and the failure green as well, whose value the failure consumed anyway.
The step key is a query parameter rather than a path segment because a key may contain a slash, as refund/issue does, and an encoded slash inside a path segment is rewritten or refused by enough proxies to make it a bad contract.
Path parameters
| Name | Type | Description |
|---|---|---|
run_id |
string |
The run to read. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
step |
string |
required | The step key to explain, exactly as the journal keys it, such as refund/issue or poll[7]/classify. |
max_depth |
u32 |
32 |
How far back to walk. The walk stops there and sets truncated, so a long chain, a loop or a definition that reads its own earlier steps repeatedly cannot produce an unbounded answer. |
Response: StepCause
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No run with that id in a project the key reaches, or the run’s definition declares no step with that key. |
Authentication: an API key with read scope.
Deliver an event to one run
/v1/api/runs/{run_id}/signalDelivers an event to one run by id. The daemon reads the run’s open wait on event, takes its key, and routes the event as the publish endpoint would, with the same deduplication.
Path parameters
| Name | Type | Description |
|---|---|---|
run_id |
string |
The run that is waiting. |
Request body: SignalRequest
Response: EventReceipt
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No run with that id, or the run holds no open wait on that event. |
Authentication: an API key with execute scope.
Publish an event
/v1/api/eventsDelivers an event to whichever run is waiting for it, matched on project, event name and key. Delivery writes the payload as the waiting step’s output and moves the run in the same transaction. With no run waiting, the event is parked for 24 hours, and the next run that reaches a matching wait with accept_parked consumes it, oldest first.
Request body: PublishEventRequest
Response: EventReceipt
Authentication: an API key with execute scope.
Workers
Register a worker
/v1/api/workers/registerStores the definitions a worker serves and tells it the lease timings. Nothing about the worker itself is stored: dispatch reads the runnable set, never a worker table, so a worker that dies needs no deregistration.
Request body: WorkerRegistration
Response: WorkerRegistered
Other responses
| Status | Body | When |
|---|---|---|
409 |
· | A stored version holds a different body under the number you named. A stored version never changes: post the body under a new version, or leave version absent and let the daemon assign one. |
Authentication: an API key with worker scope.
Claim runnable steps
/v1/api/steps/claimLong-polls for runnable steps, up to wait_ms. A step is runnable when it has a pending attempt whose retry timer, if any, has fired, its run and its root are running, no other attempt of the same step is live or inside its reclaim window, and neither the run nor its root has spent max_steps or max_cost_usd. A run refused for a spent ceiling is ended failed with an error naming the ceiling.
Request body: ClaimRequest
Response: ClaimResponse
Authentication: an API key with worker scope.
Declare that a step body is about to run
/v1/api/steps/{lease_id}/startedMoves the attempt from leased to started, extends the lease and closes an open reclaim window. Required under at_most_once: post it, wait for the 200, then run the body. Optional under at_least_once, where skipping it only means a crash mid-body is recorded in the journal as a requeue rather than in the trace as an error on that attempt. The body is empty.
Path parameters
| Name | Type | Description |
|---|---|---|
lease_id |
string |
The lease from the claim. |
Response: LeaseState
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No lease with that id. |
409 |
StepConflict |
The lease is no longer the one that may act on this attempt, or the run has ended. reason says which, and what to do next. |
Authentication: an API key with worker scope.
Keep a lease alive
/v1/api/steps/{lease_id}/heartbeatExtends the lease. Post it every heartbeat_ms while the body runs, whether or not started was posted. reclaimed is true when the call found the attempt orphaned and restored it to the state it came from. The body is empty.
Path parameters
| Name | Type | Description |
|---|---|---|
lease_id |
string |
The lease from the claim. |
Response: LeaseState
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No lease with that id. |
409 |
StepConflict |
The lease is no longer the one that may act on this attempt, or the run has ended. reason says which, and what to do next. |
Authentication: an API key with worker scope.
Complete a step
/v1/api/steps/{lease_id}/completeEnds an attempt with an output. In one transaction the attempt becomes succeeded, state is merged shallowly into the run, usage is priced and charged to the attempt, the run and the root, the step’s deadlines are deleted, the closing span is enqueued, and once every slot the cursor requires has ended the cursor advances and everything the next position requires is written.
Path parameters
| Name | Type | Description |
|---|---|---|
lease_id |
string |
The lease from the claim. |
Request body: StepResult
Response: StepAck
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No lease with that id. |
409 |
StepConflict |
The lease is no longer the one that may act on this attempt, or the run has ended. reason says which, and what to do next. |
Authentication: an API key with worker scope.
Fail a step
/v1/api/steps/{lease_id}/failEnds an attempt with an error. class decides what follows: retryable writes a retry timer while attempts remain, at the node’s backoff or retry_after_ms if that is larger; unservable returns the attempt to pending after one lease length without spending the retry budget; every other class applies the node’s on_error at once, as does a retryable failure with no attempts left. fail_run ends the run failed with the message; continue marks the step failed and advances.
Path parameters
| Name | Type | Description |
|---|---|---|
lease_id |
string |
The lease from the claim. |
Request body: StepFailure
Response: StepAck
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No lease with that id. |
409 |
StepConflict |
The lease is no longer the one that may act on this attempt, or the run has ended. reason says which, and what to do next. |
Authentication: an API key with worker scope.
Monitors
List monitors
/v1/api/monitorsEvery configured monitor the key reaches, with the condition it is latched in and its most recent reading. A monitor names the project it watches and carries the webhook it posts to, so the list is filtered by the key’s project grant rather than returned whole. Monitors are configured in the daemon’s configuration file, so there is nothing to create here.
Response: MonitorState
Authentication: an API key with read scope.
Acknowledge a monitor
/v1/api/monitors/{id}/ackAcknowledges a tripped monitor. Acknowledging silences the transition without pretending the condition cleared: the monitor stays acknowledged while it still breaches, and returns to nominal on its own when the number recovers. Acknowledging a monitor that is not tripped changes nothing. The body is empty. A key that does not reach the project the monitor watches is refused with 403, naming the monitor rather than the project that owns it.
Path parameters
| Name | Type | Description |
|---|---|---|
id |
string |
The monitor’s identifier. |
Response: MonitorState
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No monitor with that id. |
Authentication: an API key with write scope.
List objectives
/v1/api/slosEvery configured service level objective the key reaches, filtered by the key’s project grant the way the monitor list is. Objectives are configured in the daemon’s configuration file, so there is nothing to create here.
Response: SloState
Authentication: an API key with read scope.
Acknowledge an objective
/v1/api/slos/{id}/ackAcknowledges a tripped objective, under the same latching rule as a monitor: it stays acknowledged while the burn rate still breaches, and returns to nominal when it recovers. The body is empty. The project grant is checked the way it is on a monitor acknowledgement.
Path parameters
| Name | Type | Description |
|---|---|---|
id |
string |
The objective’s identifier. |
Response: SloState
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No objective with that id. |
Authentication: an API key with write scope.
Intelligence
The aspects a project describes traces along
/v1/api/aspectsEvery aspect this project has, with the bounds a clustering pass runs under.
An aspect is an axis. Four ship with the daemon: goal is what the user wanted, sentiment is the tone they expressed, behaviour is what the agent did about it, and outcome is how the interaction ended. They are ordinary rows rather than a list compiled into a client, so nothing can hold a second opinion about what exists.
outcome ships derived, which means the daemon writes its notes itself from the run’s status, the step error class, the silent producer flag on the cause report and the divergence watch, with no model anywhere. A deployment that has configured nothing therefore fills one aspect on its own, which is enough to see the whole pipeline work before a model is wired to anything.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
Response: AspectCatalogue
Authentication: an API key with read scope.
The entities that have aspect notes
/v1/api/aspects/entitiesWhich agents, workflows and tools have enough recorded to be worth opening, and which are still filling up.
A row appears as soon as the first note about an entity is recorded, before any theme exists. That is deliberate: an empty screen with a count moving on it is a different thing from an empty screen, and a reader has to be able to tell them apart.
Every roll-up here is computed and none of it is optional, because mcpd serves one version of itself and has no older server to tolerate.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
entity_kind |
EntityKind |
· | Only entities of this kind. |
limit |
u32 |
50 |
Page size, 1 to 200. |
cursor |
string |
· | Opaque cursor from a previous page. |
Response: EntityIndexResponse
Other responses
| Status | Body | When |
|---|---|---|
501 |
· | This deployment’s storage backend does not implement what this endpoint needs. The body names the capability in its capability field, using the same word GET /v1/api/capabilities publishes as theme_analysis, so a caller can read the capability set once and stop asking rather than retrying a refusal that will never change. |
Authentication: an API key with read scope.
How far the pipeline has got for one entity
/v1/api/aspects/progressThe honest empty state. For one entity: how many of its traces carry notes, and per aspect how many notes exist, what state that aspect is in, and how many more notes it needs before a clustering pass will run. Every number is counted from stored rows, so nothing here is an estimate of what is coming.
An aspect reads collecting below the floor of 50 notes, processing once it has them and no snapshot covers it yet, and ready when one does. Read this before concluding that a themes screen is broken: the three states look identical on screen and mean different things.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
entity_kind |
EntityKind |
required | The kind of entity to read. An entity is the pair of a kind and a name, which is the identity spans already carry. |
entity_name |
string |
required | The name of that entity. |
Response: AspectProgress
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No entity of that kind and name in a project this key reaches. |
501 |
· | This deployment’s storage backend does not implement what this endpoint needs. The body names the capability in its capability field, using the same word GET /v1/api/capabilities publishes as theme_analysis, so a caller can read the capability set once and stop asking rather than retrying a refusal that will never change. |
Authentication: an API key with read scope.
One entity’s clustering snapshots
/v1/api/themes/snapshotsThe timeline. Each snapshot is one clustering pass over a moving window of notes, and every theme query names exactly one of them.
There are two presentations. inventory returns consecutive snapshots for a caller walking the series. landmarks returns a bounded, time balanced selection across the whole range, with the oldest and the newest snapshot in that range always included and the rest sampled by time, which is what a timeline of a couple of dozen points needs; each row then says which of the two reasons put it there.
Windows overlap. Two snapshots describe overlapping sets of traces, so their counts must never be added together.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
entity_kind |
EntityKind |
required | The kind of entity to read. An entity is the pair of a kind and a name, which is the identity spans already carry. |
entity_name |
string |
required | The name of that entity. |
aspects |
string[] |
· | Only snapshots that cover all of these aspects. Unset means every snapshot for the entity, whatever it covers. |
presentation |
string |
inventory |
inventory for consecutive snapshots, landmarks for a time balanced selection across the range. |
from |
timestamp |
· | Only snapshots whose cutoff falls at or after this. It bounds the range a landmark selection is drawn from as well as what is returned. |
to |
timestamp |
· | Only snapshots whose cutoff falls before this. |
limit |
u32 |
24 |
How many snapshots to return, 1 to 100. A landmark selection of more than a couple of dozen points is more than a timeline can draw and more than a reader can scrub. |
cursor |
string |
· | Opaque cursor from a previous page. |
Response: SnapshotListResponse
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No entity of that kind and name in a project this key reaches. |
501 |
· | This deployment’s storage backend does not implement what this endpoint needs. The body names the capability in its capability field, using the same word GET /v1/api/capabilities publishes as theme_analysis, so a caller can read the capability set once and stop asking rather than retrying a refusal that will never change. |
Authentication: an API key with read scope.
Themes across two to four aspects at once
/v1/api/themes/flowThe cross-aspect view: one stage per aspect, its nodes, and the links between neighbouring stages. A link’s count is the number of traces assigned to both of its ends.
This shows association and not causation. A ribbon between a goal theme and an outcome theme means the two were written about the same runs. It does not mean one caused the other.
The order of aspects is meaningful and is not a display preference. Links only ever join neighbouring stages, so goal beside outcome answers a different question from goal beside behaviour, and asking for the same aspects in another order asks another question. Fewer than two aspects is refused by name rather than answered with an empty flow, because one stage has no links in it.
Each stage carries at most one noise node and at most one overflow node. Noise is the notes that joined no theme, and it is not a failure bucket: it is rare requests, wording nothing else matched and patterns that have not recurred yet, so a large noise share means varied traffic or too few notes. Overflow is everything past theme_limit_per_stage, folded into one node so the stage totals still add up.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
entity_kind |
EntityKind |
required | The kind of entity to read. An entity is the pair of a kind and a name, which is the identity spans already carry. |
entity_name |
string |
required | The name of that entity. |
aspects |
string[] |
required | Two to four aspect names, in the order the stages are wanted. The order changes the answer. |
snapshot_id |
string |
required | Which snapshot to read. Every count in the response belongs to this one window. |
theme_limit_per_stage |
u32 |
8 |
How many themes to return per stage, 1 to 100, before the rest are folded into the overflow node. The fold happens on the server because only the server knows what it truncated. |
filter |
string[] |
· | Drill in. Repeat as filter=aspect:theme_id, or filter=aspect:noise, up to four times with a different aspect each time. The daemon recomputes the counts and the shares over the traces that survive every filter, so the answer does not change shape with the size of the snapshot and a client never has to fetch the assignments and filter them itself. |
Response: ThemeFlow
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No such snapshot for that entity, or aspects names an aspect the project does not have. |
501 |
· | This deployment’s storage backend does not implement what this endpoint needs. The body names the capability in its capability field, using the same word GET /v1/api/capabilities publishes as theme_analysis, so a caller can read the capability set once and stop asking rather than retrying a refusal that will never change. |
Authentication: an API key with read scope.
One theme in one snapshot
/v1/api/themes/{theme_id}A theme’s label, lifecycle state and counts in one snapshot, returned beside the snapshot’s own counts so a caller can write 28 of 70 traces in this window without a second request.
A theme this snapshot does not hold is a 200 with no theme rather than a 404. Scrubbing a timeline back past a theme’s birth is a reasonable question, and the answer is that the theme was not there yet. The 404 is kept for a theme id this entity has never had, which is a different mistake and deserves a different answer.
Path parameters
| Name | Type | Description |
|---|---|---|
theme_id |
string |
The theme to read. noise is reserved and is not a theme: read a stage’s noise from the flow. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
entity_kind |
EntityKind |
required | The kind of entity to read. An entity is the pair of a kind and a name, which is the identity spans already carry. |
entity_name |
string |
required | The name of that entity. |
aspect |
string |
required | Which aspect the theme was clustered along. |
snapshot_id |
string |
required | Which snapshot to read it in. |
Response: ThemeDetail
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No such snapshot, or a theme id this entity has never had. A theme that exists and is absent from this snapshot is a 200 with no theme. |
501 |
· | This deployment’s storage backend does not implement what this endpoint needs. The body names the capability in its capability field, using the same word GET /v1/api/capabilities publishes as theme_analysis, so a caller can read the capability set once and stop asking rather than retrying a refusal that will never change. |
Authentication: an API key with read scope.
The notes behind a theme
/v1/api/themes/{theme_id}/examplesThe sentences that put traces in this theme, each with the run it was written about, so a reader can go from a theme to the runs behind it.
Ordered by how much each note’s wording overlaps the theme’s label, most alike first, with the note id breaking a tie so the order is total and the same on every read and every replica.
Pass noise as the theme id to read the notes that joined no theme in this snapshot. There is no separate noise route: noise is a node in the flow and its notes are read here, because a second pair of routes returning an identically shaped answer would be two more rows in this reference and no new question answered.
The text is payload. This endpoint takes a read key, it is recorded in the audit ledger like every other endpoint that hands back what a user wrote, and the notes it returns are swept by retention and erased with their subject.
Path parameters
| Name | Type | Description |
|---|---|---|
theme_id |
string |
The theme to read, or noise for the notes in this snapshot that joined no theme. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
entity_kind |
EntityKind |
required | The kind of entity to read. An entity is the pair of a kind and a name, which is the identity spans already carry. |
entity_name |
string |
required | The name of that entity. |
aspect |
string |
required | Which aspect the theme was clustered along. |
snapshot_id |
string |
required | Which snapshot to read it in. |
filter |
string[] |
· | Drill in. Repeat as filter=aspect:theme_id, or filter=aspect:noise, up to four times with a different aspect each time. The daemon recomputes the counts and the shares over the traces that survive every filter, so the answer does not change shape with the size of the snapshot and a client never has to fetch the assignments and filter them itself. |
limit |
u32 |
50 |
Page size, 1 to 100. |
cursor |
string |
· | Opaque cursor from a previous page. |
Response: ThemeExampleListResponse
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No such snapshot, or a theme id this entity has never had. |
501 |
· | This deployment’s storage backend does not implement what this endpoint needs. The body names the capability in its capability field, using the same word GET /v1/api/capabilities publishes as theme_analysis, so a caller can read the capability set once and stop asking rather than retrying a refusal that will never change. |
Authentication: an API key with read scope.
One theme’s life across snapshots
/v1/api/themes/{theme_id}/historyEvery stored snapshot that holds this theme, with its state, counts and coverage at each point, and the themes it split from, split into, merged from or merged into.
This is the only theme route that names no snapshot, because the series is the answer. Points come back oldest first, so nothing has to be reordered before it is drawn or differenced.
Retention bounds what can be answered. The oldest point returned is the oldest snapshot still stored, which is a lower bound on the theme’s age and not necessarily its birth, so a series that starts at the edge of the window says when mcpd started looking rather than when the theme started.
Windows overlap, so read the points as a series and never add their counts together.
Path parameters
| Name | Type | Description |
|---|---|---|
theme_id |
string |
The theme to follow. |
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
project |
string |
required | Which project to read. Required. |
entity_kind |
EntityKind |
required | The kind of entity to read. An entity is the pair of a kind and a name, which is the identity spans already carry. |
entity_name |
string |
required | The name of that entity. |
aspect |
string |
required | Which aspect the theme was clustered along. |
limit |
u32 |
100 |
Page size, 1 to 500. |
cursor |
string |
· | Opaque cursor from a previous page. |
Response: ThemeHistory
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | A theme id this entity has never had. |
501 |
· | This deployment’s storage backend does not implement what this endpoint needs. The body names the capability in its capability field, using the same word GET /v1/api/capabilities publishes as theme_analysis, so a caller can read the capability set once and stop asking rather than retrying a refusal that will never change. |
Authentication: an API key with read scope.
What was written about one trace
/v1/api/traces/{trace_id}/aspectsEvery aspect note recorded about one trace, in catalogue order. A trace id crosses service boundaries, so two projects can each hold a run under it: name the project to read that one. Without it, the projects the key reaches are tried in order and the first that holds the trace answers.
Read it beside the trace itself. A note is the sentence that put this run in each of its themes, so it is the shortest route from a theme back to what happened, and it is also the place to check a theme you do not believe: a note proves a definition said this about the trace at a version, and the trace proves the rest.
An empty list is the ordinary answer for a recent trace. Writing a note lags ingestion.
Path parameters
| Name | Type | Description |
|---|---|---|
trace_id |
string |
The trace to read notes for. |
project |
string |
The project whose run of this trace to read. Left out, the first project the key reaches that holds the trace. |
Response: TraceAspectNotes
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | No trace with that id in a project this key reaches. |
501 |
· | This deployment’s storage backend does not implement what this endpoint needs. The body names the capability in its capability field, using the same word GET /v1/api/capabilities publishes as theme_analysis, so a caller can read the capability set once and stop asking rather than retrying a refusal that will never change. |
Authentication: an API key with read scope.
Accounts
Create an account
/v1/api/account/signupCreates an account and a new project that belongs to it, and logs it in. The project has a random name, and no other account or key can reach it. The email is checked for shape only; there is no verification mail. Limited per client address: a client that signs up too often is answered with 429 and a Retry-After header. Answers 404 when the daemon was not started with --signup.
Request body: AccountCredentials
Response: AccountSession
Other responses
| Status | Body | When |
|---|---|---|
400 |
· | The email is not an address, or the password is shorter than 10 characters or longer than 1024 bytes. |
404 |
· | This daemon does not offer sign-up. |
409 |
· | An account with this email already exists. Log in instead. |
429 |
· | Too many sign-ups from this address. Wait for the number of seconds in Retry-After. |
Authentication: none; this endpoint is public.
Log in
/v1/api/account/loginStarts a session for an account. A wrong password and an unknown email get the same 401 with the same message, so the answer does not say which emails have accounts. Limited per client address and per email. Answers 404 when the daemon was not started with --signup.
Request body: AccountCredentials
Response: AccountSession
Other responses
| Status | Body | When |
|---|---|---|
401 |
· | The email and password do not match an account. |
404 |
· | This daemon does not offer accounts. |
429 |
· | Too many attempts from this address or for this email. Wait for the number of seconds in Retry-After. |
Authentication: none; this endpoint is public.
Log out
/v1/api/account/logoutEnds the session whose token the request carries. The account’s API keys keep working. The body is empty. Answers 404 when the daemon was not started with --signup.
Response: DeleteResult
Authentication: an account session token from POST /v1/api/account/login. An API key is not accepted.
Read the logged-in account
/v1/api/accountThe email and the project of the account whose session the request carries. Answers 404 when the daemon was not started with --signup.
Response: Account
Authentication: an account session token from POST /v1/api/account/login. An API key is not accepted.
List the account’s API keys
/v1/api/account/keysEvery API key the account holds, newest first. No secret is returned: the daemon keeps only digests. Answers 404 when the daemon was not started with --signup.
Response: AccountKey
Authentication: an account session token from POST /v1/api/account/login. An API key is not accepted.
Create an API key
/v1/api/account/keysCreates an API key with one scope on the account’s project. The secret is in this response and nowhere else. A key reaches the account’s project only: a signal it writes that names another project is stored in the account’s project instead. An account holds at most 50 keys. Answers 404 when the daemon was not started with --signup.
Request body: CreateAccountKeyRequest
Response: CreatedAccountKey
Other responses
| Status | Body | When |
|---|---|---|
400 |
· | The scope is not one of the six, the name is too long, or the account already holds 50 keys. |
Authentication: an account session token from POST /v1/api/account/login. An API key is not accepted.
Revoke an API key
/v1/api/account/keys/{key_id}Revokes one of the account’s API keys. It stops working at once. Answers 404 when the daemon was not started with --signup.
Path parameters
| Name | Type | Description |
|---|---|---|
key_id |
string |
The key’s id, as listed. |
Response: DeleteResult
Other responses
| Status | Body | When |
|---|---|---|
404 |
· | The account holds no key with that id. |
Authentication: an account session token from POST /v1/api/account/login. An API key is not accepted.