Enterprise
Runs and results
Poll a schedule run, read its structured output and artifacts, and cancel it.
Each fire (cron, manual, or API) makes a run with a receipt that you can poll.
Polls always work. On api.dev.sume.com and api.sume.com,
run webhooks deliver the same receipt without a loop.
Run lifecycle
queued -> processing -> completed | failed | canceled | skipped| Status | Meaning |
|---|---|
queued | Accepted, not started. |
processing | The Agent works on the run. |
completed | Finished. output and artifacts are populated. |
failed | Finished with an error. |
canceled | A cancel request stopped it. |
skipped | Never ran, because another run was active. |
The Action vocabulary spells it canceled with one l. The Action statuses map
the internal statuses to new names: done shows as completed, error as
failed, and cancelled as canceled. Do not assume that 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 occur. |
output_schema | { "name", "strict", "source" }, where source is default, action_default, or request_override. |
output | Structured result. null unless status is completed or failed. |
output_error | { "code", "message", "details" } when projection failed. null unless status is completed or failed. Refer to 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 | { "debited_credits", "held_credits", "refunded_credits", "final", "cap": { "limit_credits", "counted_credits", "remaining_credits" } }, or null when Sume could not read the credits. |
error | { "code", "message" }. code is output_error.code when one is set. If not, it is action_run_failed. 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 | Use these URLs. Do not build URLs yourself. |
events_url | Always null. The API does not expose run lifecycle events. |
cancelable | true while queued or processing. |
next_action | Recommended next step. Refer to the section below. |
idempotency_hit | true when this receipt is an idempotency replay. |
usage.cap.counted_credits is the generation credits of this run. It is
the same total that Sume uses to enforce the cap of the run,
usage.cap.limit_credits. This total includes both reserved and captured
credits. It increases while the run is in flight, and it settles when the run
terminates.
Know two limits of this value. First, it does not include the LLM turn of the
agent. Thus, the value is not the total credits of the run:
usage.debited_credits is. Second, it is a receipt figure, not an invoice.
GET /v1/usage is still the authoritative billing record.
usage is null when Sume could not read the credits at all. That is different
from 0, which means that the run truly used no credits yet.
Poll for completion
GET /v1/action-runs/{run_id}/status returns a trimmed payload for poll
loops:
curl -sS "https://api.sume.com/v1/action-runs/$RUN_ID/status" \
-H "Authorization: Bearer $SUME_API_KEY"{
"data": {
"id": "run_...",
"status": "processing",
"started_at": "2026-07-31T09:00:01.000Z",
"finished_at": null,
"next_action": "poll_status",
"cancelable": true
}
}Branch on next_action:
next_action | When | Do |
|---|---|---|
poll_status | queued or processing | Continue to poll with backoff. |
retry_later | skipped | Another run was active. Try again. |
none | Every terminal run — completed, failed, canceled | There is nothing more to fetch. On a failure, read error and output_error on this receipt. |
These three values are the full set. events_url is always null, because
there is no public run events route. Thus, no receipt tells you to inspect one.
Fetch the result
GET /v1/action-runs/{run_id}/result returns the full receipt, but only after
the run is terminal. While the run is queued or processing, it returns
409 run_not_completed, with the current status in details.status.
curl -sS "https://api.sume.com/v1/action-runs/$RUN_ID/result" \
-H "Authorization: Bearer $SUME_API_KEY"Structured output
Schedule runs use the same structured-output contract as Format runs.
Structured output documents this contract one
time. These items all apply here with no change: 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.
Two items are specific to Scheduled:
- A schedule binds its default schema in the dashboard, not at author time. The
receipt shows this default as
output_schema.source: "action_default". - A per-request
output_schemaonPOST /v1/actions/{action_id}/runsoverrides it for that run. The receipt shows it asrequest_override.
List runs
curl -sS "https://api.sume.com/v1/actions/$ACTION_ID/runs?limit=20" \
-H "Authorization: Bearer $SUME_API_KEY"limit accepts 1–100, and the default is 50. The response is a page:
{ "data": [ ... ], "has_more": false, "next_cursor": null }. To read the full
history, send next_cursor back as cursor until has_more is false. The
cursor is opaque. If Sume did not create the cursor, the API rejects it with
400 invalid_request.
GET /v1/actions pages the same way: send next_cursor back as cursor until
has_more is false.
You can also read a single run under its Action at
GET /v1/actions/{action_id}/runs/{run_id}.
Cancel a run
curl -sS -X POST "https://api.sume.com/v1/action-runs/$RUN_ID/cancel" \
-H "Authorization: Bearer $SUME_API_KEY"Cancel requires actions:write and is idempotent. If you cancel a run that is
already terminal, the API returns that terminal receipt with 200.
End-to-end example
export SUME_API_KEY="sume_live_..."
export ACTION_ID="aut_..."
RUN=$(curl -sS -X POST "https://api.sume.com/v1/actions/$ACTION_ID/runs" \
-H "Authorization: Bearer $SUME_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"input":{"product_name":"Aurora Headphones"}}')
RUN_ID=$(echo "$RUN" | jq -r '.data.id')
STATUS=$(echo "$RUN" | jq -r '.data.status')
while [ "$STATUS" = "queued" ] || [ "$STATUS" = "processing" ]; do
sleep 5
STATUS=$(curl -sS "https://api.sume.com/v1/action-runs/$RUN_ID/status" \
-H "Authorization: Bearer $SUME_API_KEY" | jq -r '.data.status')
done
if [ "$STATUS" = "completed" ]; then
curl -sS "https://api.sume.com/v1/action-runs/$RUN_ID/result" \
-H "Authorization: Bearer $SUME_API_KEY" \
| jq -r '.data.primary_output_url'
else
echo "run ended as $STATUS"
fiIn production, use exponential backoff, not a fixed five-second sleep.