GET /api/operator/v1/routing — the routing decision-log BOARD (explainability), on the #358 `{items,total,facets}` contract. Returns a server-paginated, filtered + sorted page of `backlog_routing_decisions` (each enriched with the chosen profile name, candidate count, item state + a reason summary) plus the pager total and optional facet chips. `?workspace_id=` filters to one workspace; absent reads cross-tenant. `q`/`sort`/`dir`/`limit`/`offset`/`include_facets`/`since`/`until` drive the board.
const url = 'https://example.com/api/operator/v1/routing';const options = {method: 'GET'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url https://example.com/api/operator/v1/routingParameters
Section titled “Parameters”Query Parameters
Section titled “Query Parameters”Free-text search over the owning item’s title.
routing_source filter (classify|escalate|fallback|manual).
backlog_items.state filter (the routing outcome).
Whitelisted sort key: created_at|item_title|routing_source|outcome.
Sort direction: asc | desc (default desc).
Include the facet chips (by_source/by_state + total) in the response.
Range start (inclusive) on the decision’s created_at.
Range end (inclusive) on the decision’s created_at.
Responses
Section titled “Responses”One page of the routing board: the filtered + paged rows, the pager total, and
(optionally) the facet chips — the #358 {items, total, facets} envelope.
object
The workspace-wide facet chips for the routing board.
object
Counts per routing_source (classify|escalate|fallback|manual).
One filter-chip value and its count. Counts are workspace-wide (independent of the active filter — the chip totals do not shift as the user filters).
object
How many rows fall in this bucket.
The bucket value (e.g. a scope, a role, a framework).
Counts per the owning item’s state (the routing outcome).
One filter-chip value and its count. Counts are workspace-wide (independent of the active filter — the chip totals do not shift as the user filters).
object
How many rows fall in this bucket.
The bucket value (e.g. a scope, a role, a framework).
All routing decisions in scope (the pager-header total, filter-independent).
One routing decision-log entry (reader of backlog_routing_decisions, mig 212).
decision_factors is the verbatim JSON the classifier / escalation wrote — the
operator-facing explainability of WHY the item dispatched / which model was
chosen / why it was paused. routing_source is the path that produced it
(classify|escalate|fallback|manual).
object
#626 Operations — the number of candidate profiles the router chose among
(the multi-agent work-lane size), or None when no explicit candidate set was
recorded (single-agent path). Honest-NULL — never a fabricated 1.
#626 Operations — the name of the profile the router bound to the item
(agent_profiles.name), or None for a single-agent fallback / un-routed item.
object
#626 Operations — the owning item’s CURRENT lifecycle state (the routing
“outcome” the operator console renders), or None if unreadable.
#626 Operations — a short human-readable summary of WHY the item routed as it
did, derived from decision_factors (the escalation reason, or a
classify-path summary of severity + routed effort). None when nothing
summarizable was recorded.
Examplegenerated
{ "facets": { "by_source": [ { "count": 1, "value": "example" } ], "by_state": [ { "count": 1, "value": "example" } ], "total": 1 }, "items": [ { "candidate_count": 1, "chosen_profile_name": "example", "created_at": "2026-04-15T12:00:00Z", "decision_factors": { "additionalProperty": "example" }, "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "item_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "item_state": "example", "item_title": "example", "project_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "reason_summary": "example", "routing_source": "example" } ], "total": 1}