Backend API Specification
Definitive API contract for River Agents -- all 61 HTTP endpoints served by Backend (:8005), exposed exclusively through TLO Gateway (:8001). Every request is JWT-validated and ACL-enforced at the gateway before reaching the backend. The river-agent microservice (:8007) never exposes endpoints directly.
Quick Navigation
API Conventions
Base URL
All endpoints are prefixed with /api/v1. TLO Gateway proxies requests from :8001/api/v1/* to Backend at :8005/api/v1/* after JWT validation and ACL enforcement.
Authentication
Every request requires Authorization: Bearer <access_token>. TLO Gateway middleware:
- Validates the JWT signature and expiry
- Extracts user context and injects headers:
X-User-ID,X-Org-ID,X-Workspace-ID,X-Internal-Call: true - Checks per-endpoint ACL permissions before forwarding
Backend resolves these headers via Depends(get_current_active_user) and enforces WHERE org_id = {X-Org-ID} on every query.
Response Envelope
Success:
{
"success": true,
"data": {},
"message": "Operation successful"
}
Error:
{
"success": false,
"error": {
"code": "AGENT_NOT_FOUND",
"message": "Agent with id a1b2c3d4 does not exist in this workspace."
}
}
Pagination Envelope
All list endpoints return:
{
"items": [],
"total": 142,
"page": 1,
"page_size": 20,
"total_pages": 8
}
Default page_size is 20. Maximum page_size is 100.
Common Error Codes
| HTTP Status | Error Code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | Request body or query parameter failed validation |
| 401 | UNAUTHORIZED | Missing or invalid JWT |
| 403 | FORBIDDEN | Valid JWT but insufficient permissions |
| 404 | NOT_FOUND | Resource does not exist or belongs to another org |
| 409 | CONFLICT | State transition conflict or duplicate resource |
| 422 | UNPROCESSABLE_ENTITY | Semantically invalid (e.g., agent config fails validation) |
| 429 | RATE_LIMITED | Too many requests; Retry-After header included |
| 500 | INTERNAL_ERROR | Unexpected server error |
| 502 | UPSTREAM_ERROR | Downstream service (river-agent, Temporal) unavailable |
| 504 | TIMEOUT | Upstream service did not respond in time |
Soft Delete
Agents use soft delete (deleted_at IS NOT NULL). All list endpoints exclude soft-deleted records unless include_deleted=true is explicitly passed.
ID and Timestamp Format
Top-level entity IDs (agents, templates, policies, executions, approvals) are UUID v4 strings. Child record IDs (triggers, versions, bindings) are integers. All timestamps are ISO 8601 with timezone: "2026-04-23T14:30:00Z".
Endpoint Group Summary
| Group | Count | Page(s) Served |
|---|---|---|
| Agent CRUD | 6 | Agent Library, Create Guided, Agent Detail |
| Agent Lifecycle | 5 | Agent Detail (hero actions) |
| Agent Versions | 4 | Agent Detail -- Versions tab |
| Agent Triggers | 4 | Agent Detail -- Triggers tab |
| Agent Data Sources | 3 | Agent Detail -- Data Sources tab |
| Agent Tools | 2 | Agent Detail -- Tools tab |
| Agent Policies | 4 | Agent Detail -- Governance tab |
| Agent Metrics and Executions | 3 | Agent Detail -- Metrics tab, Runs tab, Audit tab |
| AI-Powered Creation | 3 | AI Create page |
| Templates | 5 | Template Library, Template Detail |
| Executions and Runs | 7 | Runs List, Run Detail |
| Approvals | 5 | Runs page (approval modal) |
| Monitoring | 4 | System Monitoring page |
| Settings | 7 | Settings page (6 tabs) |
| Audit and Logs | 3 | Audit and Logs page |
| Total | 65 | 12 pages |
Agent CRUD
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/agents | agent:read | List agents with filtering, search, and pagination |
| GET | /api/v1/agents/stats | agent:read | Workspace-level KPI aggregates for the Agent Library dashboard |
| POST | /api/v1/agents | agent:create | Create a new agent in draft status |
| GET | /api/v1/agents/{id} | agent:read | Get full agent detail with computed fields |
| PUT | /api/v1/agents/{id} | agent:update | Update agent configuration; creates a new version if deployed |
| DELETE | /api/v1/agents/{id} | agent:delete | Soft-delete agent; preserves execution history and audit logs |
GET /api/v1/agents
Query parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
search | string | No | -- | Full-text search on name and description (max 255 chars) |
status | string | No | -- | Comma-separated lifecycle statuses: draft, configured, validated, deployed, active, paused, archived |
category | string | No | -- | Business function: customer_support, sales, finance, risk_compliance, data_analyst, operations, executive, custom |
action_level | string | No | -- | read_respond, recommend, act_with_approval, fully_automated |
owner_user_id | uuid | No | -- | Filter by agent owner |
page | integer | No | 1 | Page number |
page_size | integer | No | 20 | Items per page (max 100) |
sort_by | string | No | updated_at | name, status, created_at, updated_at, health_status |
sort_order | string | No | desc | asc or desc |
Response includes per-agent trigger_summary and metrics_summary fields (7-day aggregates) computed on read. These are non-blocking reads from agent_metrics_hourly and do not add meaningful latency.
GET /api/v1/agents/stats
Returns workspace-level aggregate counts for the Agent Library KPI cards. No query parameters.
{
"total_agents": 28,
"active_agents": 15,
"running_agents": 3,
"pending_approvals": 7,
"runs_today": 142,
"success_rate_7d": 0.94,
"paused_agents": 4,
"draft_agents": 6,
"archived_agents": 3
}
| Field | Description |
|---|---|
running_agents | Agents with at least one running execution at request time |
pending_approvals | approval_requests with status = pending across all workspace agents |
runs_today | Executions with started_at in the last 24 hours |
success_rate_7d | Ratio of completed to completed + failed executions in last 7 days |
POST /api/v1/agents
Request body:
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
name | string | Yes | 1-255 chars | Agent display name |
description | string | No | max 2000 chars | Agent purpose description |
business_function | string | Yes | Enum (see query params above) | Business category |
domain | string | No | max 100 chars | Business domain tag (e.g., "Billing", "Logistics") |
action_level | string | Yes | Enum: read_respond, recommend, act_with_approval, fully_automated | Autonomy level |
governance_level | string | No | Enum: standard, strict, custom. Default: standard | Policy strictness |
instruction_set | string | Yes | 1-10000 chars | Natural language goal and behavioral instructions |
template_id | uuid | No | Valid template UUID | Source template |
tools | array | No | -- | Initial tool bindings |
tools[].tool_name | string | Yes (if tools provided) | max 100 chars | Tool identifier |
tools[].tool_config | object | No | -- | Tool-specific configuration overrides |
data_sources | array | No | -- | Initial data source bindings |
data_sources[].data_source_id | uuid | Yes (if data_sources provided) | Valid UUID in workspace | Data source to connect |
data_sources[].access_level | string | No | read_only or read_write. Default: read_only | Access level |
triggers | array | No | -- | Initial trigger configurations |
triggers[].trigger_type | string | Yes (if triggers provided) | manual, scheduled, event, api, threshold, workflow | Trigger type |
triggers[].trigger_config | object | Yes (if triggers provided) | Type-specific schema | Trigger configuration |
triggers[].is_active | boolean | No | Default: true | Whether trigger starts enabled |
approval_rules | object | No | -- | Approval gate configuration (see data-model doc for JSONB schema) |
notification_config | object | No | -- | Notification channel configuration |
Returns 201 Created with the full agent object. status is always draft on creation.
Error codes:
| HTTP | Code | Condition |
|---|---|---|
| 400 | VALIDATION_ERROR | Missing required fields, invalid enum values, instruction_set too long |
| 404 | NOT_FOUND | Referenced template_id or data_source_id does not exist |
| 409 | CONFLICT | Agent with same name already exists in workspace |
GET /api/v1/agents/{id}
Returns the full agent record plus a computed field block:
| Computed Field | Type | Description |
|---|---|---|
uptime_seconds | integer | Seconds since agent was last deployed (null if never deployed) |
uptime_human | string | Human-readable uptime: "13d 0h" |
deployed_at | datetime | Timestamp of last deployment (null if never deployed) |
trigger_summary | object | {total, types[], next_scheduled} |
recent_runs_count | integer | Execution count in last 7 days |
recent_success_rate | float | Success ratio in last 7 days (0.0-1.0) |
recent_avg_duration_ms | integer | Average execution duration in last 7 days |
PUT /api/v1/agents/{id}
All fields are optional -- only provided fields are updated. If the agent is in deployed, active, or paused status, this automatically creates a new version snapshot. If in draft or configured status, the current version record is updated in place.
Returns 200 OK with the updated full agent object.
Error codes:
| HTTP | Code | Condition |
|---|---|---|
| 409 | CONFLICT | Agent is archived and cannot be updated |
DELETE /api/v1/agents/{id}
Soft-delete: sets deleted_at to current timestamp. Active triggers are deactivated. Running executions are cancelled with reason: agent_deleted. Execution history, audit logs, and version history are preserved. Returns 204 No Content.
Agent Lifecycle
All lifecycle endpoints enforce the agent state machine. Invalid transitions return 409 CONFLICT.
| Method | Path | Permission | Description |
|---|---|---|---|
| POST | /api/v1/agents/{id}/deploy | agent:deploy | Validate config and transition to deployed; activates triggers |
| POST | /api/v1/agents/{id}/pause | agent:update | Suspend all triggers; running executions complete normally |
| POST | /api/v1/agents/{id}/resume | agent:update | Reactivate triggers; transition paused -> active |
| POST | /api/v1/agents/{id}/archive | agent:delete | Deactivate all triggers; cancel running executions; terminal state |
| POST | /api/v1/agents/{id}/validate | agent:read | Run pre-deployment checks without changing status |
POST /api/v1/agents/{id}/deploy
Performs: (1) pre-deployment validation, (2) immutable version snapshot creation, (3) status transition to deployed, (4) trigger registration with Temporal.
Response 200 OK:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "deployed",
"version_number": 3,
"deployed_at": "2026-04-23T14:30:00Z",
"validation_result": {
"passed": true,
"checks": [
{ "check": "data_source_connectivity", "status": "passed", "details": "All 2 data sources reachable" },
{ "check": "tool_availability", "status": "passed", "details": "All 4 tools available" },
{ "check": "policy_compliance", "status": "passed", "details": "No policy violations" },
{ "check": "trigger_validity", "status": "passed", "details": "All 2 triggers valid" }
]
},
"triggers_activated": 2
}
Error codes:
| HTTP | Code | Condition |
|---|---|---|
| 409 | CONFLICT | Agent is already deployed or active, or is archived |
| 422 | VALIDATION_FAILED | Pre-deployment check failed; response includes validation_result with status: failed entries |
If trigger registration fails for any configured trigger after version creation, deployment is rolled back and the agent returns to validated status.
POST /api/v1/agents/{id}/validate
Runs checks without deploying. Returns identical validation_result shape as deploy. Check names: data_source_connectivity, tool_availability, policy_compliance, trigger_validity, instruction_set. Status values: passed, failed, warning.
Returns 200 OK with:
{
"passed": false,
"checks": [
{ "check": "data_source_connectivity", "status": "passed", "details": "All 2 data sources reachable" },
{ "check": "policy_compliance", "status": "failed", "details": "Tool 'issue_refund' requires governance_level 'strict' but agent is set to 'standard'" },
{ "check": "instruction_set", "status": "warning", "details": "Instruction set is under 50 characters -- consider adding more detail" }
]
}
POST /api/v1/agents/{id}/pause
Response:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "paused",
"paused_at": "2026-04-23T14:30:00Z",
"triggers_suspended": 2,
"running_executions": 1
}
running_executions is the count of in-flight executions that will complete normally before the agent fully pauses.
POST /api/v1/agents/{id}/resume
Response:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "active",
"resumed_at": "2026-04-23T14:30:00Z",
"triggers_reactivated": 2
}
POST /api/v1/agents/{id}/archive
Response:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "archived",
"archived_at": "2026-04-23T14:30:00Z",
"executions_cancelled": 0,
"triggers_removed": 2
}
archived is a terminal state. The agent cannot be re-deployed. Use soft-delete (DELETE) to remove the agent from all list views.
Agent Versions
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/agents/{id}/versions | agent:read | List version history ordered by version_number DESC |
| GET | /api/v1/agents/{id}/versions/{version_id} | agent:read | Get a specific version record with full configuration snapshot |
| GET | /api/v1/agents/{id}/versions/diff | agent:read | Structured diff between two version records |
| POST | /api/v1/agents/{id}/versions | agent:update | Manually create a checkpoint version snapshot |
| POST | /api/v1/agents/{id}/versions/{version_id}/rollback | agent:deploy | Roll back to a prior version by creating a copy as the new latest |
GET /api/v1/agents/{id}/versions
Per-version response fields:
| Field | Type | Description |
|---|---|---|
id | integer | Version row ID (internal) |
version_number | integer | Sequential version number |
deployment_state | string | draft, active, or archived |
instruction_set | string | Agent instructions at this version |
tools | array | Tools bound at this version |
data_sources | array | Data sources bound at this version |
approval_rules | object | Approval rules at this version |
trigger_config | object | Trigger config snapshot |
created_by | object | {id, name} of the user who created this version |
created_at | datetime | Version creation timestamp |
change_summary | string | User-provided or auto-generated change description (null if not set) |
GET /api/v1/agents/{id}/versions/diff
Query parameters: from (version number) and to (version number). Returns a structured diff of instruction_set, tools, data_sources, approval_rules, and trigger_config between the two versions.
POST /api/v1/agents/{id}/versions/{version_id}/rollback
Rollback does not restore the old record. It creates a new agent_versions record as an exact copy of the target version, increments version_number, and sets that as current_version_id. This preserves the linear version history. Audit records agent.version_rolled_back with both source and target version IDs.
Agent Triggers
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/agents/{id}/triggers | agent:read | List all trigger configurations for the agent |
| POST | /api/v1/agents/{id}/triggers | agent:update | Add a new trigger |
| PUT | /api/v1/agents/{id}/triggers/{trigger_id} | agent:update | Update an existing trigger |
| DELETE | /api/v1/agents/{id}/triggers/{trigger_id} | agent:update | Remove a trigger |
GET /api/v1/agents/{id}/triggers
Per-trigger response fields:
| Field | Type | Description |
|---|---|---|
id | integer | Trigger ID |
trigger_type | string | manual, scheduled, event, api, threshold, workflow |
trigger_config | object | Type-specific configuration (see data-model doc, section JSONB Schema Specifications) |
is_active | boolean | Whether the trigger is currently enabled |
last_fired_at | datetime | Timestamp of the last execution started by this trigger (null if never fired) |
next_fire_at | datetime | Next scheduled fire time (null for non-scheduled triggers) |
created_at | datetime | Creation timestamp |
POST /api/v1/agents/{id}/triggers
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
trigger_type | string | Yes | Trigger type enum |
trigger_config | object | Yes | Type-specific configuration |
is_active | boolean | No | Default: true |
PUT /api/v1/agents/{id}/triggers/{trigger_id}
All fields optional. Returns updated trigger object. For a deployed agent, trigger config changes take effect on the next trigger fire -- they do not require a re-deploy for is_active changes. Changes to trigger_config on an active agent do require re-deploy to take effect on the version snapshot.
Agent Data Sources
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/agents/{id}/data-sources | agent:read | List bound data sources with connection status |
| POST | /api/v1/agents/{id}/data-sources | agent:update | Bind a data source (connectivity tested before binding) |
| DELETE | /api/v1/agents/{id}/data-sources/{binding_id} | agent:update | Unbind a data source |
GET /api/v1/agents/{id}/data-sources
Per-binding response fields:
| Field | Type | Description |
|---|---|---|
binding_id | string | Binding record ID |
data_source_id | uuid | Data source ID (references platform data_sources table) |
name | string | Data source display name |
type | string | saas_api, database, file, warehouse |
connector_type | string | zendesk, postgres, confluence, etc. |
access_level | string | read_only or read_write |
schema_filter | object | Column/table restrictions (allowed_tables, excluded_columns) -- null if unrestricted |
status | string | connected, disconnected, error |
last_tested_at | datetime | Last successful connectivity check (null if never tested) |
POST /api/v1/agents/{id}/data-sources
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
data_source_id | uuid | Yes | Data source to bind (must exist in same workspace) |
access_level | string | No | Default: read_only |
schema_filter | object | No | {allowed_tables: [], excluded_columns: []} |
A connectivity test is performed before the binding record is created. Returns 409 CONFLICT if the data source is already bound.
Agent Tools
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/agents/{id}/tools | agent:read | List tools bound to the agent's active version |
| PUT | /api/v1/agents/{id}/tools | agent:update | Replace all tool bindings (full replacement, not partial) |
GET /api/v1/agents/{id}/tools
Per-tool response fields:
| Field | Type | Description |
|---|---|---|
tool_name | string | Tool identifier |
tool_config | object | Configuration overrides (null if using defaults) |
category | string | reasoning, execution, or interaction |
description | string | Human-readable tool description |
requires_approval | boolean | Whether this tool is in the agent's approval_rules.require_approval_for list |
PUT /api/v1/agents/{id}/tools
The provided tools array becomes the complete replacement set -- tools not included are unbound. Returns the updated tool list. Returns 404 NOT_FOUND if any tool_name does not exist in the tool registry.
Agent Policies
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/agents/{id}/policies | agent:read | List bound policies including inherited org-wide policies |
| POST | /api/v1/agents/{id}/policies | agent:update | Bind a governance policy to the agent |
| PUT | /api/v1/agents/{id}/policies/{policy_id} | agent:update | Toggle binding active state |
| DELETE | /api/v1/agents/{id}/policies/{policy_id} | agent:update | Unbind a workspace-specific policy |
Org-wide policies (those with workspace_id = NULL) are automatically evaluated for all agents and appear in the list response with scope: organization. They cannot be unbound per-agent via DELETE -- use PUT to toggle the binding off if the agent requires an exemption (subject to governance approval).
GET /api/v1/agents/{id}/policies
Per-binding response fields:
| Field | Type | Description |
|---|---|---|
binding_id | integer | Policy binding ID |
policy.id | uuid | Policy identifier |
policy.name | string | Policy name |
policy.description | string | Policy description |
policy.scope | string | organization or workspace |
policy.conditions | object | Policy condition expressions |
policy.enforcement | string | block, gate, alert, log |
policy.is_active | boolean | Whether the policy is globally active |
is_active | boolean | Whether this binding is active for this specific agent |
bound_at | datetime | When the policy was bound to this agent |
POST /api/v1/agents/{id}/policies
Request body: { "policy_id": "pol-001" }. The policy must already exist at org or workspace level.
Agent Metrics and Executions
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/agents/{id}/metrics | agent:read | Daily aggregate metrics over a configurable time range |
| GET | /api/v1/agents/{id}/executions | agent:read | Paginated execution list for a specific agent |
| GET | /api/v1/agents/{id}/audit | agent:read | Audit log entries scoped to a single agent |
GET /api/v1/agents/{id}/metrics
Query parameters: period (24h, 7d, 30d, 90d; default 7d), granularity (hour, day; default day).
Response:
{
"agent_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"period": "7d",
"summary": {
"total_executions": 42,
"successful": 40,
"failed": 2,
"success_rate": 0.952,
"avg_duration_ms": 12400,
"avg_turns": 4.2,
"total_tokens_input": 128000,
"total_tokens_output": 45000,
"total_approvals_requested": 15,
"total_approvals_approved": 14,
"total_approvals_rejected": 1
},
"daily": [
{
"date": "2026-04-23",
"executions": 8,
"successful": 7,
"failed": 1,
"avg_duration_ms": 11200,
"avg_turns": 3.8,
"tokens_input": 21000,
"tokens_output": 7200
}
]
}
GET /api/v1/agents/{id}/executions
Query parameters: status (comma-separated execution statuses), page, page_size. Returns paginated execution objects. See Executions and Runs for the full execution object shape.
GET /api/v1/agents/{id}/audit
Query parameters: page, page_size. Returns paginated audit log entries scoped to this agent. See Audit and Logs for the full audit entry shape.
AI-Powered Creation
| Method | Path | Permission | Description |
|---|---|---|---|
| POST | /api/v1/agents/ai/generate | agent:create | Generate full agent config from natural language; returns SSE stream |
| POST | /api/v1/agents/ai/suggest-goal | agent:create | Generate a suggested instruction_set from name, description, and business function |
| POST | /api/v1/agents/ai/refine | agent:create | Refine an AI-generated draft using a follow-up prompt |
POST /api/v1/agents/ai/generate
Accepts a natural language description and orchestrates generation via the river-agent microservice. The agent is NOT created by this endpoint -- the frontend displays the generated config for user review, then calls POST /api/v1/agents to persist it.
Request body:
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
prompt | string | Yes | 10-5000 chars | Natural language description of the desired agent |
quick_starter_id | string | No | Valid quick starter ID | Pre-defined starter prompt (e.g., customer_support, data_pipeline) |
Returns Server-Sent Events (SSE). The connection stays open for the duration of generation. Each event corresponds to one of four generation stages.
SSE event format:
event: stage_update
data: {"stage": 1, "label": "Understanding Requirements", "progress": 25}
event: stage_update
data: {"stage": 2, "label": "Selecting Tools and Data Sources", "progress": 50}
event: stage_update
data: {"stage": 3, "label": "Drafting Instruction Set", "progress": 75}
event: stage_update
data: {"stage": 4, "label": "Applying Governance", "progress": 100}
event: generation_complete
data: {"generation_id": "gen-abc-123", "configuration": { ... }}
The configuration object in generation_complete has the same shape as the POST /api/v1/agents request body. The caller passes this directly to create the agent.
Error codes:
| HTTP | Code | Condition |
|---|---|---|
| 400 | VALIDATION_ERROR | prompt too short or too long |
| 502 | UPSTREAM_ERROR | river-agent microservice unavailable |
| 504 | TIMEOUT | Generation did not complete within 60 seconds |
POST /api/v1/agents/ai/suggest-goal
Generates a suggested instruction_set string from lightweight inputs. Used by the Guided Wizard "Generate with AI" button on the Instructions step.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Agent name |
description | string | No | Short description of agent purpose |
business_function | string | Yes | Business category enum |
Returns 200 OK:
{
"suggestion": "You are a billing support specialist. Your goal is to ..."
}
POST /api/v1/agents/ai/refine
Accepts a generation_id from a prior generation and a follow-up prompt to refine the configuration. Returns a new SSE stream with the same format as /generate.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
generation_id | string | Yes | ID from a prior generation_complete event |
prompt | string | Yes | Follow-up refinement instruction |
Templates
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/templates | agent:read | List templates -- platform-provided and workspace-custom |
| GET | /api/v1/templates/{id} | agent:read | Get full template including required tools and data source types |
| POST | /api/v1/templates | agent:create | Publish a custom template from an existing agent configuration |
| PATCH | /api/v1/templates/{id} | agent:update | Update a custom template (platform templates are read-only) |
| DELETE | /api/v1/templates/{id} | agent:delete | Delete a custom template (platform templates cannot be deleted) |
GET /api/v1/templates
Query parameters:
| Parameter | Type | Description |
|---|---|---|
category | string | Filter by business_function |
search | string | Text search on name and description |
type | string | platform (built-in only) or custom (workspace-created only) |
page | integer | Page number |
page_size | integer | Items per page (max 100) |
Per-template response fields:
| Field | Type | Description |
|---|---|---|
id | uuid | Template ID |
name | string | Template name |
description | string | Template description |
category | string | Business function category |
type | string | platform or custom |
icon_name | string | Icon identifier for UI rendering |
icon_bg | string | Icon background color (hex) |
icon_color | string | Icon foreground color (hex) |
governance_default | string | Default governance level when creating from this template |
required_tools | array | Tools required for this template |
required_data_source_types | array | Data source connector types the template expects |
usage_count | integer | Number of agents created from this template |
created_by | object | {id, name} -- null for platform templates |
created_at | datetime | Creation timestamp |
POST /api/v1/templates
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
source_agent_id | uuid | Yes | Agent whose current version configuration becomes the template base |
name | string | Yes | Template name |
description | string | Yes | Template description |
category | string | Yes | Business function category |
template_variables | array | No | Parameterized fields that users fill in when creating from template |
Returns 201 Created with the full template object.
Executions and Runs
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/agents/runs | agent:read | List runs across all workspace agents |
| GET | /api/v1/agents/{id}/runs | agent:read | List runs for a specific agent |
| GET | /api/v1/agents/runs/{execution_id} | agent:read | Get a single execution record with full detail |
| GET | /api/v1/agents/runs/{execution_id}/logs | agent:read | Get turn-by-turn log entries for an execution |
| POST | /api/v1/agents/{id}/runs | agent:execute | Manually trigger an agent execution |
| POST | /api/v1/agents/runs/{execution_id}/stop | agent:execute | Cancel a running execution |
| POST | /api/v1/agents/runs/{execution_id}/retry | agent:execute | Re-trigger with the same context as the original run |
GET /api/v1/agents/runs
Query parameters:
| Parameter | Type | Description |
|---|---|---|
agent_id | uuid | Filter to a specific agent |
status | string | Comma-separated: queued, running, paused, completed, failed, cancelled, approval_pending, approval_expired |
trigger_type | string | Filter by trigger type |
from | ISO 8601 | Start of started_at range |
to | ISO 8601 | End of started_at range |
page | integer | Page number |
page_size | integer | Items per page (max 100) |
GET /api/v1/agents/runs/{execution_id}
Returns the full execution record:
{
"id": "exec-001-uuid",
"agent_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"agent_name": "Customer Support Agent",
"agent_version_id": 3,
"version_number": 3,
"status": "completed",
"trigger_type": "event",
"trigger_payload": {
"ticket_id": "TKT-9912",
"priority": "high"
},
"started_at": "2026-04-23T14:30:00Z",
"completed_at": "2026-04-23T14:30:45Z",
"duration_ms": 45200,
"turn_count": 4,
"tokens_input": 3200,
"tokens_output": 1100,
"approval_count": 1,
"output_summary": "Drafted response to TKT-9912. Refund of $75 approved and issued.",
"error_message": null
}
GET /api/v1/agents/runs/{execution_id}/logs
Returns the turn-by-turn log:
{
"items": [
{
"id": 1,
"execution_id": "exec-001-uuid",
"turn_number": 1,
"log_type": "reasoning",
"content": {
"thought": "I need to look up the ticket details and check the customer's history.",
"next_action": "get_ticket_history"
},
"model_used": "balanced",
"tokens_input": 800,
"tokens_output": 220,
"latency_ms": 1240,
"created_at": "2026-04-23T14:30:02Z"
},
{
"id": 2,
"execution_id": "exec-001-uuid",
"turn_number": 2,
"log_type": "tool_call",
"content": {
"tool_name": "get_ticket_history",
"arguments": { "ticket_id": "TKT-9912" },
"result": { "history": [...] },
"governance_decision": "executed"
},
"model_used": null,
"tokens_input": null,
"tokens_output": null,
"latency_ms": 340,
"created_at": "2026-04-23T14:30:04Z"
}
],
"total": 8,
"page": 1,
"page_size": 20,
"total_pages": 1
}
log_type values: reasoning, tool_call, tool_result, approval_requested, approval_resolved, error, observation.
POST /api/v1/agents/{id}/runs
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
trigger_payload | object | No | Optional context payload passed to the agent as the trigger input |
input_overrides | object | No | Override specific agent_version fields for this run only (test mode) |
Returns 202 Accepted:
{
"execution_id": "exec-001-uuid",
"status": "queued"
}
The Temporal workflow is started immediately. Poll GET /api/v1/agents/runs/{execution_id} or connect to /ws/agent-runs/{execution_id} for live status.
POST /api/v1/agents/runs/{execution_id}/stop
Sends a cancellation signal to the Temporal workflow. The current tool call is aborted. Execution transitions to cancelled with reason: user_requested. Returns 200 OK with the updated execution object.
POST /api/v1/agents/runs/{execution_id}/retry
Creates a new execution using the same agent_version_id and trigger_payload as the original run. Returns 202 Accepted with a new execution_id.
Error codes:
| HTTP | Code | Condition |
|---|---|---|
| 400 | AGENT_NOT_ACTIVE | Agent is not in active status; manual runs require active status |
| 404 | EXECUTION_NOT_FOUND | {execution_id} does not exist |
| 429 | RATE_LIMITED | Workspace concurrent run limit exceeded |
Approvals
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/agents/approvals | agent:approve | List approval requests; defaults to status=pending |
| GET | /api/v1/agents/approvals/{id} | agent:approve | Get a single approval request with full context |
| GET | /api/v1/agents/{id}/approvals | agent:read | List all approval requests for a specific agent |
| PATCH | /api/v1/agents/approvals/{id} | agent:approve | Resolve an approval (approve, reject, or edit-and-approve) |
| POST | /api/v1/agents/approvals/{id}/expire | agent:deploy | Force-expire a pending approval (emergency use; Workspace Admin only) |
GET /api/v1/agents/approvals
Query parameters:
| Parameter | Type | Description |
|---|---|---|
status | string | Default: pending. Also: approved, rejected, expired, auto_approved |
agent_id | uuid | Filter to a specific agent |
from | ISO 8601 | Start of created_at range |
to | ISO 8601 | End of created_at range |
page | integer | Page number |
page_size | integer | Items per page (max 100) |
GET /api/v1/agents/approvals/{id}
Returns the full approval request with all context for the review screen:
{
"id": "apr-001-uuid",
"execution_id": "exec-001-uuid",
"agent_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"agent_name": "Customer Support Agent",
"status": "pending",
"tool_name": "issue_refund",
"tool_arguments": {
"amount": 150.00,
"charge_id": "ch_abc123",
"reason": "Product defect -- customer reported within 30 days"
},
"reasoning_summary": "The customer reported a product defect within the 30-day window. The refund amount of $150 exceeds the $100 auto-approve threshold, requiring manual approval per policy.",
"risk_context": {
"policy_checks": [
{ "policy": "Refund Amount Limit", "result": "triggered", "detail": "Amount $150 exceeds $100 threshold" }
],
"data_classification": "internal"
},
"historical_precedent": {
"similar_approvals_30d": 12,
"approval_rate": 0.917,
"last_approved_at": "2026-04-21T10:30:00Z"
},
"assigned_approver_id": "user-001",
"expires_at": "2026-04-24T14:30:00Z",
"created_at": "2026-04-23T14:30:00Z",
"resolved_at": null,
"resolved_by": null,
"resolution_note": null,
"modified_args": null
}
PATCH /api/v1/agents/approvals/{id}
Three resolution paths:
Approve:
{
"status": "APPROVED",
"note": "Verified against policy; customer is within 30-day window."
}
Reject:
{
"status": "REJECTED",
"reason": "Customer is outside the 30-day refund window per updated policy."
}
Edit and Approve:
{
"status": "APPROVED",
"modified_args": { "amount": 75.00, "charge_id": "ch_abc123" },
"note": "Approved for partial refund only -- full amount not warranted."
}
After resolution, TLO Gateway sends the approval_resolved signal to the Temporal workflow. Execution resumes on the next Temporal heartbeat. modified_args replaces the original tool arguments in the dispatch if provided.
Error codes:
| HTTP | Code | Condition |
|---|---|---|
| 404 | NOT_FOUND | Approval request does not exist |
| 409 | CONFLICT | Request is already resolved (not in pending status) |
| 410 | GONE | Request has passed expires_at |
Monitoring
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/agents/monitoring/summary | agent:monitor | Workspace aggregate: active count, 24h runs, avg latency, failure rate |
| GET | /api/v1/agents/monitoring/throughput | agent:monitor | Hourly run counts for the last 24 hours, broken down by status |
| GET | /api/v1/agents/monitoring/cluster | agent:monitor | Infrastructure health: per-instance CPU, memory, active runs |
| GET | /api/v1/agents/monitoring/alerts | agent:monitor | Active anomaly alerts and auto-pause events |
GET /api/v1/agents/monitoring/summary
{
"active_agents": 15,
"running_executions": 3,
"pending_approvals": 7,
"runs_24h": 142,
"success_rate_24h": 0.944,
"avg_latency_ms_24h": 11800,
"failure_rate_24h": 0.056,
"token_cost_24h_usd": 12.40
}
GET /api/v1/agents/monitoring/throughput
Returns an array of hourly buckets for the last 24 hours:
{
"series": [
{
"hour": "2026-04-23T13:00:00Z",
"completed": 8,
"failed": 1,
"running": 2,
"approval_pending": 0
}
]
}
GET /api/v1/agents/monitoring/cluster
Returns per-instance infrastructure health. Used by the cluster health panel on the Monitoring page.
{
"instances": [
{
"instance_id": "worker-01",
"cpu_percent": 42,
"memory_percent": 61,
"active_runs": 2,
"uptime_seconds": 864000,
"status": "healthy"
}
]
}
GET /api/v1/agents/monitoring/alerts
Returns active anomaly alerts and auto-pause events:
{
"items": [
{
"id": "alert-001",
"type": "auto_pause",
"agent_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"agent_name": "Finance Reconciliation Agent",
"reason": "3 consecutive failures detected",
"severity": "warning",
"created_at": "2026-04-23T12:00:00Z",
"resolved_at": null
}
],
"total": 1
}
Settings
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/workspace/settings | agent:read | Get all workspace-level River Agents settings |
| PATCH | /api/v1/workspace/settings | agent:update | Update general workspace settings |
| GET | /api/v1/workspace/settings/api-keys | agent:read | List workspace API keys |
| POST | /api/v1/workspace/settings/api-keys | agent:update | Create a new workspace API key |
| DELETE | /api/v1/workspace/settings/api-keys/{key_id} | agent:update | Revoke an API key |
| POST | /api/v1/workspace/settings/webhooks | agent:update | Register a new outbound webhook |
| DELETE | /api/v1/workspace/settings/webhooks/{webhook_id} | agent:update | Remove a webhook registration |
GET /api/v1/workspace/settings
Returns all settings across all tabs:
| Field | Type | Default | Description |
|---|---|---|---|
default_action_level | string | recommend | Default action level for new agents created in this workspace |
max_concurrent_runs | integer | 10 | Maximum simultaneous executions across all workspace agents |
default_max_turns | integer | 15 | Default max_turns applied to new agent versions |
default_tool_timeout_seconds | integer | 30 | Default per-tool call timeout |
approval_timeout_hours | integer | 24 | Default expiry for pending approval requests |
approval_escalation_hours | integer | 8 | Hours before escalation notification fires |
max_agents | integer | 50 | Maximum agents in the workspace (quota) |
max_runs_per_day | integer | 1000 | Maximum daily executions across all workspace agents |
default_model_tier | string | balanced | Default LLM tier: fast, balanced, reasoning, coding |
governance_level | string | standard | Workspace-wide governance level applied unless overridden per agent |
notification_channels | object | {} | Configured notification channels (Slack, email, PagerDuty) |
webhooks | array | [] | Registered outbound webhooks |
api_keys | array | [] | Workspace API keys (masked -- only last 4 chars visible after creation) |
POST /api/v1/workspace/settings/api-keys
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Human-readable key name |
scopes | array | Yes | Permissions granted to this key (subset of agent:* permissions) |
expires_at | datetime | No | Key expiration (null for non-expiring) |
Returns 201 Created with the full key value. The raw key is only returned once. Subsequent reads return a masked version.
POST /api/v1/workspace/settings/webhooks
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | HTTPS endpoint URL |
events | array | Yes | Event types to deliver (e.g., execution.completed, tool.approval_requested) |
secret | string | No | HMAC signing secret for payload verification |
Returns 201 Created. A test ping is sent to the URL before the webhook is activated.
Audit and Logs
| Method | Path | Permission | Description |
|---|---|---|---|
| GET | /api/v1/audit | agent:audit | Query the immutable audit log with filtering |
| GET | /api/v1/audit/{entry_id} | agent:audit | Get a single audit log entry with full payload |
| POST | /api/v1/audit/export | agent:audit | Queue or stream an export of filtered audit records |
GET /api/v1/audit
Query parameters:
| Parameter | Type | Description |
|---|---|---|
agent_id | uuid | Scope to a specific agent |
event_type | string | Filter by event type or prefix (e.g., tool. matches all tool events) |
actor_type | string | human, agent, or system |
outcome | string | success, failure, or blocked |
from | ISO 8601 | Start of created_at range |
to | ISO 8601 | End of created_at range |
page | integer | Page number (default: 1) |
page_size | integer | Items per page (default: 50, max: 200) |
Per-entry response fields:
| Field | Type | Description |
|---|---|---|
id | uuid | Audit log entry ID |
event_type | string | Event type (see governance-safety doc for full taxonomy) |
actor_type | string | human, agent, or system |
actor_user_id | uuid | User ID (null for system events; may be nulled for GDPR erasure) |
agent_id | uuid | Agent ID (null for workspace-level events) |
execution_id | uuid | Execution ID (null for non-execution events) |
event_payload | object | Event-specific payload fields |
ip_address | string | Source IP (for human actor events) |
user_agent | string | Browser or client identifier (for human actor events) |
created_at | datetime | Timestamp of the event (write time) |
The audit log is write-once. POST, PATCH, and DELETE requests to /api/v1/audit return 405 Method Not Allowed. There is no endpoint to delete or modify audit entries.
POST /api/v1/audit/export
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
filters | object | Yes | Same filter fields as GET /api/v1/audit |
format | string | Yes | csv or json |
delivery | string | Yes | stream (chunked response) or email (queued delivery) |
delivery: stream returns a chunked response for immediate download. delivery: email queues the export and delivers to the requesting user's email -- use for exports exceeding 10,000 rows. Returns 202 Accepted with { "export_id": "exp-001", "estimated_rows": 4200 }.