Executions
Understanding the lifecycle of a webhook execution and how to inspect it
An execution is a single attempt to process a single event. It records everything that happened—whether it succeeded, failed, was retried, what steps ran, and what logs were written.
Execution shape
type Execution = {
id: string; // Unique identifier for this execution attempt
eventId: string; // The event's unique webhook ID (deduped by this)
eventType: string; // The event type (e.g., "stripe.charge.succeeded")
eventData: Record<string, unknown>; // The event payload
status: 'pending' | 'completed' | 'failed';
attempt: number; // Attempt number (1 on first try, 2 after first retry, etc.)
replayedFrom?: string; // If this is a replay, the source execution ID
createdAt: string; // ISO 8601 timestamp
completedAt?: string; // Set when status changes from pending
error?: string; // If failed, the error message
};Lifecycle
-
Ingest: Webhook arrives, provider signature verified, event normalized, and an
Executionis created withstatus: 'pending'andattempt: 1. -
Run: All registered handlers for the event type run sequentially. Each handler can call
ctx.step.run()to checkpoint work. -
Complete or fail: Execution completes successfully or fails with an error.
completedAtis set andstatuschanges accordingly. -
Auto-retry (if failed and retries remain): A timer schedules the next retry after a delay computed by the retry policy. The retry increments
attemptand re-runs everything except completed steps.
Querying executions
Use the Relay API:
// List all executions (most recent first)
const executions = await restaq.listExecutions();
// Get a specific execution
const exec = await restaq.getExecution(executionId);
// List all steps for an execution
const steps = await restaq.listSteps(executionId);
// List all logs for an execution
const logs = await restaq.listLogs(executionId);Or use the CLI:
# List recent executions
relay events list
# Inspect a specific execution with all its steps and logs
relay inspect <executionId>
# Same output as JSON
relay inspect <executionId> --json
# Show every step attempt (useful for debugging retries)
relay inspect <executionId> --historyReplayed executions
When you call replayExecution(executionId), a brand-new execution is created with a fresh eventId (so it won't be deduped against the original). The new execution records the source execution's ID in its replayedFrom field so you can trace the lineage.
The original execution is never modified. This is useful for testing handler changes against real historical data without affecting past audit trails.
Log sources
Every log line is tagged with where it came from, so you can tell a runtime decision from your own handler's output:
type LogSource = 'system' | 'handler';
// 'system' — written by the runtime itself:
// "event ingested", "retrying (attempt 2, scheduled)", "restarting (attempt 2) - all steps will re-run"
// 'handler' — written by your code via ctx.log.info(), ctx.log.error(), etc.listLogs(executionId) and relay inspect <executionId> both return this tag, so you can filter for just your own logs or just the runtime's.