Sovereign Agent BFF — Handler Dispatch Engine — OpenAPI 1.0.0
<!-- GENERATED by scripts/render-platform-map-api-docs.mjs — do not hand-edit; edit apps/agent-bff/openapi/<spec>.json and re-run. --> Scope: The published reference for Sovereign Agent BFF — Handler Dispatch Engine. 17 paths — the measured spec-vs-implementation coverage lives in the complete API inventory. Status: rendered 2026-11-09 from the OpenAPI source for Sovereign Agent BFF — Handler Dispatch Engine — regenerate with node scripts/render-platform-map-api-docs.mjs. See also: the OpenAPI spec index.
Servers
| URL | Notes |
|---|---|
| http://127.0.0.1:3030 | Local dev (DEV_SKIP_AUTH=1) |
Tags
| Tag | Summary |
|---|---|
| dispatch-status | Engine health, flags, counters, documentation |
| dispatch-instance | Handler instance registration, lifecycle, heartbeat |
| dispatch-route | Declarative signal routing table |
| dispatch-signal | Signal routing via the dispatch engine |
| dispatch-circuit-breaker | 3-state circuit breaker (closed/open/half_open) |
| dispatch-audit | Append-only dispatch audit log |
| dispatch-sweeper | Periodic maintenance sweepers |
| dispatch-backfill | Migration tracking from old governance_handler_registry |
Paths — 17 paths / 22 operations
| Method | Path | Operation ID | Summary |
|---|---|---|---|
| GET | /v1/dispatch/status | getDispatchStatus | Engine status, flags, counters, database health |
| GET | /v1/dispatch/health | getDispatchHealth | Lightweight liveness check |
| GET | /v1/dispatch/docs | getDispatchDocs | Inline API reference with endpoint listing and transport types |
| GET | /v1/dispatch/instances | listDispatchInstances | List registered handler instances |
| GET | /v1/dispatch/instances/{instanceId} | getDispatchInstance | Get handler instance detail |
| DELETE | /v1/dispatch/instances/{instanceId} | deleteDispatchInstance | Disable a handler instance (soft delete) |
| POST | /v1/dispatch/instances/{instanceId}/heartbeat | heartbeatDispatchInstance | Update instance heartbeat timestamp |
| PATCH | /v1/dispatch/instances/{instanceId}/status | updateDispatchInstanceStatus | Update instance status (active/paused/disabled/deprecated) |
| POST | /v1/dispatch/register | registerDispatchInstance | Register a new handler instance (idempotent) |
| GET | /v1/dispatch/routes | listDispatchRoutes | List active dispatch routes |
| POST | /v1/dispatch/routes | registerDispatchRoute | Register a new dispatch route (idempotent on signal_pattern) |
| GET | /v1/dispatch/routes/{routeId} | getDispatchRoute | Get dispatch route detail |
| DELETE | /v1/dispatch/routes/{routeId} | deleteDispatchRoute | Disable a dispatch route (soft delete) |
| GET | /v1/dispatch/resolve | resolveDispatchRoutes | Resolve routes for a signal type and scope |
| POST | /v1/dispatch/signal | routeSignal | Route a signal through the dispatch engine |
| GET | /v1/dispatch/circuit-breaker/{instanceId} | getCircuitBreaker | Get circuit breaker state for an instance |
| PATCH | /v1/dispatch/circuit-breaker/{instanceId} | updateCircuitBreakerConfig | Update circuit breaker configuration thresholds |
| POST | /v1/dispatch/circuit-breaker/{instanceId}/reset | resetCircuitBreaker | Force-reset circuit breaker to closed state |
| GET | /v1/dispatch/audit | queryDispatchAudit | Query dispatch audit log entries |
| POST | /v1/dispatch/sweep | triggerDispatchSweep | Trigger all maintenance sweepers (ops-only recovery) |
| GET | /v1/dispatch/backfill | listDispatchBackfill | List pending backfill entries for migration tracking |
| POST | /v1/dispatch/backfill | createDispatchBackfill | Start a new backfill entry for a legacy handler |
Operations
GET /v1/dispatch/status
Summary: Engine status, flags, counters, database health
Tags: dispatch-status Operation ID: getDispatchStatus Responses:
| Status | Meaning |
|---|---|
| 200 | Full engine status including feature flags, counters, security gate state, legacy system hints |
GET /v1/dispatch/health
Summary: Lightweight liveness check
Tags: dispatch-status Operation ID: getDispatchHealth Responses:
| Status | Meaning |
|---|---|
| 200 | Engine is ready |
| 503 | Engine not ready (returns same body shape as 200 with status set to 503) |
GET /v1/dispatch/docs
Summary: Inline API reference with endpoint listing and transport types
Tags: dispatch-status Operation ID: getDispatchDocs Responses:
| Status | Meaning |
|---|---|
| 200 | API reference with endpoints, transport types, routing strategies, security features |
GET /v1/dispatch/instances
Summary: List registered handler instances
Tags: dispatch-instance Operation ID: listDispatchInstances
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| no | ||||
| no |
Responses:
| Status | Meaning |
|---|---|
| 200 | Instance list |
GET /v1/dispatch/instances/{instanceId}
Summary: Get handler instance detail
Tags: dispatch-instance Operation ID: getDispatchInstance
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| no |
Responses:
| Status | Meaning |
|---|---|
| 200 | Instance detail |
| 404 |
DELETE /v1/dispatch/instances/{instanceId}
Summary: Disable a handler instance (soft delete)
Tags: dispatch-instance Operation ID: deleteDispatchInstance
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| no |
Responses:
| Status | Meaning |
|---|---|
| 200 | Instance disabled |
| 404 |
POST /v1/dispatch/instances/{instanceId}/heartbeat
Summary: Update instance heartbeat timestamp
Tags: dispatch-instance Operation ID: heartbeatDispatchInstance
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| no |
Responses:
| Status | Meaning |
|---|---|
| 200 | Heartbeat updated |
| 404 |
PATCH /v1/dispatch/instances/{instanceId}/status
Summary: Update instance status (active/paused/disabled/deprecated)
Tags: dispatch-instance Operation ID: updateDispatchInstanceStatus
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| no |
Request body:
Content type: application/json
| Field | Type | Notes |
|---|---|---|
| status | string enum: active, paused, disabled, deprecated | New lifecycle status |
Responses:
| Status | Meaning |
|---|---|
| 200 | Status updated |
| 400 | |
| 404 |
POST /v1/dispatch/register
Summary: Register a new handler instance (idempotent)
Tags: dispatch-instance Operation ID: registerDispatchInstance Request body:
Content type: application/json
| Field | Type | Notes |
|---|
Responses:
| Status | Meaning |
|---|---|
| 200 | Instance registered (updated existing) |
| 400 |
GET /v1/dispatch/routes
Summary: List active dispatch routes
Tags: dispatch-route Operation ID: listDispatchRoutes
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| signal_pattern | query | no | string | Filter by signal pattern (e.g. claim.created) |
| no |
Responses:
| Status | Meaning |
|---|---|
| 200 | Route list |
POST /v1/dispatch/routes
Summary: Register a new dispatch route (idempotent on signal_pattern)
Tags: dispatch-route Operation ID: registerDispatchRoute Request body:
Content type: application/json
| Field | Type | Notes |
|---|
Responses:
| Status | Meaning |
|---|---|
| 200 | Route registered (updated existing) |
| 400 |
GET /v1/dispatch/routes/{routeId}
Summary: Get dispatch route detail
Tags: dispatch-route Operation ID: getDispatchRoute
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| no |
Responses:
| Status | Meaning |
|---|---|
| 200 | Route detail |
| 404 |
DELETE /v1/dispatch/routes/{routeId}
Summary: Disable a dispatch route (soft delete)
Tags: dispatch-route Operation ID: deleteDispatchRoute
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| no |
Responses:
| Status | Meaning |
|---|---|
| 200 | Route disabled |
| 404 |
GET /v1/dispatch/resolve
Summary: Resolve routes for a signal type and scope
Tags: dispatch-route Operation ID: resolveDispatchRoutes
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| signal_type | query | yes | string | Signal type/subject to resolve routes for |
| no |
Responses:
| Status | Meaning |
|---|---|
| 200 | Matched routes with instance details and circuit breaker state |
| 400 |
POST /v1/dispatch/signal
Summary: Route a signal through the dispatch engine
Tags: dispatch-signal Operation ID: routeSignal
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| dry_run | query | no | boolean | Resolve routes but do not dispatch |
| no |
Request body:
Content type: application/json
| Field | Type | Notes |
|---|
Responses:
| Status | Meaning |
|---|---|
| 200 | Signal dispatch result |
| 400 |
GET /v1/dispatch/circuit-breaker/{instanceId}
Summary: Get circuit breaker state for an instance
Tags: dispatch-circuit-breaker Operation ID: getCircuitBreaker
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| no |
Responses:
| Status | Meaning |
|---|---|
| 200 | Circuit breaker state |
| 404 |
PATCH /v1/dispatch/circuit-breaker/{instanceId}
Summary: Update circuit breaker configuration thresholds
Tags: dispatch-circuit-breaker Operation ID: updateCircuitBreakerConfig
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| no |
Request body:
Content type: application/json
| Field | Type | Notes |
|---|---|---|
| failure_threshold | int | Failures before tripping to open |
| recovery_timeout_ms | int | MS before open transitions to half_open |
| half_open_max_requests | int | Test requests allowed in half_open state |
Responses:
| Status | Meaning |
|---|---|
| 200 | Updated circuit breaker state (or { ok: true } if CB does not yet exist) |
| 400 | |
| 404 |
POST /v1/dispatch/circuit-breaker/{instanceId}/reset
Summary: Force-reset circuit breaker to closed state
Tags: dispatch-circuit-breaker Operation ID: resetCircuitBreaker
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| no |
Responses:
| Status | Meaning |
|---|---|
| 200 | Circuit breaker reset |
| 404 |
GET /v1/dispatch/audit
Summary: Query dispatch audit log entries
Tags: dispatch-audit Operation ID: queryDispatchAudit
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| signal_id | query | no | string | Filter by signal ID |
| handler_id | query | no | string | Filter by handler ID |
| outcome | query | no | string enum: dispatched, success, failed, timeout, rejected, circuit_breaker_open, load_shed, cancelled | Filter by outcome |
| limit | query | no | int | Maximum entries to return |
Responses:
| Status | Meaning |
|---|---|
| 200 | Audit log entries |
POST /v1/dispatch/sweep
Summary: Trigger all maintenance sweepers (ops-only recovery) Description: DISPATCH-15: Ops-only recovery endpoint. The primary dispatch engine path is event-driven (NATS consumers → routeSignal). This sweep endpoint is for manual recovery when the event path is disrupted. Not for periodic use in production — set DISPATCH_SWEEPER_ENABLED=0 in prod.
Tags: dispatch-sweeper Operation ID: triggerDispatchSweep
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| limit | query | no | int | Max items to process per sweeper |
Responses:
| Status | Meaning |
|---|---|
| 200 | Sweep results with counts per sweeper |
GET /v1/dispatch/backfill
Summary: List pending backfill entries for migration tracking
Tags: dispatch-backfill Operation ID: listDispatchBackfill
Parameters:
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
| limit | query | no | int | Maximum entries to return |
Responses:
| Status | Meaning |
|---|---|
| 200 | Backfill entries |
POST /v1/dispatch/backfill
Summary: Start a new backfill entry for a legacy handler
Tags: dispatch-backfill Operation ID: createDispatchBackfill Request body:
Content type: application/json
| Field | Type | Notes |
|---|---|---|
| source_handler_id | string | ID from governance_handler_registry |
| strategy | string enum: clone, split, merge, manual, skip | Migration strategy |
Responses:
| Status | Meaning |
|---|---|
| 200 | Backfill entry created or updated |
| 400 |
Full specification — verbatim source of truth
{
"openapi": "3.1.0",
"info": {
"title": "Sovereign Agent BFF — Handler Dispatch Engine",
"version": "1.0.0",
"description": "OpenAPI 3.1 fragment for the unified Handler Dispatch Engine (v3). Routes on sovereign-agent-bff :3030 under /v1/dispatch/*.\n\nDispatch engine phases: instance registry, signal routing, transport dispatch, circuit breaker, DLQ, security gates, backfill, sweeper, audit.\n\nHuman-readable: docs/architecture/HANDLER_DISPATCH_NATS_CONSUMER_PATTERNS.md\nSchema: database/schema/strata/s158_dispatch_engine.surql"
},
"servers": [
{
"url": "http://127.0.0.1:3030",
"description": "Local dev (DEV_SKIP_AUTH=1)"
}
],
"tags": [
{
"name": "dispatch-status",
"description": "Engine health, flags, counters, documentation"
},
{
"name": "dispatch-instance",
"description": "Handler instance registration, lifecycle, heartbeat"
},
{
"name": "dispatch-route",
"description": "Declarative signal routing table"
},
{
"name": "dispatch-signal",
"description": "Signal routing via the dispatch engine"
},
{
"name": "dispatch-circuit-breaker",
"description": "3-state circuit breaker (closed/open/half_open)"
},
{
"name": "dispatch-audit",
"description": "Append-only dispatch audit log"
},
{
"name": "dispatch-sweeper",
"description": "Periodic maintenance sweepers"
},
{
"name": "dispatch-backfill",
"description": "Migration tracking from old governance_handler_registry"
}
],
"paths": {
"/v1/dispatch/status": {
"get": {
"tags": [
"dispatch-status"
],
"summary": "Engine status, flags, counters, database health",
"operationId": "getDispatchStatus",
"responses": {
"200": {
"description": "Full engine status including feature flags, counters, security gate state, legacy system hints",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DispatchStatusResponse"
}
}
}
}
}
}
},
"/v1/dispatch/health": {
"get": {
"tags": [
"dispatch-status"
],
"summary": "Lightweight liveness check",
"operationId": "getDispatchHealth",
"responses": {
"200": {
"description": "Engine is ready",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"ok",
"not_ready"
]
},
"service": {
"type": "string",
"const": "dispatch-engine"
},
"mode": {
"type": "string",
"enum": [
"disabled",
"shadow",
"active"
]
},
"database": {
"type": "string",
"enum": [
"connected",
"disconnected"
]
},
"in_flight": {
"type": "integer"
},
"last_error": {
"type": "string",
"nullable": true
},
"security_rejections": {
"type": "integer"
},
"stale_instances": {
"type": "integer",
"description": "Number of stale instances detected by sweeper"
},
"consumers": {
"type": "object",
"properties": {
"started": {
"type": "boolean"
},
"count": {
"type": "integer"
}
}
}
}
}
}
}
},
"503": {
"description": "Engine not ready (returns same body shape as 200 with status set to 503)"
}
}
}
},
"/v1/dispatch/docs": {
"get": {
"tags": [
"dispatch-status"
],
"summary": "Inline API reference with endpoint listing and transport types",
"operationId": "getDispatchDocs",
"responses": {
"200": {
"description": "API reference with endpoints, transport types, routing strategies, security features",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"service": {
"type": "string"
},
"version": {
"type": "integer"
},
"endpoints": {
"type": "object"
},
"transport_types": {
"type": "array",
"items": {
"type": "string"
}
},
"routing_strategies": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"/v1/dispatch/instances": {
"get": {
"tags": [
"dispatch-instance"
],
"summary": "List registered handler instances",
"operationId": "listDispatchInstances",
"parameters": [
{
"$ref": "#/components/parameters/TransportQuery"
},
{
"$ref": "#/components/parameters/ScopeQuery"
}
],
"responses": {
"200": {
"description": "Instance list",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"service": {
"type": "string"
},
"count": {
"type": "integer"
},
"instances": {
"type": "array",
"items": {
"$ref": "#/components/schemas/DispatchInstance"
}
}
}
}
}
}
}
}
}
},
"/v1/dispatch/instances/{instanceId}": {
"get": {
"tags": [
"dispatch-instance"
],
"summary": "Get handler instance detail",
"operationId": "getDispatchInstance",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"responses": {
"200": {
"description": "Instance detail",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DispatchInstance"
}
}
}
},
"404": {
"$ref": "#/components/responses/NotFound"
}
}
},
"delete": {
"tags": [
"dispatch-instance"
],
"summary": "Disable a handler instance (soft delete)",
"operationId": "deleteDispatchInstance",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"responses": {
"200": {
"description": "Instance disabled",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"ok": {
"type": "boolean"
}
}
}
}
}
},
"404": {
"$ref": "#/components/responses/NotFound"
}
}
}
},
"/v1/dispatch/instances/{instanceId}/heartbeat": {
"post": {
"tags": [
"dispatch-instance"
],
"summary": "Update instance heartbeat timestamp",
"operationId": "heartbeatDispatchInstance",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"responses": {
"200": {
"description": "Heartbeat updated",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"ok": {
"type": "boolean"
}
}
}
}
}
},
"404": {
"$ref": "#/components/responses/NotFound"
}
}
}
},
"/v1/dispatch/instances/{instanceId}/status": {
"patch": {
"tags": [
"dispatch-instance"
],
"summary": "Update instance status (active/paused/disabled/deprecated)",
"operationId": "updateDispatchInstanceStatus",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"status"
],
"properties": {
"status": {
"type": "string",
"enum": [
"active",
"paused",
"disabled",
"deprecated"
],
"description": "New lifecycle status"
}
}
}
}
}
},
"responses": {
"200": {
"description": "Status updated",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"ok": {
"type": "boolean"
}
}
}
}
}
},
"400": {
"$ref": "#/components/responses/ValidationProblem"
},
"404": {
"$ref": "#/components/responses/NotFound"
}
}
}
},
"/v1/dispatch/register": {
"post": {
"tags": [
"dispatch-instance"
],
"summary": "Register a new handler instance (idempotent)",
"operationId": "registerDispatchInstance",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RegisterInstanceRequest"
}
}
}
},
"responses": {
"200": {
"description": "Instance registered (updated existing)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RegisterInstanceResponse"
}
}
}
},
"400": {
"$ref": "#/components/responses/ValidationProblem"
}
}
}
},
"/v1/dispatch/routes": {
"get": {
"tags": [
"dispatch-route"
],
"summary": "List active dispatch routes",
"operationId": "listDispatchRoutes",
"parameters": [
{
"name": "signal_pattern",
"in": "query",
"schema": {
"type": "string"
},
"description": "Filter by signal pattern (e.g. claim.created)"
},
{
"$ref": "#/components/parameters/ScopeQuery"
}
],
"responses": {
"200": {
"description": "Route list",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"service": {
"type": "string"
},
"count": {
"type": "integer"
},
"routes": {
"type": "array",
"items": {
"$ref": "#/components/schemas/DispatchRoute"
}
}
}
}
}
}
}
}
},
"post": {
"tags": [
"dispatch-route"
],
"summary": "Register a new dispatch route (idempotent on signal_pattern)",
"operationId": "registerDispatchRoute",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RegisterRouteRequest"
}
}
}
},
"responses": {
"200": {
"description": "Route registered (updated existing)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RegisterRouteResponse"
}
}
}
},
"400": {
"$ref": "#/components/responses/ValidationProblem"
}
}
}
},
"/v1/dispatch/routes/{routeId}": {
"get": {
"tags": [
"dispatch-route"
],
"summary": "Get dispatch route detail",
"operationId": "getDispatchRoute",
"parameters": [
{
"$ref": "#/components/parameters/RouteId"
}
],
"responses": {
"200": {
"description": "Route detail",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DispatchRoute"
}
}
}
},
"404": {
"$ref": "#/components/responses/NotFound"
}
}
},
"delete": {
"tags": [
"dispatch-route"
],
"summary": "Disable a dispatch route (soft delete)",
"operationId": "deleteDispatchRoute",
"parameters": [
{
"$ref": "#/components/parameters/RouteId"
}
],
"responses": {
"200": {
"description": "Route disabled",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"ok": {
"type": "boolean"
}
}
}
}
}
},
"404": {
"$ref": "#/components/responses/NotFound"
}
}
}
},
"/v1/dispatch/resolve": {
"get": {
"tags": [
"dispatch-route"
],
"summary": "Resolve routes for a signal type and scope",
"operationId": "resolveDispatchRoutes",
"parameters": [
{
"name": "signal_type",
"in": "query",
"required": true,
"schema": {
"type": "string",
"example": "claim.created"
},
"description": "Signal type/subject to resolve routes for"
},
{
"$ref": "#/components/parameters/ScopeQuery"
}
],
"responses": {
"200": {
"description": "Matched routes with instance details and circuit breaker state",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"service": {
"type": "string"
},
"signal_type": {
"type": "string"
},
"scope": {
"type": "string",
"nullable": true
},
"match_count": {
"type": "integer"
},
"routes": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ResolvedRoute"
}
}
}
}
}
}
},
"400": {
"$ref": "#/components/responses/ValidationProblem"
}
}
}
},
"/v1/dispatch/signal": {
"post": {
"tags": [
"dispatch-signal"
],
"summary": "Route a signal through the dispatch engine",
"operationId": "routeSignal",
"parameters": [
{
"name": "dry_run",
"in": "query",
"schema": {
"type": "boolean",
"default": false
},
"description": "Resolve routes but do not dispatch"
},
{
"$ref": "#/components/parameters/ScopeQuery"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RouteSignalRequest"
}
}
}
},
"responses": {
"200": {
"description": "Signal dispatch result",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RouteSignalResponse"
}
}
}
},
"400": {
"$ref": "#/components/responses/ValidationProblem"
}
}
}
},
"/v1/dispatch/circuit-breaker/{instanceId}": {
"get": {
"tags": [
"dispatch-circuit-breaker"
],
"summary": "Get circuit breaker state for an instance",
"operationId": "getCircuitBreaker",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"responses": {
"200": {
"description": "Circuit breaker state",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CircuitBreakerState"
}
}
}
},
"404": {
"$ref": "#/components/responses/NotFound"
}
}
},
"patch": {
"tags": [
"dispatch-circuit-breaker"
],
"summary": "Update circuit breaker configuration thresholds",
"operationId": "updateCircuitBreakerConfig",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"failure_threshold": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Failures before tripping to open"
},
"recovery_timeout_ms": {
"type": "integer",
"minimum": 1000,
"maximum": 3600000,
"description": "MS before open transitions to half_open"
},
"half_open_max_requests": {
"type": "integer",
"minimum": 1,
"maximum": 20,
"description": "Test requests allowed in half_open state"
}
}
}
}
}
},
"responses": {
"200": {
"description": "Updated circuit breaker state (or { ok: true } if CB does not yet exist)",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/CircuitBreakerState"
},
{
"type": "object",
"properties": {
"ok": {
"type": "boolean"
}
}
}
]
}
}
}
},
"400": {
"$ref": "#/components/responses/ValidationProblem"
},
"404": {
"$ref": "#/components/responses/NotFound"
}
}
}
},
"/v1/dispatch/circuit-breaker/{instanceId}/reset": {
"post": {
"tags": [
"dispatch-circuit-breaker"
],
"summary": "Force-reset circuit breaker to closed state",
"operationId": "resetCircuitBreaker",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"responses": {
"200": {
"description": "Circuit breaker reset",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"new_state": {
"$ref": "#/components/schemas/CircuitBreakerState"
}
}
}
}
}
},
"404": {
"$ref": "#/components/responses/NotFound"
}
}
}
},
"/v1/dispatch/audit": {
"get": {
"tags": [
"dispatch-audit"
],
"summary": "Query dispatch audit log entries",
"operationId": "queryDispatchAudit",
"parameters": [
{
"name": "signal_id",
"in": "query",
"schema": {
"type": "string"
},
"description": "Filter by signal ID"
},
{
"name": "handler_id",
"in": "query",
"schema": {
"type": "string"
},
"description": "Filter by handler ID"
},
{
"name": "outcome",
"in": "query",
"schema": {
"type": "string",
"enum": [
"dispatched",
"success",
"failed",
"timeout",
"rejected",
"circuit_breaker_open",
"load_shed",
"cancelled"
]
},
"description": "Filter by outcome"
},
{
"name": "limit",
"in": "query",
"schema": {
"type": "integer",
"default": 50,
"maximum": 1000
},
"description": "Maximum entries to return"
}
],
"responses": {
"200": {
"description": "Audit log entries",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"service": {
"type": "string"
},
"count": {
"type": "integer"
},
"entries": {
"type": "array",
"items": {
"$ref": "#/components/schemas/DispatchAuditEntry"
}
}
}
}
}
}
}
}
}
},
"/v1/dispatch/sweep": {
"post": {
"tags": [
"dispatch-sweeper"
],
"summary": "Trigger all maintenance sweepers (ops-only recovery)",
"description": "DISPATCH-15: Ops-only recovery endpoint. The primary dispatch engine path is event-driven (NATS consumers → routeSignal). This sweep endpoint is for manual recovery when the event path is disrupted. Not for periodic use in production — set DISPATCH_SWEEPER_ENABLED=0 in prod.",
"operationId": "triggerDispatchSweep",
"x-ops-only": true,
"x-design-principle": "events-not-polling — background work uses NATS events, not polling sweeps",
"parameters": [
{
"name": "limit",
"in": "query",
"schema": {
"type": "integer",
"default": 100,
"maximum": 1000
},
"description": "Max items to process per sweeper"
}
],
"responses": {
"200": {
"description": "Sweep results with counts per sweeper",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"results": {
"type": "object"
}
}
}
}
}
}
}
}
},
"/v1/dispatch/backfill": {
"get": {
"tags": [
"dispatch-backfill"
],
"summary": "List pending backfill entries for migration tracking",
"operationId": "listDispatchBackfill",
"parameters": [
{
"name": "limit",
"in": "query",
"schema": {
"type": "integer",
"default": 50,
"maximum": 500
},
"description": "Maximum entries to return"
}
],
"responses": {
"200": {
"description": "Backfill entries",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"entries": {
"type": "array",
"items": {
"$ref": "#/components/schemas/BackfillEntry"
}
}
}
}
}
}
}
}
},
"post": {
"tags": [
"dispatch-backfill"
],
"summary": "Start a new backfill entry for a legacy handler",
"operationId": "createDispatchBackfill",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"source_handler_id"
],
"properties": {
"source_handler_id": {
"type": "string",
"description": "ID from governance_handler_registry"
},
"strategy": {
"type": "string",
"enum": [
"clone",
"split",
"merge",
"manual",
"skip"
],
"default": "clone",
"description": "Migration strategy"
}
}
}
}
}
},
"responses": {
"200": {
"description": "Backfill entry created or updated",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BackfillEntry"
}
}
}
},
"400": {
"$ref": "#/components/responses/ValidationProblem"
}
}
}
}
},
"components": {
"parameters": {
"InstanceId": {
"name": "instanceId",
"in": "path",
"required": true,
"schema": {
"type": "string",
"example": "dinst:abc123-def456"
},
"description": "Dispatch instance ID (dinst:...)"
},
"RouteId": {
"name": "routeId",
"in": "path",
"required": true,
"schema": {
"type": "string",
"example": "droute:abc123-def456"
},
"description": "Dispatch route ID (droute:...)"
},
"TransportQuery": {
"name": "transport",
"in": "query",
"schema": {
"type": "string",
"enum": [
"nats",
"webhook",
"service",
"hitl",
"fsm",
"inproc"
]
},
"description": "Filter by transport type"
},
"ScopeQuery": {
"name": "scope",
"in": "query",
"schema": {
"type": "string"
},
"description": "Scope filter for dispatch resolution (e.g. sid_root or subscope:dev:tz_compliance)"
}
},
"responses": {
"NotFound": {
"description": "Resource not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
},
"ValidationProblem": {
"description": "Validation error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorEnvelope"
}
}
}
}
},
"schemas": {
"ErrorEnvelope": {
"type": "object",
"properties": {
"error": {
"type": "string"
}
}
},
"DispatchStatusResponse": {
"type": "object",
"properties": {
"service": {
"type": "string"
},
"version": {
"type": "integer"
},
"transports": {
"type": "array",
"items": {
"type": "string"
}
},
"enabled": {
"type": "boolean"
},
"mode": {
"type": "string",
"enum": [
"disabled",
"shadow",
"active"
]
},
"started": {
"type": "boolean"
},
"started_at": {
"type": "string",
"format": "date-time"
},
"flags": {
"type": "object"
},
"counters": {
"type": "object",
"properties": {
"signals_received": {
"type": "integer"
},
"signals_routed": {
"type": "integer"
},
"dispatches_attempted": {
"type": "integer"
},
"dispatches_succeeded": {
"type": "integer"
},
"dispatches_failed": {
"type": "integer"
},
"dispatches_timed_out": {
"type": "integer"
},
"cb_trips": {
"type": "integer"
},
"cb_half_open": {
"type": "integer"
},
"dlq_enqueued": {
"type": "integer"
},
"load_shed": {
"type": "integer"
},
"security_rejected": {
"type": "integer"
},
"security_rejected_scope": {
"type": "integer"
},
"security_rejected_claim": {
"type": "integer"
}
}
},
"in_flight": {
"type": "integer"
},
"max_in_flight": {
"type": "integer"
},
"last_error": {
"type": "string",
"nullable": true
},
"security": {
"type": "object",
"properties": {
"mode": {
"type": "string",
"enum": [
"client_side",
"server_side"
],
"description": "Authorization mode"
},
"server_side_auth": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"override": {
"type": "string",
"description": "When enabled, bypasses client-side gates"
}
}
},
"scope_gate": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"rejections": {
"type": "integer"
}
}
},
"claim_verification": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"rejections": {
"type": "integer"
}
}
}
}
},
"database": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"connected",
"disconnected"
]
},
"last_check_at": {
"type": "string",
"format": "date-time",
"nullable": true
},
"error": {
"type": "string",
"nullable": true
}
}
},
"legacy_systems": {
"type": "object"
},
"timestamp": {
"type": "string",
"format": "date-time"
}
}
},
"DispatchInstance": {
"type": "object",
"properties": {
"instance_id": {
"type": "string",
"example": "dinst:abc123-def456"
},
"handler_id": {
"type": "string",
"example": "hdlr:governance:claim_validation"
},
"display_name": {
"type": "string",
"nullable": true
},
"description": {
"type": "string",
"nullable": true
},
"transport": {
"type": "string",
"enum": [
"nats",
"webhook",
"service",
"hitl",
"fsm",
"inproc"
]
},
"transport_config": {
"type": "object"
},
"scope": {
"type": "string",
"nullable": true
},
"environment": {
"type": "string",
"enum": [
"development",
"staging",
"production",
"shadow"
]
},
"status": {
"type": "string",
"enum": [
"active",
"paused",
"disabled",
"deprecated"
]
},
"is_active": {
"type": "boolean"
},
"concurrency": {
"type": "integer"
},
"max_retries": {
"type": "integer"
},
"timeout_ms": {
"type": "integer"
},
"deliver_group": {
"type": "string",
"nullable": true
},
"use_push_consumer": {
"type": "boolean"
},
"required_claims": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RequiredClaim"
}
},
"governance_hook": {
"type": "boolean"
},
"governance_path_spec_id": {
"type": "string",
"nullable": true
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"metadata": {
"type": "object"
},
"last_heartbeat_at": {
"type": "string",
"format": "date-time",
"nullable": true
},
"created_at": {
"type": "string",
"format": "date-time"
},
"updated_at": {
"type": "string",
"format": "date-time"
}
}
},
"RequiredClaim": {
"type": "object",
"properties": {
"claim_uri": {
"type": "string",
"description": "Claim URI to verify"
},
"category": {
"type": "string",
"enum": [
"caller",
"subject",
"payload"
],
"default": "caller",
"description": "Where to look for the claim in the signal"
},
"optional": {
"type": "boolean",
"default": false,
"description": "If true, missing claim does not block dispatch"
}
}
},
"RegisterInstanceRequest": {
"type": "object",
"required": [
"handler_id",
"transport"
],
"properties": {
"handler_id": {
"type": "string",
"example": "hdlr:governance:claim_validation"
},
"display_name": {
"type": "string"
},
"description": {
"type": "string"
},
"transport": {
"type": "string",
"enum": [
"nats",
"webhook",
"service",
"hitl",
"fsm",
"inproc"
]
},
"transport_config": {
"type": "object",
"description": "Transport-specific config. See s158 schema for per-transport fields."
},
"scope": {
"type": "string",
"description": "Scope restriction for this instance"
},
"concurrency": {
"type": "integer",
"default": 128,
"minimum": 1,
"maximum": 10000
},
"max_retries": {
"type": "integer",
"default": 5,
"minimum": 0,
"maximum": 100
},
"timeout_ms": {
"type": "integer",
"default": 30000,
"minimum": 1000,
"maximum": 3600000
},
"deliver_group": {
"type": "string"
},
"use_push_consumer": {
"type": "boolean",
"default": false
},
"required_claims": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RequiredClaim"
},
"description": "Claims that must be verified before dispatch"
},
"governance_hook": {
"type": "boolean",
"default": false
},
"governance_path_spec_id": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"metadata": {
"type": "object"
}
}
},
"RegisterInstanceResponse": {
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"instance_id": {
"type": "string"
},
"updated": {
"type": "boolean",
"description": "true if existing instance was updated"
}
}
},
"DispatchRoute": {
"type": "object",
"properties": {
"route_id": {
"type": "string",
"example": "droute:abc123-def456"
},
"display_name": {
"type": "string",
"nullable": true
},
"signal_pattern": {
"type": "string",
"example": "claim.created"
},
"target_instance_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Target handler instance IDs"
},
"routing_strategy": {
"type": "string",
"enum": [
"first_match",
"all_match",
"weighted",
"quorum",
"best_effort"
]
},
"routing_weights": {
"type": "object",
"description": "Instance ID → weight mapping"
},
"routing_config": {
"type": "object",
"description": "Quorum config: { min_responses, timeout_ms }"
},
"filter_criteria": {
"type": "string",
"nullable": true
},
"priority": {
"type": "integer"
},
"scope": {
"type": "string",
"nullable": true
},
"is_active": {
"type": "boolean"
},
"description": {
"type": "string",
"nullable": true
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"created_at": {
"type": "string",
"format": "date-time"
},
"updated_at": {
"type": "string",
"format": "date-time"
}
}
},
"RegisterRouteRequest": {
"type": "object",
"required": [
"signal_pattern",
"target_instance_ids"
],
"properties": {
"signal_pattern": {
"type": "string",
"description": "Signal subject pattern (supports NATS wildcards > and *)",
"example": "claim.created"
},
"target_instance_ids": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"description": "Target handler instance IDs"
},
"routing_strategy": {
"type": "string",
"enum": [
"first_match",
"all_match",
"weighted",
"quorum",
"best_effort"
],
"default": "first_match"
},
"routing_weights": {
"type": "object",
"description": "Instance ID → weight for weighted strategy"
},
"routing_config": {
"type": "object",
"description": "Strategy config (e.g. quorum: { min_responses, timeout_ms })"
},
"priority": {
"type": "integer",
"default": 100,
"description": "Lower = higher priority"
},
"scope": {
"type": "string",
"description": "Scope restriction"
},
"filter_criteria": {
"type": "string",
"description": "SurrealQL-like filter expression"
},
"display_name": {
"type": "string"
},
"description": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"RegisterRouteResponse": {
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"route_id": {
"type": "string"
},
"updated": {
"type": "boolean"
}
}
},
"ResolvedRoute": {
"type": "object",
"properties": {
"route_id": {
"type": "string"
},
"signal_pattern": {
"type": "string"
},
"routing_strategy": {
"type": "string"
},
"priority": {
"type": "integer"
},
"routing_weights": {
"type": "object"
},
"routing_config": {
"type": "object"
},
"filter_criteria": {
"type": "string",
"nullable": true
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"instance_id": {
"type": "string"
},
"handler_id": {
"type": "string"
},
"transport": {
"type": "string"
},
"status": {
"type": "string"
},
"is_active": {
"type": "boolean"
}
}
}
},
"instances": {
"type": "array",
"items": {
"$ref": "#/components/schemas/DispatchInstance"
}
}
}
},
"RouteSignalRequest": {
"type": "object",
"required": [
"type"
],
"properties": {
"type": {
"type": "string",
"description": "Signal type / subject",
"example": "claim.created"
},
"id": {
"type": "string",
"description": "Signal ID"
},
"payload": {
"type": "object",
"description": "Signal payload"
},
"ctx": {
"type": "object",
"description": "Signal context including caller information",
"properties": {
"caller": {
"type": "object",
"properties": {
"principal": {
"type": "string"
},
"did": {
"type": "string"
},
"user_id": {
"type": "string"
},
"scope": {
"type": "string"
},
"roles": {
"type": "array",
"items": {
"type": "string"
}
},
"claims": {
"type": "array",
"items": {
"oneOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"claim_uri": {
"type": "string"
},
"uri": {
"type": "string"
}
}
}
]
}
}
}
},
"claims": {
"type": "array",
"items": {
"type": "string"
}
},
"scope": {
"type": "string"
}
}
},
"scope": {
"type": "string",
"description": "Scope for route resolution"
},
"trace_id": {
"type": "string"
},
"span_id": {
"type": "string"
}
}
},
"RouteSignalResponse": {
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"error": {
"type": "string"
},
"route": {
"type": "object"
},
"instance": {
"type": "object"
},
"envelope": {
"type": "object"
},
"audit_id": {
"type": "string"
},
"dispatch_result": {
"type": "object"
},
"cb_state": {
"type": "string"
},
"strategy": {
"type": "string",
"enum": [
"all_match",
"quorum",
"best_effort"
]
},
"instance_count": {
"type": "integer"
},
"success_count": {
"type": "integer"
},
"shadow": {
"type": "boolean"
},
"dry_run": {
"type": "boolean"
},
"matched_route_count": {
"type": "integer"
},
"signal_type": {
"type": "string"
}
}
},
"CircuitBreakerState": {
"type": "object",
"properties": {
"cb_id": {
"type": "string",
"example": "cb:abc123"
},
"instance_id": {
"type": "string"
},
"handler_id": {
"type": "string"
},
"state": {
"type": "string",
"enum": [
"closed",
"open",
"half_open"
]
},
"consecutive_failures": {
"type": "integer"
},
"failure_threshold": {
"type": "integer"
},
"total_failures": {
"type": "integer"
},
"total_successes": {
"type": "integer"
},
"last_failure_at": {
"type": "string",
"format": "date-time",
"nullable": true
},
"last_success_at": {
"type": "string",
"format": "date-time",
"nullable": true
},
"opened_at": {
"type": "string",
"format": "date-time",
"nullable": true
},
"recovery_timeout_ms": {
"type": "integer"
},
"half_open_max_requests": {
"type": "integer"
},
"half_open_successes": {
"type": "integer"
},
"half_open_failures": {
"type": "integer"
},
"created_at": {
"type": "string",
"format": "date-time"
},
"updated_at": {
"type": "string",
"format": "date-time"
}
}
},
"DispatchAuditEntry": {
"type": "object",
"properties": {
"audit_id": {
"type": "string",
"example": "daudit:abc123"
},
"signal_id": {
"type": "string"
},
"signal_type": {
"type": "string"
},
"handler_id": {
"type": "string"
},
"instance_id": {
"type": "string",
"nullable": true
},
"transport": {
"type": "string"
},
"attempt": {
"type": "integer"
},
"max_attempts": {
"type": "integer"
},
"outcome": {
"type": "string",
"enum": [
"dispatched",
"success",
"failed",
"timeout",
"rejected",
"circuit_breaker_open",
"load_shed",
"cancelled"
]
},
"status_code": {
"type": "integer",
"nullable": true
},
"error_message": {
"type": "string",
"nullable": true
},
"started_at": {
"type": "string",
"format": "date-time"
},
"completed_at": {
"type": "string",
"format": "date-time",
"nullable": true
},
"duration_ms": {
"type": "integer",
"nullable": true
},
"trace_id": {
"type": "string",
"nullable": true
},
"scope": {
"type": "string",
"nullable": true
},
"caller": {
"type": "string",
"nullable": true
},
"envelope": {
"type": "object"
},
"response": {
"type": "object",
"nullable": true
},
"metadata": {
"type": "object"
}
}
},
"BackfillEntry": {
"type": "object",
"properties": {
"backfill_id": {
"type": "string",
"example": "bfill:governance_handler_registry"
},
"source_handler_id": {
"type": "string"
},
"source_table": {
"type": "string"
},
"target_instance_id": {
"type": "string",
"nullable": true
},
"target_route_id": {
"type": "string",
"nullable": true
},
"status": {
"type": "string",
"enum": [
"pending",
"in_progress",
"completed",
"skipped",
"failed"
]
},
"error_message": {
"type": "string",
"nullable": true
},
"migration_strategy": {
"type": "string",
"enum": [
"clone",
"split",
"merge",
"manual",
"skip"
]
},
"created_at": {
"type": "string",
"format": "date-time"
},
"updated_at": {
"type": "string",
"format": "date-time"
},
"completed_at": {
"type": "string",
"format": "date-time",
"nullable": true
}
}
}
}
}
}