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.
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.
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:
- Pending — The execution is created and queued
- Running — The pipeline is actively processing (prompt rendering, LLM calls, tool invocations, sub-agent delegations, guardrail checks)
- Terminal or paused state — The execution reaches
completed,failed,cancelled, or parks atwaiting_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
| Status | Description |
|---|---|
pending | Execution created, waiting to be processed |
running | Actively executing (LLM calls, tools, sub-agents, etc.) |
completed | Successfully finished with output |
failed | Encountered an error during execution |
cancelled | Cancelled before completion |
waiting_approval | Parked at an approval-gated tool or sub-agent call, awaiting a human |
cancel_requested | A 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:
| Field | Description |
|---|---|
token_usage | JSON object with prompt and completion token counts |
cost | Total cost in USD (calculated from token usage and model pricing) |
duration_ms | Total 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
| Field | Type | Description |
|---|---|---|
id | KSUID | Unique execution identifier |
agent_id | KSUID | The agent that was executed |
agent_version_id | KSUID | The specific agent version used |
workspace_id | KSUID | Workspace scope |
user_id | KSUID | User who initiated the execution (if authenticated) |
session_id | string | Chat session ID (if applicable) |
parent_execution_id | KSUID | Parent execution (null for a root run) |
status | string | Current execution status |
input | JSON | Input provided to the agent |
output | JSON | Result produced by the agent |
error | string | Error message (if failed) |
token_usage | JSON | Token consumption breakdown |
cost | float | Total cost in USD (subtree roll-up) |
duration_ms | integer | Total duration in milliseconds |
trace_id | string | Link to the execution trace |
approval_expires_at | timestamp | When a waiting_approval decision lapses |
children | array | Child executions (populated by /executions/{id}/tree) |
started_at | timestamp | When execution started |
completed_at | timestamp | When execution finished |
created_at | timestamp | When the execution record was created |
Related Topics
- Agents — Executing agents
- Tracing — Detailed execution traces
- Cost Tracking — Cost analysis from traces
- Approvals — Human-in-the-loop execution flow
- Agent Versioning — Run budgets that bound the tree