Executions

Follow one agent run from request to result: status, input, output, the sub-agent execution tree, human approvals, and trace links.

Executions

An execution is one run of an agent. It starts when your app, webhook, schedule, chat session, or Studio action invokes the agent and ends when the run completes, fails, is cancelled, or waits for approval.

Use executions as the run ledger for the workspace. When someone asks “what happened to that agent request?”, the execution record gives you the status, input, output, duration, cost, the sub-agent tree, and the trace link.

Execution history is visible from the traces area. Use the filters to narrow by kind, status, level, source, or tag, then open a run for the full span tree.

What Executions Answer

Executions are useful before you open the deeper trace view:

  • Did it finish? Check whether the run completed, failed, was cancelled, or is waiting for approval.
  • What did it receive and return? Review the request input and final output.
  • What did it delegate? Walk the execution tree to see which sub-agents, handoffs, or workflow nodes ran.
  • How expensive was it? See token usage, estimated cost, and duration, rolled up over the whole tree.
  • Where do I debug it? Open the linked trace to inspect prompts, model calls, tools, guardrails, and errors.

Execution Trees

In API v2 executions form a tree. A root execution has no parent. When a supervisor delegates to a sub-agent, hands off to another agent, or a workflow runs a node, that produces a child execution with parent_execution_id set to the parent. Cost, tokens, and budget roll up from the leaves to the root.

1Root execution
2Sub-agent (child)
3Nested delegate (grandchild)
4Result rolls up

Fetch a run with its full children tree populated:

tree = client.executions.tree("execution-id")
 
def walk(node, depth=0):
    print("  " * depth, node.agent_id, node.status, f"${node.cost:.6f}")
    for child in node.children:
        walk(child, depth + 1)
 
walk(tree)

The run budget on the agent version bounds the whole tree (max cost, tokens, tool calls, children, and delegation depth) and is enforced at the root.

Execution Lifecycle

At a high level, an execution moves through these states:

  1. Pending — The execution is created and queued
  2. Running — The pipeline is actively processing (prompt rendering, LLM calls, tool invocations, sub-agent delegations, guardrail checks)
  3. Terminal or paused state — The execution reaches completed, failed, cancelled, or parks at waiting_approval
1Pending
2Running
3Completed, Failed, Cancelled, or Waiting Approval

Use the states to decide the next action: open the trace for failures, approve or deny paused runs, cancel a runaway tree, and review cost when a workflow becomes expensive.

Technical detailsExecution data model

Status Types

StatusDescription
pendingExecution created, waiting to be processed
runningActively executing (LLM calls, tools, sub-agents, etc.)
completedSuccessfully finished with output
failedEncountered an error during execution
cancelledCancelled before completion
waiting_approvalParked at an approval-gated tool or sub-agent call, awaiting a human
cancel_requestedA cooperative cancel was observed and is being applied

Input and Output

Input

The input is the JSON object passed by the caller. If the active agent version has an input schema, PromptRails validates the request before the run starts.

{
  "message": "What is the status of order #12345?",
  "customer_id": "cust_789",
  "language": "en"
}

Output

The output is the result produced by the agent. Its shape depends on any output schema configured on the active version.

{
  "response": "Your order #12345 is currently in transit and expected to arrive by Friday.",
  "confidence": 0.95,
  "sources": ["order_database"]
}

Token Usage and Cost

Every execution tracks token consumption and estimated cost, aggregated over its whole subtree:

FieldDescription
token_usageJSON object with prompt and completion token counts
costTotal cost in USD (calculated from token usage and model pricing)
duration_msTotal execution time in milliseconds

Detailed cost and token roll-ups are reported through traces — see Cost Tracking and /traces/summary.

Technical detailsHuman-in-the-loop approvals

Approvals

When a tool or sub-agent is attached with requires_approval, the execution pauses at waiting_approval instead of proceeding. The paused run appears in the execution-scoped approval inbox, and approval_expires_at records when the pending decision lapses. Who may decide is set by the version’s approval_policy (admins, assigned, or any_member).

Approving resumes the run in place; denying resumes it with a denial the agent must handle. Nothing is discarded — the same execution continues.

# List runs parked at waiting_approval
for execution in client.executions.approval_inbox().data:
    print(execution.id, execution.agent_id, execution.approval_expires_at)
 
# Approve (resumes the run) or deny (resumes with a denial)
client.executions.approve("execution-id", reason="Looks good")
client.executions.deny("execution-id", reason="Do not send external email")

JavaScript SDK

const inbox = await client.executions.approvalInbox()
for (const exec of inbox.data) {
  await client.executions.approve(exec.id, { reason: 'looks good' })
  // or: await client.executions.deny(exec.id, { reason: 'denied' })
}

See Approvals for the full human-in-the-loop guide.

Cancelling a Run

Request cooperative cancellation of a running execution. The run moves to cancel_requested and finalizes as cancelled at the next safe checkpoint:

client.executions.cancel("execution-id")
Technical detailsExecution API examples and fields

Running Synchronously vs Asynchronously

By default agents.execute returns as soon as the run is dispatched, giving you an execution_id you can stream or poll. Pass sync=True to block until the run reaches a terminal or paused state and return the result inline.

# Wait for the result inline
result = client.agents.execute(
    agent_id="your-agent-id",
    input={"message": "Hello"},
    sync=True,
)
print(result.output)
# Dispatch, then poll for completion
execution = client.agents.execute(agent_id="your-agent-id", input={"message": "..."})
 
import time
while True:
    status = client.executions.get(execution.execution_id)
    if status.status in ("completed", "failed", "cancelled", "waiting_approval"):
        break
    time.sleep(1)
 
if status.status == "waiting_approval":
    print("Execution is waiting for approval. Open the approval inbox to continue it.")
else:
    print(status.output)

Streaming Execution Events

Instead of polling, subscribe to the execution’s Server-Sent Events stream to receive thinking, tool_start, tool_end, content, and done frames as they happen.

GET /api/v1/executions/{execution_id}/stream
Accept: text/event-stream

The event schema is identical to the chat streaming endpoint — see Sessions and Chat for the full table. Useful when the execution was started outside a chat session (e.g. a one-shot agents.execute or a webhook trigger) and you still want live updates.

// JavaScript / TypeScript
for await (const event of client.executions.stream(executionId)) {
  if (event.type === 'content') process.stdout.write(event.content)
  if (event.type === 'done') break
}
# Python — sync iterator (AsyncExecutionsResource.stream for async)
from promptrails import ContentEvent, DoneEvent
 
for event in client.executions.stream(execution_id):
    if isinstance(event, ContentEvent):
        print(event.content, end="", flush=True)
    elif isinstance(event, DoneEvent):
        break
// Go
stream, _ := client.Executions.Stream(ctx, executionID)
defer stream.Close()
for stream.Next() {
    if e, ok := stream.Event().(*promptrails.ContentEvent); ok {
        fmt.Print(e.Content)
    }
}

Already-completed executions emit a single done (or error) frame and close, so the same code works whether you subscribe mid-run or after the fact.

Listing and Filtering Executions

# List all executions
executions = client.executions.list(page=1, limit=20)
 
# Filter by agent, session, or status
executions = client.executions.list(agent_id="your-agent-id", status="completed")

JavaScript SDK

const executions = await client.executions.list({
  agentId: 'your-agent-id',
  status: 'completed',
  page: 1,
  limit: 20,
})

Execution Response Fields

FieldTypeDescription
idKSUIDUnique execution identifier
agent_idKSUIDThe agent that was executed
agent_version_idKSUIDThe specific agent version used
workspace_idKSUIDWorkspace scope
user_idKSUIDUser who initiated the execution (if authenticated)
session_idstringChat session ID (if applicable)
parent_execution_idKSUIDParent execution (null for a root run)
statusstringCurrent execution status
inputJSONInput provided to the agent
outputJSONResult produced by the agent
errorstringError message (if failed)
token_usageJSONToken consumption breakdown
costfloatTotal cost in USD (subtree roll-up)
duration_msintegerTotal duration in milliseconds
trace_idstringLink to the execution trace
approval_expires_attimestampWhen a waiting_approval decision lapses
childrenarrayChild executions (populated by /executions/{id}/tree)
started_attimestampWhen execution started
completed_attimestampWhen execution finished
created_attimestampWhen the execution record was created