> For the complete documentation index, see [llms.txt](https://docs.mithrl.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.mithrl.com/docs/rest-api.md).

# Mithrl-1 API (REST)

Submit and monitor workflow runs, manage uploads, and browse the workflow catalog.

For the complete upload → run → monitor → download lifecycle, see [Running and managing Inference](/guides/workflow-cli.md).

## Runs

### `GET /api/v1/workflows/runs`

List workflow runs

Returns a page of run summaries for the calling organization, most recently submitted first. Filter with `workflow`, `status`, `label` and `submitted_after` (an ISO-8601 timestamp; a value with no offset is read as UTC). A page carries at most 200 items, 50 by default, and an opaque `next_cursor` to pass back for the following page.

| Parameter         | In    | Type      | Required | Description                                                                                                                                           |
| ----------------- | ----- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cursor`          | query | `string`  | no       | Opaque cursor identifying the next page, taken from the previous response. When supplied it takes precedence over `offset`.                           |
| `label`           | query | `string`  | no       | Return only runs carrying this label.                                                                                                                 |
| `limit`           | query | `integer` | no       | Maximum number of items to return. Between 1 and 200. Defaults to `50`.                                                                               |
| `offset`          | query | `integer` | no       | Number of items to skip. Between 0 and 1000000. Ignored when `cursor` is supplied. Defaults to `0`.                                                   |
| `status`          | query | `string`  | no       | Return only runs in this state: `submitted`, `validating`, `queued`, `running`, `succeeded`, `failed` or `cancelled`.                                 |
| `submitted_after` | query | `string`  | no       | Return only runs submitted at or after this ISO-8601 timestamp. A timestamp with no timezone offset is read as UTC. An unparseable value is rejected. |
| `workflow`        | query | `string`  | no       | Return only runs of this workflow.                                                                                                                    |

### `POST /api/v1/workflows/runs`

Submit a workflow run

Starts a run of a saved catalog workflow, named by `workflow_id`, or of an inline graph supplied as `graph`. Run-level `params` and `inputs` are validated before anything is scheduled, and an optional `label` is stored with the run. Supplying an `idempotency_key` makes the call safe to repeat: a second request carrying the same key returns the run the first one created rather than starting another. The response is 202 while the run is still processing and 200 when the run has already reached a terminal state or was replayed.

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

Retrieve one run in full

Returns the run's status, the state of each node in its graph, any validation warnings, the workflow version and kind it resolved to, whether a cancellation has been requested, and whether the run can be forked. The payload grows with the size of the graph, so use the run status operation for repeated polling.

| Parameter | In   | Type     | Required |
| --------- | ---- | -------- | -------- |
| `run_id`  | path | `string` | yes      |

### `GET /api/v1/workflows/runs/{run_id}/artifacts`

List the files a run consumed and produced

Returns a page of artifact records for one run — the inputs it read and the outputs and reports it wrote — each with its role, name, media type, size in bytes, SHA-256 digest, the graph node that produced it, and whether it is downloadable. A page carries at most 200 items, 50 by default, plus an opaque `next_cursor`. Metadata only; download links are minted by the single-artifact operation.

| Parameter | In    | Type      | Required | Description                                                                                                                 |
| --------- | ----- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| `cursor`  | query | `string`  | no       | Opaque cursor identifying the next page, taken from the previous response. When supplied it takes precedence over `offset`. |
| `limit`   | query | `integer` | no       | Maximum number of items to return. Between 1 and 200. Defaults to `50`.                                                     |
| `offset`  | query | `integer` | no       | Number of items to skip. Between 0 and 1000000. Ignored when `cursor` is supplied. Defaults to `0`.                         |
| `run_id`  | path  | `string`  | yes      |                                                                                                                             |

### `POST /api/v1/workflows/runs/{run_id}/cancel`

Request cancellation of a run

Records a cancellation request against an active run. Cancellation is cooperative: the request is recorded immediately and the run stops when its executor next observes it, so the status in the response may still be an active one. The response therefore also reports the outcome as its own field — `requested` when the request was recorded, and `noop_terminal` when the run had already finished and nothing was cancelled. Repeating the call is safe.

| Parameter | In   | Type     | Required |
| --------- | ---- | -------- | -------- |
| `run_id`  | path | `string` | yes      |

### `POST /api/v1/workflows/runs/{run_id}/rerun`

Rerun a finished run, optionally with changed parameters

Creates a new run forked from a run that has finished, pinned to the exact graph the original executed so a later edit to the catalog entry cannot change what is repeated. Completed steps are seeded from the original's checkpoints, so unchanged work is reused rather than recomputed. Send `modifications` to change run parameters, or omit them to repeat the original as it was; an `idempotency_key` is accepted as on submission. A run that has not finished, or whose graph can no longer be recovered, is refused with 409.

| Parameter | In   | Type     | Required |
| --------- | ---- | -------- | -------- |
| `run_id`  | path | `string` | yes      |

### `GET /api/v1/workflows/runs/{run_id}/results`

Retrieve a finished run's results

Returns the run's result record together with its warnings and its output and report artifacts, each carrying a freshly minted download link that expires after a fixed interval. A run that has not finished is refused with 409 rather than reported as an empty success; poll the run until it reports results as full or partial. A failed or cancelled run has no result record but still exposes whatever partial outputs it wrote.

| Parameter | In   | Type     | Required |
| --------- | ---- | -------- | -------- |
| `run_id`  | path | `string` | yes      |

### `POST /api/v1/workflows/runs/{run_id}/retry`

Retry a failed run

Creates a new run forked from a failed run, reusing its parameters unchanged and resuming from the checkpoints its completed steps left behind rather than starting over. Available only for a run that both failed and is marked retryable — the run record reports which failures qualify; any other state is refused with 409. An `idempotency_key` is accepted as on submission.

| Parameter | In   | Type     | Required |
| --------- | ---- | -------- | -------- |
| `run_id`  | path | `string` | yes      |

### `GET /api/v1/workflows/runs/{run_id}/status`

Check whether a run is still active

Returns three fields and nothing else: the run identifier, its status string, and a boolean saying whether the run is still active. The response is a fixed size that does not grow with the run's graph, which makes it the operation to poll on while waiting for a run to finish.

| Parameter | In   | Type     | Required |
| --------- | ---- | -------- | -------- |
| `run_id`  | path | `string` | yes      |

## Uploads

### `GET /api/v1/workflows/uploads`

List uploads

Returns a page of the calling organization's uploads with their file name, status, declared size, creation time and expiry, optionally filtered by `status`. A page carries at most 200 items, 50 by default, plus an opaque `next_cursor`. This is the only read-only operation on uploads.

| Parameter | In    | Type      | Required | Description                                                                                                                 |
| --------- | ----- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| `cursor`  | query | `string`  | no       | Opaque cursor identifying the next page, taken from the previous response. When supplied it takes precedence over `offset`. |
| `limit`   | query | `integer` | no       | Maximum number of items to return. Between 1 and 200. Defaults to `50`.                                                     |
| `offset`  | query | `integer` | no       | Number of items to skip. Between 0 and 1000000. Ignored when `cursor` is supplied. Defaults to `0`.                         |
| `status`  | query | `string`  | no       | Return only uploads in this state: `initiated`, `completed`, `aborted` or `expired`.                                        |

### `POST /api/v1/workflows/uploads`

Start a multipart file upload

Registers a file you intend to use as a run input and returns a presigned URL for each part to be written to, the part size in bytes, and the expiry of the upload. Send `filename` and `declared_size_bytes` (which must be greater than zero and is checked against the assembled object later); `media_type` and an explicit `part_size` are optional. Part size and part count are checked against object-storage limits — a part is 5 MiB to 5 GiB and there may be at most 10,000 of them — before any storage is reserved. An upload left incomplete expires after 7 days.

### `GET /api/v1/workflows/uploads/{upload_id}`

Resume an upload and re-mint presigned part URLs

Reports which parts of an interrupted upload are already stored, with their etags, and returns a fresh presigned write URL for every part still missing, so a client can carry on from where it stopped. Parts never sent are reported missing as well, derived from the declared size. Because it mints URLs that write data, this operation is not read-only despite being a GET: it needs the same permission as starting an upload, and a caller holding read-only access is refused with 403. An upload that has already completed or been aborted returns that status instead, and a completed one also reports its file reference, file name, stored size and SHA-256 digest so a resuming client can tell whether it holds the same file.

| Parameter   | In   | Type     | Required |
| ----------- | ---- | -------- | -------- |
| `upload_id` | path | `string` | yes      |

### `POST /api/v1/workflows/uploads/{upload_id}/abort`

Abort an upload and discard its parts

Cancels an upload that is still in progress and discards the parts already written. Repeating the call on an upload that is already aborted or expired returns its status without error. An upload that has completed owns a durable file that aborting would strand, so it is refused with 409.

| Parameter   | In   | Type     | Required |
| ----------- | ---- | -------- | -------- |
| `upload_id` | path | `string` | yes      |

### `POST /api/v1/workflows/uploads/{upload_id}/complete`

Complete an upload and mint a file reference

Assembles the uploaded parts into one stored object and returns the `file_ref` that run inputs use to name it, with the stored size and the digest supplied. Send every part as a `part_number` and `etag` pair, including parts written in an earlier session, and optionally the file's `sha256`. Repeating the call on an upload that already completed returns the same `file_ref` rather than minting a second one. An upload that was aborted or has expired is refused with 409.

| Parameter   | In   | Type     | Required |
| ----------- | ---- | -------- | -------- |
| `upload_id` | path | `string` | yes      |

## Catalog

### `GET /api/v1/workflows/catalog`

List workflows in the catalog

Returns a page of catalog entries visible to the calling organization: the curated presets and the custom workflows the organization has saved. Filter with `kind` and `status`. A page carries at most 200 items, 50 by default, plus an opaque `next_cursor`. Deprecated entries are excluded.

| Parameter | In    | Type      | Required | Description                                                                                                                 |
| --------- | ----- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| `cursor`  | query | `string`  | no       | Opaque cursor identifying the next page, taken from the previous response. When supplied it takes precedence over `offset`. |
| `kind`    | query | `string`  | no       | Return only entries of this kind: `preset` or `custom`.                                                                     |
| `limit`   | query | `integer` | no       | Maximum number of items to return. Between 1 and 200. Defaults to `50`.                                                     |
| `offset`  | query | `integer` | no       | Number of items to skip. Between 0 and 1000000. Ignored when `cursor` is supplied. Defaults to `0`.                         |
| `status`  | query | `string`  | no       | Return only entries with this lifecycle status. Deprecated and retired entries are never listed, whatever this is set to.   |

### `POST /api/v1/workflows/catalog`

Save a custom workflow

Validates a workflow graph and, if nothing blocking is found, stores it as version 1 of a new custom catalog entry owned by the calling organization. Send the `graph` together with a `name`, a `short_description` and the `scientific_question` it answers. The response carries the new identifier, version and status, plus any non-blocking warnings from validation. A graph with blocking errors is refused with 422 and every error is reported at once; no entry is created.

### `GET /api/v1/workflows/catalog/tox-endpoints`

List the curated toxicity endpoints

Returns every toxicity endpoint a toxicity run may name in its scope, each with its identifier, label, category, description, the number of distinct ontology terms it covers and a per-source breakdown of where those terms came from. The response also carries the ontology release pins that identify which materialization of the set this is, so a count can be cited alongside the release it was measured on. The set is small and fixed per release, so this operation is not paged, and it does not enumerate the terms themselves.

### `GET /api/v1/workflows/catalog/{workflow_id}`

Retrieve one catalog entry

Returns the entry's descriptive metadata together with its latest graph and the run interface derived from that graph — the inputs a run must supply and the parameters it may set. Accepts either a curated preset slug or a custom workflow identifier. A deprecated entry is not returned.

| Parameter     | In   | Type     | Required |
| ------------- | ---- | -------- | -------- |
| `workflow_id` | path | `string` | yes      |

### `PUT /api/v1/workflows/catalog/{workflow_id}`

Save a new version of a custom workflow

Validates the supplied graph and stores it as the next version of an existing custom entry, and updates the entry's name, short description and scientific question at the same time. Earlier versions are retained, so a run that cites one keeps resolving to the graph it actually executed. The response reports the version that was created. Curated presets cannot be edited, and a graph with blocking errors is refused with 422.

| Parameter     | In   | Type     | Required |
| ------------- | ---- | -------- | -------- |
| `workflow_id` | path | `string` | yes      |

### `DELETE /api/v1/workflows/catalog/{workflow_id}`

Deprecate a custom workflow

Marks a custom entry and its stored graph versions deprecated. Nothing is erased: runs that already cite the entry keep their lineage and stay readable. The entry stops appearing in catalog listings, is no longer returned individually, and can no longer be submitted. Curated presets cannot be deprecated.

| Parameter     | In   | Type     | Required |
| ------------- | ---- | -------- | -------- |
| `workflow_id` | path | `string` | yes      |

## Parts

### `GET /api/v1/workflows/parts`

List the analysis parts a workflow can be built from

Returns a page of the reusable analysis steps a workflow graph composes, each with its identifier, name, description, latest version and status, optionally filtered by `status`. A page carries at most 500 items, 100 by default, plus an opaque `next_cursor`.

| Parameter | In    | Type      | Required | Description                                                                                                                 |
| --------- | ----- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| `cursor`  | query | `string`  | no       | Opaque cursor identifying the next page, taken from the previous response. When supplied it takes precedence over `offset`. |
| `limit`   | query | `integer` | no       | Maximum number of items to return. Between 1 and 500. Defaults to `100`.                                                    |
| `offset`  | query | `integer` | no       | Number of items to skip. Between 0 and 1000000. Ignored when `cursor` is supplied. Defaults to `0`.                         |
| `status`  | query | `string`  | no       | Return only parts with this lifecycle status.                                                                               |

### `GET /api/v1/workflows/parts/{part_id}`

Retrieve one part's interface

Returns a single version of a part: its input and output ports, its parameter specifications, the steps it performs, its citations and its stated limitations — what a node using this part must be wired to and what it can be asked to do. Defaults to the part's latest version; pass `version` to pin an earlier one.

| Parameter | In    | Type     | Required | Description                                                                                                  |
| --------- | ----- | -------- | -------- | ------------------------------------------------------------------------------------------------------------ |
| `part_id` | path  | `string` | yes      |                                                                                                              |
| `version` | query | `string` | no       | Return this version of the part. Defaults to its latest version; a version that does not exist is not found. |

## Validation

### `POST /api/v1/workflows/validate`

Validate a workflow without running it

Resolves a preset identifier, a saved custom identifier or an inline `graph` and applies the same checks a submission applies: graph structure, run parameters merged with part defaults, and the existence, ownership and expiry of every input file reference. No run is created and no work is scheduled. Every blocking error is reported at once with 422; otherwise the response is 200 with any non-blocking warnings. By default the check is partial, tolerating inputs not yet supplied; pass `pending_inputs` naming the files you are about to upload to rehearse a complete submission instead.

## Artifacts

### `GET /api/v1/workflows/artifacts`

List files available as run inputs

Returns a page of the calling organization's stored files — uploaded inputs and reusable outputs of earlier runs — so an existing `file_ref` can be found instead of uploading the same file again. Filter with `role`, `media_type`, `filename`, `created_after` and `source`, which takes `upload` or `run:` followed by a run identifier. Only files that can feed a run are listed by default: set `reusable=false` to widen that, and `include_expired=true` to see the metadata of files whose retention has run out. A page carries at most 200 items, 50 by default, plus an opaque `next_cursor`.

| Parameter         | In    | Type      | Required | Description                                                                                                                                                                           |
| ----------------- | ----- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `created_after`   | query | `string`  | no       | Return only artifacts created at or after this ISO-8601 timestamp. A timestamp with no timezone offset is read as UTC. An unparseable value is rejected.                              |
| `cursor`          | query | `string`  | no       | Opaque cursor identifying the next page, taken from the previous response. When supplied it takes precedence over `offset`.                                                           |
| `filename`        | query | `string`  | no       | Return only artifacts with exactly this file name.                                                                                                                                    |
| `include_expired` | query | `boolean` | no       | Include artifacts whose retention window has passed. Defaults to `False`.                                                                                                             |
| `limit`           | query | `integer` | no       | Maximum number of items to return. Between 1 and 200. Defaults to `50`.                                                                                                               |
| `media_type`      | query | `string`  | no       | Return only artifacts with exactly this media type.                                                                                                                                   |
| `offset`          | query | `integer` | no       | Number of items to skip. Between 0 and 1000000. Ignored when `cursor` is supplied. Defaults to `0`.                                                                                   |
| `reusable`        | query | `boolean` | no       | Return only artifacts whose reusable-as-input flag has this value. This filter is always applied; pass `false` to list artifacts that cannot be reused as inputs. Defaults to `True`. |
| `role`            | query | `string`  | no       | Return only artifacts in this role: `input`, `intermediate`, `output`, `report` or `log`.                                                                                             |
| `source`          | query | `string`  | no       | Return only artifacts from one source: `upload` for artifacts uploaded directly, or `run:<run_id>` for the artifacts of one run. Any other value is rejected.                         |

### `GET /api/v1/workflows/artifacts/{artifact_id}`

Retrieve one file's metadata and a download link

Returns a stored file's metadata and, when the file is durable, a freshly minted download link that expires after a fixed interval. A transient intermediate carries metadata for lineage but no link. A file whose retention has run out, or that has been deleted, is reported with 410 rather than 404 — it existed, and that is a different thing for a caller to act on than an identifier that was never valid. Metadata for an expired file remains readable from the listing operation.

| Parameter     | In   | Type     | Required |
| ------------- | ---- | -------- | -------- |
| `artifact_id` | path | `string` | yes      |

## Feedback

### `POST /api/v1/feedback`

Send feedback about the platform

Records one free-text feedback message of up to 4000 characters, with an optional `rating` from 0 to 3. The submission must be attributable to a person, so the credential has to identify an individual user: a bearer token, or an API key owned by a user, is accepted, while a service token, an API key owned by an organization, and a legacy `ltk_live_` key each name no person and are refused with 403 even though they authenticate successfully everywhere else. On success the response is 204 with no body — a submission is not addressable and cannot be read back.

**Request body**

Required. Accepted media types: `application/json`, `application/x-www-form-urlencoded`, `multipart/form-data`.

`{ message, rating? }` — commentary required, rating optional.

| Field     | Type      | Required | Description                       |
| --------- | --------- | -------- | --------------------------------- |
| `message` | `string`  | yes      | At most 4000 characters.          |
| `rating`  | `integer` | no       | Between `0` and `3`, may be null. |
