Skip to main content
The Runflow runtime API is callable two ways:
  1. From SDK code — use createRunflowAPIClient() (and the higher-level Reviews / Memory / Knowledge modules built on top of it).
  2. From any HTTP client — talk to the runtime endpoints directly, e.g. from a service that doesn’t run the SDK.
This page covers both — the SDK exports first, then the raw REST surface.

SDK API Client

import { createRunflowAPIClient } from '@runflow-ai/sdk';

const api = createRunflowAPIClient(); // reads RUNFLOW_API_URL, RUNFLOW_API_KEY, RUNFLOW_AGENT_ID
The factory follows a config priority: explicit options → rf.json → env vars → defaults. The resulting client exposes typed methods for chat, chatStream, vectorSearch, connector, memory.*, documents.*, prompts.*, and reviews.* (since SDK ≥ 1.1.13). Higher-level modules — Reviews, Memory, Knowledge, RAG, LLM — wrap this client with stricter argument validation and typed errors. Prefer them in agent code; reach for the raw client only when you need something the modules don’t cover.

Authentication

All runtime endpoints accept an API key in the x-api-key header (or as a Bearer token in Authorization):
GET /api/v1/runtime/v1/observability/reviews
x-api-key: rf_live_...
Get an API key from the portal under Settings → API Keys. Each key is tenant-scoped; the platform derives tenantId from the key and applies it to every query. The reviewedBy / updatedBy audit fields on execution reviews are auto-populated from the API key label — name your keys descriptively (e.g. qa-bot, nightly-export) so the audit trail is readable.

Base URL

https://api.runflow.ai
The runtime surface lives under the /api/v1/runtime/v1/observability/... prefix. Paths below are shown relative to the base URL.

Execution Reviews

CRUD + stats + training-data export for the execution-review feedback loop. The SDK’s Reviews class wraps these endpoints — use it from agent code unless you need raw HTTP.

Create a review

POST /api/v1/runtime/v1/observability/executions/:executionId/reviews
{
  "agentId": "agent-uuid",
  "rating": "bad",
  "comment": "Bot gave wrong business hours (≥ 10 chars required)",
  "priority": "high",
  "tags": ["hours_wrong"]
}
Each execution can only have one review. A second POST returns 409 Conflict. Use the exists endpoint below for an idempotent check.

Check if an execution already has a review

GET /api/v1/runtime/v1/observability/executions/:executionId/reviews/exists
Response: { "exists": true, "review": { "id", "rating", "status", "comment", "createdAt" } }.

List reviews

GET /api/v1/runtime/v1/observability/reviews
Query params (all optional):
ParamTypeDescription
agentIdstringFilter by agent.
statusenumpending_review, in_progress, resolved, wont_fix.
ratingenumgood, bad, needs_improvement.
priorityenumlow, medium, high, critical.
dateFrom / dateToISO 8601Date range.
searchstringPortuguese full-text search across comments.
limitnumberDefault 50, capped at 100.
offsetnumberPagination offset.

Get a single review

GET /api/v1/runtime/v1/observability/reviews/:reviewId

Update a review

PATCH /api/v1/runtime/v1/observability/reviews/:reviewId
Partial update — send only the fields you want to change. Allowed fields: status, actionTaken, resolutionNotes, correctedOutput, comment, priority, tags. Setting status: "resolved" auto-stamps resolvedBy and resolvedAt.

Delete a review

DELETE /api/v1/runtime/v1/observability/reviews/:reviewId
Hard delete.

Stats

GET /api/v1/runtime/v1/observability/reviews/stats?agentId=...
agentId is required. Response: { total, pending, inProgress, resolved, badReviews, needsImprovement, critical, highPriority, avgResolutionHours }.

Export for training

GET /api/v1/runtime/v1/observability/reviews/export/training-data?agentId=...&status=resolved
Returns an array of OpenAI conversational fine-tuning examples — one per matching review, with the correctedOutput substituted into the assistant turn when present.
{
  "training_examples": [
    {
      "messages": [
        { "role": "user", "content": "..." },
        { "role": "assistant", "content": "..." }
      ],
      "metadata": {
        "review_id": "...",
        "execution_id": "...",
        "rating": "bad",
        "was_corrected": true
      }
    }
  ],
  "total": 1,
  "format": "openai-conversational",
  "generated_at": "2026-05-20T12:00:00.000Z"
}
Typical filter: { agentId, status: 'resolved' } to pull only human-reviewed and corrected examples.

Dashboards & Events

The pieces of the metrics pipeline that are exposed to API-key callers (SDK, CLI, server-to-server):

Ingest events

POST /api/v1/runtime/v1/observability/events
Used by the SDK’s track() and CLI tools to push raw events. The events are written async (fire-and-forget) and become queryable within a few seconds.
{
  "events": [
    {
      "eventName": "alert_received",
      "properties": { "severity": "high", "company": "NW" },
      "timestamp": "2026-05-20T12:00:00.000Z"
    },
    {
      "eventName": "ticket_resolved",
      "properties": { "duration": 45, "category": "network" }
    }
  ]
}
tenantId and agentId are derived from the API key — you don’t need to pass them.

Recent events feed

GET /api/v1/runtime/v1/observability/events/feed
Most-recent-first list of events for an agent. Useful for debugging emissions, building external “live event” widgets, or sanity-checking that track() calls landed. Query params:
ParamTypeDescription
agentIdstringOptional. Defaults to the agent bound to the API key.
eventNamestringOptional filter.
limitnumberDefault 50, capped at 100.
offsetnumberPagination offset.
Response:
{
  "events": [
    {
      "id": "ev-uuid",
      "eventName": "alert_received",
      "properties": { "severity": "high", "company": "NW" },
      "threadId": "thread-uuid",
      "executionId": "exec-uuid",
      "timestamp": "2026-05-20T12:00:00.000Z"
    }
  ],
  "total": 142
}

Query events (raw or grouped)

POST /api/v1/runtime/v1/observability/events/query
Table-style query against the event store. Two modes:
  • mode: 'raw' (default) — returns individual events with the columns you ask for. Use for backfill jobs, exports, or anything that needs the underlying rows.
  • mode: 'aggregate' — groups rows by a single property key and applies one or more metrics (count, sum, avg, min, max). Use for table-shaped reports.
The KPI-card style standalone aggregate (one number per query — sum/avg/count/rate of a metric) remains portal-only for now; this endpoint is for the table-shaped reads. Body:
FieldTypeDescription
agentIdstringOptional. Defaults to the API-key agent.
eventNamestringRequired.
mode'raw' | 'aggregate'Default 'raw'.
columnsstring[](raw mode) Property keys to project. Omit for all.
groupBystring(aggregate mode) Property key to group on.
metricsArray<{ aggregation, propertyKey?, alias }>(aggregate mode) One per output column. aggregation is one of count, sum, avg, min, max.
filtersArray<{ propertyKey, operator, value }>Operators: eq, neq, gt, lt, gte, lte, contains, in.
dateFrom / dateToISO 8601Date range.
sortBy / sortOrderstring / asc | descResult ordering.
limit / offsetnumberPagination.
Response:
{
  "rows": [
    { "severity": "high", "count": 42, "total_duration": 1860 }
  ],
  "total": 1,
  "columns": [
    { "key": "severity", "type": "string" },
    { "key": "count", "type": "number" },
    { "key": "total_duration", "type": "number" }
  ]
}
Example — raw mode for a backfill job:
{
  "eventName": "sale",
  "mode": "raw",
  "columns": ["amount", "category", "customer_id"],
  "filters": [{ "propertyKey": "category", "operator": "eq", "value": "electronics" }],
  "dateFrom": "2026-05-01T00:00:00Z",
  "limit": 500
}
Example — aggregate mode for a “sales by category” table:
{
  "eventName": "sale",
  "mode": "aggregate",
  "groupBy": "category",
  "metrics": [
    { "aggregation": "count", "alias": "orders" },
    { "aggregation": "sum", "propertyKey": "amount", "alias": "revenue" }
  ],
  "dateFrom": "2026-05-01T00:00:00Z"
}

List dashboard cards

GET /api/v1/runtime/observability/dashboard-cards?agentId=...
Returns every card configured for the agent, with its cardType, config, and gridLayout. Useful for read-only views of what’s published, or for snapshotting a dashboard into version control.
{
  "success": true,
  "total": 5,
  "cards": [
    {
      "id": "card-uuid",
      "agentId": "agent-uuid",
      "title": "Funil de checkout",
      "cardType": "funnel",
      "config": { /* card-type-specific shape */ },
      "sortOrder": 0,
      "tabId": "tab-uuid"
    }
  ]
}

Upsert a dashboard card

POST /api/v1/runtime/observability/dashboard-cards
Idempotent on (agentId, eventName, cardType, aggregation, propertyKey). This is the endpoint metrics.sync() (SDK) and rf metrics sync (CLI) call under the hood — you can call it directly from server-to-server jobs.
{
  "agentId": "agent-uuid",
  "title": "Total alerts",
  "cardType": "number",
  "config": { "eventName": "alert_received", "aggregation": "count" },
  "tabId": "tab-uuid",
  "gridLayout": { "x": 0, "y": 0, "w": 4, "h": 3 },
  "idempotencyKey": "number:Total alerts:Overview"
}
cardType must be one of number, rate, line, bar, pie, table, funnel, gauge. The per-type config shape is validated server-side; see the portal Metrics tab for the canonical UI.

List dashboard tabs

GET /api/v1/runtime/observability/dashboard-tabs?agentId=...
Returns the tabs configured for the agent, ordered by sortOrder ASC.
{
  "success": true,
  "total": 2,
  "tabs": [
    { "id": "tab-uuid", "name": "Overview", "sortOrder": 0 },
    { "id": "tab-uuid", "name": "Vendas", "sortOrder": 1 }
  ]
}

Upsert a dashboard tab

POST /api/v1/runtime/observability/dashboard-tabs
Idempotent on (agentId, name). This is the endpoint metrics.defineTab(...) → metrics.sync() (SDK) and rf metrics sync (CLI) call.
{ "agentId": "agent-uuid", "name": "Vendas", "sortOrder": 0 }
Response: { "success": true, "created": true, "id": "tab-uuid", "tab": { ... } }. created is false on subsequent calls with the same name; if sortOrder is provided and differs, it gets updated.

Aggregating event values via MCP

KPI-style aggregation (single number — sum, avg, count, rate, distinct_count, group_by) is not exposed over REST, but it’s available to MCP-compatible clients (Claude, Claude Code, Cursor, anything connecting through the public MCP connector) via three tools:
ToolScopeWhat it does
aggregate_eventsmcp:readSingle-number KPI or time/category series. Supports dateGrouping for time series and group_by for distributions.
query_eventsmcp:readTable-style read — mode='raw' for individual rows, mode='aggregate' with groupBy + metrics for grouped reports.
render_dashboard_cardmcp:readPass a cardId and get back the current rendered value, dispatching on the card’s cardType and config. Supports number / rate / line / bar / pie / gauge / table / funnel.
These mirror the runtime endpoints events/query and events/feed (which are REST), but add the standalone aggregate that REST doesn’t cover. See MCP → Available MCP Tools for the full catalogue.

What’s still portal-only

Over REST, the KPI-style standalone aggregate (e.g. “sum of amount where category = electronics for the last 7 days”) and the discovery endpoints (get_event_names, get_event_properties, property-values) remain behind Auth0JwtGuard. If you need them server-to-server today:
  • Use MCP (aggregate_events covers exactly this shape).
  • Or use the REST events/query endpoint with mode: 'aggregate' + groupBy — the same numbers come back as rows.

Errors

The runtime API uses standard HTTP status codes:
StatusMeaning
400Invalid input — usually a Zod/DTO validation failure. The body has a message array.
401Missing / invalid API key.
403API key is valid but lacks access to the requested resource (wrong tenant, disabled scope).
404Resource not found. For reviews, this also fires when the ID belongs to another tenant.
409Conflict — for execution reviews, the execution already has one.
422Semantic validation error (e.g. comment shorter than 10 chars).
5xxServer error. Safe to retry with backoff.
Error responses share this shape:
{ "statusCode": 409, "message": "Execution already has a review", "error": "Conflict" }

Next Steps

Reviews module

Typed SDK wrapper for these endpoints

Observability Guide

Tracing, metrics, and the review feedback loop