Runs and results
Every fire — cron, manual, or API — produces a run with a receipt you can poll.
Polling always works; run webhooks deliver the same
receipt without a loop on both api.dev.sume.com and api.sume.com.
Run lifecycle
| Status | Meaning |
|---|---|
queued | Accepted, not started. |
processing | The Agent is working. |
completed | Finished. output and artifacts are populated. |
failed | Finished with an error. |
canceled | Stopped by a cancel request. |
skipped | Never ran, because another run was active. |
The Action vocabulary spells it canceled with one l, and it is a remapping
of internal statuses — done surfaces as completed, error as failed, and
cancelled as canceled. Do not assume job-side status strings transfer.
The run receipt
GET /v1/action-runs/{run_id} returns the full receipt.
| Field | Notes |
|---|---|
id, object | object is action.run. |
action | { "id", "title", "trigger_type" }. |
status | See the table above. |
trigger | { "source": "cron" | "manual" | "api", "idempotency_key" }. |
created_at, started_at, finished_at | started_at and finished_at are null until they happen. |
output_schema | { "name", "strict", "source" }, where source is default, action_default, or request_override. |
output | Structured result. null unless status is completed. |
output_error | { "code", "message", "details" } when projection failed. null unless status is completed. See When output cannot be produced. |
primary_output_key | null unless status is completed. |
primary_output_url | Resolved URL for primary_output_key. null unless status is completed. |
artifacts | Media harvested from the run. Empty until the run is terminal. |
usage | { "currency": "USD", "billable_amount_usd_micros", "generation_spend_cap_usd_micros" }, or null when spend could not be read. |
error | { "code": "action_run_failed", "message" }. Non-null only when status is failed. |
skip_reason | previous_run_active on a skipped run, otherwise null. |
request_id | Log this. |
status_url, result_url, cancel_url | Follow these rather than building URLs. |
events_url | Always null. Run lifecycle events are not exposed over the API. |
cancelable | true while queued or processing. |
next_action | Recommended next step — see below. |
idempotency_hit | true when this receipt is an idempotency replay. |
usage.billable_amount_usd_micros is the generation spend attributed to this
run — the same running total the run's own
generation_spend_cap_usd_micros is enforced against, counting both reserved
and captured amounts. It climbs while the run is in flight and settles when the
run terminates.
Two things it is not. It excludes the agent's own LLM turn, which bills the
separate Agent wallet, so it is not the run's total cost. And it is a receipt
figure, not an invoice — GET /v1/usage remains the authoritative billing
record.
usage itself is null when spend could not be read at all. That is
distinct from 0, which means the run really has spent nothing yet.
Poll for completion
GET /v1/action-runs/{run_id}/status returns a trimmed payload built for
polling loops:
Branch on next_action:
next_action | When | Do |
|---|---|---|
poll_status | queued or processing | Keep polling with backoff. |
retry_later | skipped | Another run was active; try again. |
none | Every terminal run — completed, failed, canceled | Nothing left to fetch. On a failure, read error and output_error on this receipt. |
These three are the whole set. events_url is always null — there is no
public run events route — so no receipt asks you to inspect one.
Fetch the result
GET /v1/action-runs/{run_id}/result returns the full receipt, but only once
the run is terminal. While the run is queued or processing it returns
409 run_not_completed with the current status in details.status.
Structured output
Schedule runs use the same structured-output contract as Format runs, and it is
documented once, on Structured output: the
supported schema subset, SumeMediaFile, the built-in
sume/action-run-output/v1 schema, the URL gate, primary_output_key
resolution, and the output_error failure modes all apply here unchanged.
Two things are Scheduled's own:
- A schedule binds its default schema in the dashboard rather than at author
time, and that binding shows up on the receipt as
output_schema.source: "action_default". - A per-request
output_schemaonPOST /v1/actions/{action_id}/runsoverrides it for that run, and shows up asrequest_override.
List runs
limit accepts 1–100 and defaults to 50. The response is a page:
{ "data": [ ... ], "has_more": false, "next_cursor": null }. Walk the whole
history by passing next_cursor back as cursor until has_more is false;
the cursor is opaque, and one we did not mint is rejected with
400 invalid_request.
GET /v1/actions adds has_more and next_cursor, but pagination is not
implemented — has_more is always false and next_cursor is always null.
A single run is also readable under its Action at
GET /v1/actions/{action_id}/runs/{run_id}.
Cancel a run
Cancel requires actions:write and is idempotent. Canceling an
already-terminal run returns that terminal receipt with 200.
End-to-end example
Use exponential backoff in production rather than a fixed five-second sleep.