> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.cloudraker.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.cloudraker.com/_mcp/server.

# Runs

Every capability call creates a **run**. The id tells you what it is — `exr_` extract, `par_` parse, `rdr_` redact, `flr_` fill, `sgr_` sign, `plr_` pipeline — and one set of routes works across all of them.

```
GET    /v1/runs                   list your recent runs
GET    /v1/runs/:id               status + result
POST   /v1/runs/:id/cancel        stop in-flight work
DELETE /v1/runs/:id               purge now
POST   /v1/runs/:id/keep          persist into the product
GET    /v1/runs/:id/output/:name  download a produced file
```

## Track a run

```bash
curl "https://api.cloudraker.com/v1/runs/exr_01KYD1J8QW2RN4T6VXZ0ABCDEF?wait=30" \
  -H "Authorization: Bearer $CLOUDRAKER_API_KEY"
```

`GET /v1/runs/:id` returns the same body the original call returns, and takes the same `?wait=` (0–120 seconds) — long-poll instead of hammering it. `?include=evidence` is accepted and does nothing: citations are always included when the run was grounded.

| Status        | Meaning                                                                                                                                                       |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `queued`      | Accepted, not started yet.                                                                                                                                    |
| `processing`  | Reading the documents or running the capability.                                                                                                              |
| `processed`   | Finished. `output` is present.                                                                                                                                |
| `failed`      | Terminal failure. Per-file causes are on `files[].error`.                                                                                                     |
| `cancelled`   | You cancelled it.                                                                                                                                             |
| `expired`     | Past its `ttl` — the run and its files are gone.                                                                                                              |
| `needs_input` | Waiting on a human — a [fill review](/capabilities/fill#human-review) or an open [signature envelope](/capabilities/sign). Never reached by extract or parse. |

A `plr_` [pipeline](/capabilities/pipeline) additionally carries `steps[]`, each with its own id, status, and output.

## List runs

`GET /v1/runs` returns your organization's runs, newest first — the one call that answers "what is still in flight?" without a run id in hand.

```bash
curl -G "https://api.cloudraker.com/v1/runs" \
  -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
  --data-urlencode "object=extract_run" \
  --data-urlencode "status=processed" \
  --data-urlencode "limit=20"
```

```json
{
  "object": "list",
  "data": [
    {
      "object": "extract_run",
      "id": "exr_01KYDQEEK5413ASVYK21GJ375J",
      "status": "processed",
      "createdAt": "2026-07-25T22:48:31.072Z",
      "expiresAt": "2026-07-26T22:48:30.721Z",
      "statusUrl": "/v1/runs/exr_01KYDQEEK5413ASVYK21GJ375J",
      "metadata": { "job": "nightly-backfill" }
    }
  ],
  "has_more": true,
  "cursor": "MjAyNi0wNy0yNVQyMjo0ODozMS4wNzJa"
}
```

List entries are **handles, not results** — no `output`, no `config`. Fetch `GET /v1/runs/:id` for the data.

| Parameter        | Values                                                                                                                                                                                                                                                     |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `object`         | `extract_run`, `parse_run`, `redact_run`, `fill_run`, `sign_run`, `pipeline_run`.                                                                                                                                                                          |
| `status`         | `processing`, `processed`, `failed`, `cancelled`, `expired`, `needs_input`. **`queued` is not accepted here** and returns a `400` listing the six values above, even though a run body can report `queued`. Filter on `processing` to find work in flight. |
| `limit`          | 1–50, default 20. Over 50 is a `400`.                                                                                                                                                                                                                      |
| `cursor`         | The `cursor` from the previous page. Omit it for page one.                                                                                                                                                                                                 |
| `metadata.<key>` | Up to **three** `metadata.<key>=<value>` pairs, combined with AND. A fourth is a `400`.                                                                                                                                                                    |

Paging is cursor-based: pass the response's `cursor` back as `?cursor=` until `has_more` is `false`. Pages don't overlap, and a cursor is opaque — don't parse or construct one.

### Filter by your own metadata

Every capability call takes a `metadata` object, and it is the intended join key between your system and ours:

```bash
curl -G "https://api.cloudraker.com/v1/runs" \
  -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
  --data-urlencode "metadata.job=nightly-backfill" \
  --data-urlencode "metadata.tenant=acme"
```

Tag every run at creation with the ids you already have — a job id, a customer id, an invoice number — and you never need to store our run ids to find your work again.

`GET /v1/runs` is in the [SDKs](/developers/sdks) as of 0.3.0 — `client.runs.listRuns({ object, status, limit, cursor })` in TypeScript, `client.runs.list_runs(object=…, status=…, limit=…, cursor=…)` in Python. The `metadata.<key>` filters aren't typed parameters; send those over HTTP as shown above.

**The list is eventually consistent.** A run created moments ago can be missing from `GET /v1/runs`, or show a status one step behind. `GET /v1/runs/:id` is always authoritative — never decide "finished" or "failed" from the list, and never treat an absent run as a lost one.

## Cancel and purge

```bash
# stop work that's still going; files and the TTL are untouched
curl -X POST https://api.cloudraker.com/v1/runs/exr_…/cancel \
  -H "Authorization: Bearer $CLOUDRAKER_API_KEY"

# delete the run, its files, and its outputs right now
curl -X DELETE https://api.cloudraker.com/v1/runs/exr_… \
  -H "Authorization: Bearer $CLOUDRAKER_API_KEY"
```

Cancelling an already-terminal run is a no-op that returns the current body. `DELETE` is idempotent — purging an already-purged run still returns `204`.

## Download a produced file

Runs that make a document — a redacted PDF, a filled form, a sealed contract — expose it two ways. The signed URL is inline on the run body at `output.file.url`, and this route is the stable, guessable alias:

```bash
curl -L https://api.cloudraker.com/v1/runs/rdr_…/output/"w9 (redacted).pdf" \
  -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
  -o redacted.pdf
```

`:name` is the file name as reported in `output.files[].name`, or the file id. The response is a `302` to a signed, time-limited URL — follow redirects.

A produced file is registered a moment after the run reports `processed`. In that window `output.file` can arrive without its `url` and this route can answer `404 not_found` — re-fetch `GET /v1/runs/:id` and the URL will be there.

## Expiry and TTL

Runs clean themselves up. `ttl` on the create call (seconds, default **24 hours**, max **7 days**) sets when the run, its inline-created input files, and its outputs are purged; `expiresAt` on the run body tells you when.

After expiry, `GET /v1/runs/:id` answers `410 run_expired` for a grace window and then `404 not_found`. Persist anything you need before then — or `keep` it.

Two exceptions:

* **Files you registered** with [`POST /v1/files`](/developers/files) are persistent and are never purged by a run's TTL.
* **Sign runs are TTL-exempt.** An envelope must outlive the 7-day maximum, so a `sgr_` run — and a pipeline containing a `sign` step — is not purged while the envelope is open. `expiresAt` is still present on the body; for a sign run it is not enforced.

## Keep a run

`keep` is the bridge from the headless API into the CloudRaker product. It moves the run's files and outputs into a real **space**, re-indexes them for search and the knowledge graph, imports extraction results as records when the action was bound to a data object, and clears the run's TTL.

```bash
curl -X POST https://api.cloudraker.com/v1/runs/exr_01KYD7KC6B…/keep \
  -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "space": { "name": "Q3 invoices" } }'
```

```json
{
  "object": "run",
  "id": "exr_01KYD7KC6B…",
  "spaceId": "0e441359-1c8a-4f26-9d75-2a2d3c9e7f10",
  "dashboardUrl": "https://app.cloudraker.com/spaces/0e441359-1c8a-4f26-9d75-2a2d3c9e7f10"
}
```

Send **either** `{"space": {"name": "…"}}` to create a space or `{"spaceId": "…"}` to use an existing one — sending both is a `400`.

`dashboardUrl` opens the space in the app: this is the link you put in your own UI when a human needs to look at what the API produced. Afterwards `GET /v1/runs/:id` reports **`expiresAt: null`** — the run is no longer on a clock.

Keeping the same run into the same space again is a no-op; keeping it into a *different* space returns `409 already_kept`. Keeping a run that hasn't finished returns `409 pipeline_running` — wait for a terminal status first.

Everything a `/v1` run touches is invisible in the app until you keep it. That is what makes ephemeral-by-default safe: nothing accumulates in your organization's spaces unless you ask for it.

## Human tasks

A run at `needs_input` is waiting on a person:

* **Fill** — the run body carries `tasks: [{id, title, url}]`. Each `url` is a hosted review page you can open or hand to a reviewer; the same task is also readable and submittable over [`/v1/runs/:id/task`](/capabilities/fill#human-review) if you would rather build your own UI.
* **Sign** — no `tasks[]`: signing links are signer-held secrets. The run body carries **`envelopeUrl`** instead, and the envelope is tracked and managed with [`/v1/runs/:id/envelope`, `/void`, `/audit`, and `/signers/:id/resend`](/capabilities/sign#track-the-envelope).

On a pipeline these sub-routes resolve to the matching step — `/task*` to the `fill` step, the rest to the `sign` step — and `404` when the pipeline has no such step.

## Next steps

#### [Webhooks](/developers/webhooks)

Be told when a run finishes instead of polling for it.

#### [Errors](/developers/errors)

The error envelope, the codes, and what's worth retrying.

#### [Files](/developers/files)

Persistent documents that outlive any single run.

#### [Pipelines](/capabilities/pipeline)

Several capabilities, one file set, one run id.