Skip to content

Escalations

An escalation is created when evaluate() returns status == "escalated". It represents an agent output that requires human review before being served to the user.


Escalation Lifecycle

stateDiagram-v2
    [*] --> pending : evaluate() → escalated
    pending --> approved : resolve(decision="approved")
    pending --> rejected : resolve(decision="rejected")
    pending --> corrected : resolve(decision="corrected")
    approved --> [*]
    rejected --> [*]
    corrected --> [*]

Who may resolve an escalation

An escalation carries a resolver policy, set when it was created:

resolver Who may close it From an SDK?
"self" (default) Anyone holding a valid API key for the account Yes — resolve()
"role:<name>" Only a person holding that role, proven by a reviewer assertion No — Console or resume hook

A reviewer assertion is a signed statement that the person resolving the decision holds the required role. Only the Coalex Console issues one; an SDK cannot, because an API key proves which workload is calling rather than which person approved. Calling resolve() on a role-gated escalation therefore returns a 403 explaining this — see resolve() · Role-gated decisions.


Escalation Data

Each escalation contains:

Field Description
escalation_id Unique identifier (use this with resolve())
agent_id The agent that produced the output
request_id Links the escalation to its trace
input The agent's input data
output The agent's output that needs review
risk_score The computed risk score (0.0 – 1.0)
metrics Declared metric functions (computed at resolve-time)
status pending, approved, rejected, or corrected
created_at When the escalation was created

Routing Escalations

Coalex creates escalations but does not prescribe how you route them to reviewers. Common patterns:

Webhook

Configure a webhook URL in the dashboard. Coalex sends a POST request when an escalation is created:

{
    "escalation_id": "esc-001",
    "agent_id": "claims-bot",
    "risk_score": 0.73,
    "output": {"diagnosis": "..."},
    "created_at": "2025-01-15T10:30:00Z"
}

Polling

Query pending escalations from your review UI:

curl "https://traces.coalex.ai/api/v1/escalations?status=pending" \
  -H "Authorization: Bearer $COALEX_API_KEY"

Dashboard

The admin dashboard lists every escalation and opens one to show what was asked, what the agent answered, who decided and what the reviewer corrected. It is read-only: a person approves, rejects or corrects in the Coalex Console, where their identity and approval role are verified, or an SDK resolves it with resolve().

To get to the Console, open Settings in the dashboard: the Operator Console section, where an admin switches the Console on for the organisation, has an Open the Console link that opens the Console in a new tab. It leads to the Console of the environment the dashboard is served from: https://console.coalex.ai from dashboard.coalex.ai, and https://console-staging.coalex.ai from the staging dashboard. Every member sees it, whether the Console is on or off. On any other address (local development, a preview) the link is not shown.

Each escalation has an address: the dashboard's Escalations page with ?escalation=<escalation id> opens its review. The title in each row is that link, so a row opens in a new tab and a copied link opens the same review for anyone who belongs to the organisation. An id that no escalation of the organisation has reads "Escalation not found".

The page searches and sorts on the server, across every escalation and not only the 50 on screen. The search field matches text inside the reason the agent gave, the reviewer's reason, the tool, the request id and the escalation id, ignoring case, and the totals above the table count the matches. The sort offers newest first (the default), oldest first, and deadline, which lists the pending escalations by the soonest deadline (those with none last) and the rest newest first. The search and the sort are in the address as ?q= and ?order= (created_asc or expires_asc), so a link keeps them; the same two parameters, q and order, are on GET /v1/escalations.


Resolving Escalations

Once a human reviewer has assessed the output, submit their decision with resolve():

result = coalex.resolve(
    escalation_id="esc-001",
    decision="approved",
    reviewer={"name": "Dr. Smith", "email": "dr.smith@hospital.org"},
    reason="Output is clinically accurate.",
)

See Resolve for decision types and the full API.


Audit Trail

All escalations — including auto-approved evaluations — are stored permanently for compliance purposes. This gives you:

  • Complete history of every agent decision
  • Reviewer identity and reasoning for each resolution
  • Quality metrics for corrected outputs
  • Data for EU AI Act, ISO 42001, and SOC 2 reporting