> 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 shows the kind: `exr_` extract, `par_` parse, `rdr_` redact, `flr_` fill, `sgr_` sign, `plr_` pipeline. 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. It takes the same `?wait=` (0–120 seconds), so long-poll instead of polling in a tight loop. `?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 an open [signature envelope](/paperwork/capabilities/sign). Other capability runs do not reach this status. |

A `plr_` [pipeline](/paperwork/capabilities/pipeline) also carries `steps[]`. Each step has its own id, status, and output.

[Agent runs](/paperwork/developers/agent-runs) (`agr_…`) are **not** on this surface. They have their own routes and statuses, no `ttl`, and they never appear in `GET /v1/runs`. Nothing on this page — TTL, `keep`, cancel, `output/:name` — applies to them.

## List runs

`GET /v1/runs` returns your organization's runs, newest first. It is the one call that shows work in flight when you have no run id.

```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**. They carry no `output` and 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.** It returns a `400` that lists 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 do not overlap. A cursor is opaque, so do not parse or construct one.

### Filter by your own metadata

Every capability call takes a `metadata` object. 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. Then you never need to store our run ids to find your work again.

`GET /v1/runs` is in the [SDKs](/paperwork/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 are not 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. 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`. 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 gives the time.

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

Two exceptions:

* **Files you registered** with [`POST /v1/files`](/paperwork/developers/files) are persistent. A run's TTL never purges them.
* **Sign runs are TTL-exempt.** An envelope must outlive the 7-day maximum. A `sgr_` run — and a pipeline that contains 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** and re-indexes them for search and the knowledge graph. It 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. Put this link in your own UI when a human must 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 has not 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. This makes ephemeral-by-default safe. Nothing accumulates in your organization's spaces unless you ask for it.

## Signature input

A sign run at `needs_input` is waiting for its signers. Signing links are signer-held secrets and never appear on the sender's API.

The run body carries **`envelopeUrl`**. Track and manage the envelope with [`/v1/runs/:id/envelope`, `/void`, `/audit`, and `/signers/:id/resend`](/paperwork/capabilities/sign#track-the-envelope).

On a pipeline, these sub-routes resolve to the `sign` step. They answer `404` when the pipeline has no such step.

## Next steps

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

Get notified when a run finishes instead of polling for it.

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

The error envelope, the codes, and the failures worth a retry.

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

Persistent documents that outlive any single run.

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

Several capabilities, one file set, one run id.