Agent runs

Start an agent on a set of files. Then answer its sign-offs and human steps over the API.

View as Markdown

An agent is a multi-step automation with built-in human sign-off. An agent run (agr_…) is one execution of an agent. These six routes are the whole surface:

GET /v1/agents agents you can run
GET /v1/agents/:id one agent: steps, actions, gates
POST /v1/agent-runs start a run
GET /v1/agent-runs/:id where it is, accepts ?wait=
POST /v1/agent-runs/:id/approvals/:approvalId approve or reject a step
POST /v1/agent-runs/:id/tasks/:taskId/complete mark a human step done

Pick an agent

$curl https://api.cloudraker.com/v1/agents \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY"
1{
2 "object": "list",
3 "data": [
4 {
5 "object": "agent",
6 "id": "01JQ8ZKMRT4V6WXYZ0ABCDEFGH",
7 "name": "Client intake",
8 "version": 7,
9 "tasks": [
10 { "id": "task-1", "title": "Extract the intake fields", "executor": "agent", "dependsOn": [] },
11 { "id": "task-2", "title": "Countersign the engagement letter", "executor": "human", "dependsOn": ["task-1"] }
12 ],
13 "actions": [{ "name": "Sign document", "approval": "before" }]
14 // …description, updatedAt
15 }
16 ]
17}

Read tasks[] and actions[] before you start. They tell you which steps come back as human work, and which actions stop for a sign-off. GET /v1/agents/:id returns the same shape for one agent.

Start a run

$curl -X POST "https://api.cloudraker.com/v1/agent-runs?wait=60" \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "agent": "01JQ8ZKMRT4V6WXYZ0ABCDEFGH",
> "files": [{ "url": "https://example.com/intake.pdf", "name": "intake.pdf" }],
> "metadata": { "caseId": "42" },
> "webhook": { "url": "https://example.com/hooks/cloudraker" }
> }'
FieldTypeWhat it does
agentstringRequired. The agent id from GET /v1/agents.
files[]array of {url, name?, processing?} or {id}, 1–200Required. {url} registers and fetches the file. {id} reuses a file from POST /v1/files; the API does not parse it twice.
metadataobjectYour own key/values, echoed back on every read of the run. Max 10 KB. Carry your caseId here to avoid a lookup table.
webhook{url} or {id}Where to deliver this run’s events. See Webhooks.

There is no ttl. Files you pass by URL join your reusable corpus, and no deadline purges them. A run can wait days on a person.

What comes back

The call holds open for up to ?wait= seconds (default 60, max 120, 0 returns immediately). It releases early when the run finishes or blocks on a person.

OutcomeResponse
Finished inside the window200 with the full run
Files still being prepared202 with status: "queued"
Still working at the cap202 with {object, id, status, statusUrl}
Blocked on a person202 right away, status: "waiting"
1{
2 "object": "agent_run",
3 "id": "agr_01KYD1J8QW2RN4T6VXZ0ABCDEF",
4 "status": "waiting",
5 "statusUrl": "/v1/agent-runs/agr_01KYD1J8QW2RN4T6VXZ0ABCDEF"
6}

The 202 is not an error. You get the run id either way. Send an idempotency-key header to make retries safe. A replay returns the original run with an idempotent-replay: true response header.

Poll a queued run. File preparation can take minutes for scans or long audio. A read of the run starts it once the files are ready. A run that has not started reports nothing. A webhook alone leaves it queued forever. Once the run starts, events arrive as they happen.

POST
/v1/agent-runs
1curl -X POST https://api.cloudraker.com/v1/agent-runs \
2 -H "Authorization: Bearer <token>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "agent": "string",
6 "files": [
7 {
8 "url": "string"
9 }
10 ]
11}'

Poll the run

$curl "https://api.cloudraker.com/v1/agent-runs/agr_01KYD1J8QW2RN4T6VXZ0ABCDEF?wait=60" \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY"

?wait= (0–120, default 0 here) holds the request until the run finishes or blocks on a person. A loop of long polls then costs one request per minute, not one per second.

1{
2 "object": "agent_run",
3 "id": "agr_01KYD1J8QW2RN4T6VXZ0ABCDEF",
4 "agent": { "id": "01JQ8ZKMRT4V6WXYZ0ABCDEFGH", "name": "Client intake", "version": 7 },
5 "status": "waiting",
6 "waiting": { "approvals": 1, "tasks": 0, "summary": "Waiting on a sign-off for Sign document." },
7 "progress": { "tasks": { "total": 2, "completed": 1 } },
8 "tasks": [
9 {
10 "id": "task-1",
11 "title": "Extract the intake fields",
12 "executor": "agent",
13 "status": "completed",
14 "summary": "Read 14 fields from intake.pdf.",
15 "completedAt": "2026-07-29T09:41:02.117Z"
16 },
17 {
18 "id": "task-2",
19 "title": "Countersign the engagement letter",
20 "executor": "human",
21 "status": "pending",
22 "summary": null,
23 "completedAt": null
24 }
25 ],
26 "approvals": [
27 {
28 "id": "7c1e9f42-3b0d-4a5e-8f61-2d9c4b7ae013",
29 "kind": "before",
30 "action": "Sign document",
31 "requestedAt": "2026-07-29T09:41:05.902Z",
32 "rationale": "Ready to send the engagement letter for signature.",
33 "files": [{ "id": "a04d6597-4e34-4a99-94ea-964c289a4c68", "name": "engagement-letter.pdf" }],
34 "params": { "signers": [{ "name": "Jane Doe", "email": "[email protected]" }] }
35 }
36 ],
37 "statusUrl": "/v1/agent-runs/agr_01KYD1J8QW2RN4T6VXZ0ABCDEF",
38 "metadata": { "caseId": "42" },
39 "createdAt": "2026-07-29T09:40:58.004Z",
40 "expiresAt": "2026-08-05T09:40:58.004Z"
41}
FieldWhat it is
statusOne of queued, processing, waiting, paused, completed, failed, cancelled, expired. See the status table.
waitingPresent while status is waiting. Counts outstanding approvals and human steps, with a one-line summary.
pausedPresent while status is paused, with reason: "model_error" or "system_error". Not a failure.
tasks[]The step ledger: executor, status, and the summary the agent recorded when the step closed.
approvals[]Sign-offs that block the run, oldest first, with the proposed params and files.
progress.tasks{total, completed}.
resultThe agent’s closing summary, on a finished run.
output.files[]Files attached during step completion. Each url is signed and valid for about one hour.
error{code, message}, present whenever status is failed.
incompletetrue on a completed run that still had outstanding work.
expiresAtThe run’s deadline, about seven days out. Past it, an unfinished run parks permanently.
GET
/v1/agent-runs/:id
1curl https://api.cloudraker.com/v1/agent-runs/id \
2 -H "Authorization: Bearer <token>"

Decide an approval

Read the outstanding sign-offs from approvals[]. Then answer one by id:

$curl -X POST "https://api.cloudraker.com/v1/agent-runs/agr_01KYD1J8QW…/approvals/7c1e9f42-3b0d-4a5e-8f61-2d9c4b7ae013?wait=60" \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{ "decision": "approve" }'
1{
2 "object": "agent_run_approval",
3 "id": "7c1e9f42-3b0d-4a5e-8f61-2d9c4b7ae013",
4 "kind": "before",
5 "action": "Sign document",
6 "status": "approved",
7 "requestedAt": "2026-07-29T09:41:05.902Z",
8 "decidedAt": "2026-07-29T09:42:11.508Z",
9 "note": null,
10 "run": {
11 "id": "agr_01KYD1J8QW2RN4T6VXZ0ABCDEF",
12 "status": "waiting",
13 "statusUrl": "/v1/agent-runs/agr_01KYD1J8QW2RN4T6VXZ0ABCDEF"
14 }
15}

run.status tells you whether the run moved on, finished, or blocked on the next item. ?wait= holds the request while the run resumes work.

FieldWhat it does
decisionRequired. approve or reject.
note≤ 4000 characters. Required on reject. A rejection without one is a 400.
paramsReplaces the inputs the agent proposed, for a before gate.
filesReplaces the files for the step. Up to 200 file ids, from POST /v1/files or the run’s own inputs.

params and files together must stay under 1 MiB. A second decision on the same approval is a 409. The first answer stands.

$# reject, with the reason
$-d '{ "decision": "reject", "note": "Wrong signer — resend to the CFO." }'
$
$# approve, but fix the inputs first
$-d '{ "decision": "approve", "params": { "signers": [{ "name": "Ada Byron", "email": "[email protected]" }] } }'

An API key has no person behind it. The run records your organization’s key as the actor, not a named individual.

POST
/v1/agent-runs/:id/approvals/:approvalId
1curl -X POST https://api.cloudraker.com/v1/agent-runs/id/approvals/approvalId \
2 -H "Authorization: Bearer <token>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "decision": "approve"
6}'

Complete a human step

Your steps are the entries in tasks[] with executor: "human" and status: "ready". When you close one, all dependent work releases:

$curl -X POST "https://api.cloudraker.com/v1/agent-runs/agr_01KYD1J8QW…/tasks/task-2/complete?wait=60" \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{ "note": "Client signed; scan attached.", "files": ["a04d6597-4e34-4a99-94ea-964c289a4c68"] }'
1{
2 "object": "agent_run_task",
3 "id": "task-2",
4 "title": "Countersign the engagement letter",
5 "executor": "human",
6 "status": "completed",
7 "summary": "Client signed; scan attached.",
8 "completedAt": "2026-07-29T09:44:37.221Z",
9 "run": {
10 "id": "agr_01KYD1J8QW2RN4T6VXZ0ABCDEF",
11 "status": "completed",
12 "statusUrl": "/v1/agent-runs/agr_01KYD1J8QW2RN4T6VXZ0ABCDEF"
13 }
14}

Both body fields are optional. Send {} if you have neither. note is ≤ 2000 characters. files is up to 20 ids that you registered or that the run holds. They appear in the run’s output.files.

SituationResponse
The step’s executor is agent422. Only human steps are completable.
Its dependsOn steps aren’t finished409.
It is already done409.
POST
/v1/agent-runs/:id/tasks/:taskId/complete
1curl -X POST https://api.cloudraker.com/v1/agent-runs/id/tasks/taskId/complete \
2 -H "Authorization: Bearer <token>" \
3 -H "Content-Type: application/json" \
4 -d '{}'

End to end

The whole loop, with a webhook that does the waiting:

1

Start the run

POST /v1/agent-runs with {agent, files: [{url}], metadata, webhook: {url}}202, status: "queued". Keep the agr_ id.

2

Poll once to pick it up

GET /v1/agent-runs/:id?wait=60. This read starts a queued run once its files are ready. Events flow from this point.

3

A sign-off is requested

agent_run.approval_requested arrives. The event is minimal by design. Re-read the run and take the params and files from approvals[].

4

Answer it

POST /v1/agent-runs/:id/approvals/:approvalId with {"decision": "approve"}. The response’s run.status shows what the run did next.

5

A human step opens

agent_run.task_ready arrives with the step’s id and title. This is the one item a person must do.

6

Close it

POST /v1/agent-runs/:id/tasks/:taskId/complete with a note and any files. agent_run.completed follows, carrying result, progress, and output.

What this surface does not have

  • No list. There is no GET /v1/agent-runs. Hold the agr_ id from create, or tag runs with metadata you know.
  • No cancel, no delete. An agent run runs until it finishes, parks, or reaches expiresAt.
  • Not in GET /v1/runs. That list holds capability runs only (extract_run, parse_run, …). The TTL and keep behavior on Runs does not apply here. An agent run’s inputs are persistent files, and the run has no ttl.

Next steps