Runs

One id, one lifecycle. Track it, download what it produced, keep it or let it expire.
View as Markdown

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

$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.

StatusMeaning
queuedAccepted, not started yet.
processingReading the documents or running the capability.
processedFinished. output is present.
failedTerminal failure. Per-file causes are on files[].error.
cancelledYou cancelled it.
expiredPast its ttl. The run and its files are gone.
needs_inputWaiting on an open signature envelope. Other capability runs do not reach this status.

A plr_ pipeline also carries steps[]. Each step has its own id, status, and output.

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.

$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"
1{
2 "object": "list",
3 "data": [
4 {
5 "object": "extract_run",
6 "id": "exr_01KYDQEEK5413ASVYK21GJ375J",
7 "status": "processed",
8 "createdAt": "2026-07-25T22:48:31.072Z",
9 "expiresAt": "2026-07-26T22:48:30.721Z",
10 "statusUrl": "/v1/runs/exr_01KYDQEEK5413ASVYK21GJ375J",
11 "metadata": { "job": "nightly-backfill" }
12 }
13 ],
14 "has_more": true,
15 "cursor": "MjAyNi0wNy0yNVQyMjo0ODozMS4wNzJa"
16}

List entries are handles, not results. They carry no output and no config. Fetch GET /v1/runs/:id for the data.

ParameterValues
objectextract_run, parse_run, redact_run, fill_run, sign_run, pipeline_run.
statusprocessing, 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.
limit1–50, default 20. Over 50 is a 400.
cursorThe 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:

$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 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

$# 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:

$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 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.

$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" } }'
1{
2 "object": "run",
3 "id": "exr_01KYD7KC6B…",
4 "spaceId": "0e441359-1c8a-4f26-9d75-2a2d3c9e7f10",
5 "dashboardUrl": "https://app.cloudraker.com/spaces/0e441359-1c8a-4f26-9d75-2a2d3c9e7f10"
6}

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.

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

Next steps