Skip to main content
The Observability system automatically collects execution traces for analysis and debugging, and provides a track() API for emitting custom business events that power real-time dashboards.

Automatic Tracing (Agent)

Automatic Tracing (Workflow)

Workflows automatically trace every step with full hierarchy:
Each step type (function, agent, connector, condition, switch, foreach, parallel) is traced with its own color and label in the portal.

Verbose Tracing Mode

Control how much data is saved in traces. Works identically for Agent and Workflow. Modes:
  • full: Complete data including prompts and responses (default)
  • standard: Balanced metadata with truncation
  • minimal: Disables tracing entirely (no traces sent)
Simple API (string preset):
Granular Control (object config):

Trace Interceptor (onTrace)

Intercept, modify, or cancel traces before they are sent. Available in Agent, Workflow, and standalone logging.
Return values:
  • Return the trace (modified or not) to send it
  • Return null to cancel (trace is not sent)
  • Return void to send unchanged

Trace Hierarchy (startSpan)

Create parent-child relationships between custom logs for structured traces:
This produces a hierarchical trace in the portal:

Custom Executions (Non-Agent Flows)

For scenarios without agent.process() (document analysis, batch processing, etc.):

Custom Logging

Log custom events within any execution:

Conversation Messages

Available since @runflow-ai/sdk@1.1.10.
Use message() to record a turn of a conversation. Each call emits a conversation_message trace that the Runflow portal renders as a chat bubble: user inbound on the left, assistant outbound on the right. The portal switches automatically to chat view when an execution has at least one conversation_message trace — no flag, no channel hint. The thread sidebar preview also updates to show the latest user/assistant text instead of raw envelope JSON.

When to use

  • Custom workflows (WhatsApp handlers, webhook routers) where you control the message flow without agent.process().
  • LLM agents when you want to also expose the conversation as chat (wrap agent.process() calls).
  • Anywhere you want the execution to render as a conversation in the portal.
If you never call message(), nothing changes — your existing traces and rendering keep working exactly as before.

Wrapping an agent call (LLM)

Custom workflow (no LLM)

The startSpan call is optional but recommended — it groups the technical traces under the turn so the drill-down drawer in the portal stays organized.

Multiple assistant messages per turn

A turn can emit any number of assistant messages — they render as consecutive bubbles in chronological order, exactly like WhatsApp:

Structured content (buttons, audio, image)

content accepts a string OR an object with a type field. The portal renders text natively and falls back to a JSON view for structured content (buttons / media renderers are on the roadmap):

Hierarchy and grouping

Messages follow the same parenting rules as log() and startSpan(): In the portal, clicking any bubble of a turn opens the drill-down with all traces of that execution — the hierarchy you create only affects how the trace tree looks in the drawer.

Parameters

Business Event Tracking

Use track() to emit custom business events from your agent. These events power the Metrics dashboard in the portal, where you can build KPI cards, charts, and real-time feeds without writing any backend code.
Events are buffered and sent in batches automatically (up to 50 events or every 2 seconds). No manual flushing needed during normal execution.

How It Works

  1. Call track(eventName, properties) anywhere in your agent code
  2. The SDK buffers events and sends them in batches to the Runflow API
  3. Open the Metrics tab in the portal to create dashboard cards
  4. Cards auto-discover your event names and properties — no configuration needed

Parameters

Options

Flushing Before Exit

For short-lived scripts or CLI tools, call flushTrackEvents() before exiting to ensure all events are sent:

Dashboard Cards

In the portal, navigate to your agent’s Metrics tab to create cards:
  • Number — KPI with a single aggregated value (count, sum, avg)
  • Rate — Percentage based on a filtered property value
  • Line / Bar — Time-series charts grouped by hour, day, week, or month
  • Pie — Distribution chart over time periods
Cards support drag-to-resize, custom colors, and multiple aggregation types:

Best Practices

Use snake_case names that describe what happened: alert_received, ticket_resolved, payment_processed. Avoid generic names like event or action.
Properties are stored as JSON and queried via keys. Flat key-value pairs work best for dashboard aggregations:
Keep the same property as the same type across events. If duration is a number in one event, don’t send it as a string in another — aggregations like sum and avg rely on numeric values.

Execution Reviews

Available since @runflow-ai/sdk@1.1.13. Requires an API client that exposes the reviews namespace. Ownership/SLA fields (assignedToUserId, dueAt, disposition) and the queue/source filters require 1.5.1+.
Reviews exposes the execution-review feedback loop programmatically — the same surface used by the portal QA queue and the MCP tools (create_execution_review, list_execution_reviews, …). Use it from LLM-judge agents, KB curators, or scheduled jobs to flag bad executions, triage them, and feed corrected outputs back into training datasets.
Each execution can have only one review — call checkHasReview first if you don’t want a 409 surfaced to the caller. Authentication uses your RUNFLOW_API_KEY; reviewedBy is auto-populated from the API key label on the backend.

Methods

Feedback loop pattern

Errors

Reviews throws typed errors so callers can branch cleanly:
  • ReviewAlreadyExistsError (HTTP 409) — the execution already has a review.
  • ReviewNotFoundError (HTTP 404) — the reviewId doesn’t exist or belongs to another tenant.
  • ReviewsError (any other status) — generic error with status + body.

Proactive review policies

Portal feature: Agent → Reviews → Policies. Policies have no public API surface yet — the reviews they create flow through the same Reviews SDK/REST surface above, tagged with source: 'policy'.
Instead of waiting for a human (or your own judge job) to flag a bad execution, review policies watch an agent’s real conversations and open reviews automatically. A policy evaluates a conversation only after it goes quiet — a per-policy silence window, because in messaging channels there is no “end of execution”; time closes the episode. When it fires, it files an actionable review in the agent’s queue (owner, priority-driven SLA deadline) and rings the in-app bell. Two policy types: Conversation grouping is chosen per policy:
  • Whole thread — the entire thread is one conversation (in the SDK, the thread is derived from the customer identity).
  • Sessions by time gap — the customer’s stream is sliced into separate conversations whenever the gap between messages exceeds a configurable window. Recommended for WhatsApp-style channels where a single customer accumulates a long-lived thread.
Judge extras:
  • Test before saving — dry-run the criteria against the agent’s latest real conversations straight from the policy editor (no reviews are created).
  • Behavior analysis (opt-in) — include each turn’s connector/tool calls in the judge context ([actions: patch-lead ✓, run-salesbot ✗]) so it can evaluate what the agent did, not just what it replied.
  • Health at a glance — each policy row shows its last run, reviews created, evaluation errors and judge token spend over the last 7 days.

Reading traces from the SDK (with pagination)

The cross-agent SDK’s Executions.getDetails(executionId) returns the same hierarchical trace tree the portal shows on the “execution detail” page — but bounded to protect the DB. See Cross-Agent SDK → Executions for the full surface. Two modes, depending on the caller:
The portal behavior is fully backwards-compatible — the existing ObservabilityController.getExecutionDetails keeps returning the entire trace array, with the new pagination fields ignored. The 10 000 cap only kicks in if an execution actually generates that many traces (which would be a bug to investigate, not a regression).

Observability Comparison

Next Steps

Workflows

Workflow tracing and step types

Configuration

Configure observability