Web app
Approvals and escalation
Two queues that need a person: the Approvals tab holds single tool calls waiting for sign-off; the Escalation tab holds whole conversations that should have gone to a human.
In the web app Approvals
When to use this
- An agent's call is held and the agent is waiting on your answer.
- A customer asked for a person, and you need to see whether anyone picked it up.
- To tune what counts as "should have escalated".
Approvals tab
/app/approvals, filtered by ?status=: pending (the default), approved, denied, expired, used (approved, and the agent's retry has run), each with its count. From GET /api/approvals?status=….
A call lands here when the capability grant behind it was made with --requires-approval, or when a rule escalates instead of blocking: most often an irreversible tool called with arguments that came from something untrusted, or an irreversible action by a high-risk agent. Each row shows:
- agent and tool. A held message rather than a tool call shows as
message:input(ormessage:output,message:agent_message) with the message, personal data masked, as its arguments. - reason: every rule that fired, one per line. This is what you are deciding against.
- arguments: the exact values the call would run with
- requested and expires (a countdown)
- Approve with an optional rationale, or Deny. Deny means the call does not run, the same outcome as a block.
agent tool reason
Payments Operations Agent payments.transfer EU AI Act Art. 14 — irreversible action by a high-risk system requires human oversight.
Irreversible tool invoked with arguments originating in untrusted content (retrieved
document, tool result or sub-agent output). Human approval required.
The granting capability requires human approval for this action.
arguments: amount 250 · currency USD · to acct_attacker_991The buttons call POST /api/approvals/{id}/approve and /deny with {"rationale": "…"}. Deciding needs owner, admin or security, and each decision is written to the audit chain as approval.approved or approval.denied with the rationale.
An unanswered request is denied when it expires, and the call stays blocked: it fails closed. Approving does not run anything by itself: the agent retries the same call with the approval_id, and that one retry runs. The SDK waiting on an approval polls GET /api/approvals/{id} with the agent's own key. From the CLI: agentfox permit approvals list, then approve ID or deny ID. Over HTTP:
curl -s -X POST http://127.0.0.1:8080/api/approvals/apr_01m4699s09007f7x9p/deny \
-H "Authorization: Bearer $AGENTFOX_API_TOKEN" -H 'Content-Type: application/json' \
-d '{"rationale":"account closure needs a ticket"}'{"id":"apr_01m4699s09007f7x9p","status":"denied","resolver":"admin@example.com"}An empty pending queue says "Nothing is waiting on you", links to what has already been answered, and to the agents and rules that would put something here.
Escalation tab
In the web app Approvals → Escalation
/app/approvals?tab=escalation, filterable by agent. Built from GET /api/escalation/report, /missed, /handoffs and /policy. The strip at the top: missed-escalation rate (red above 5%), conversations that qualified for a hand-off, hand-offs past their SLA, hand-offs missing context, and false resolutions.
Hand-off queue
Each row is a conversation an agent handed to a person:
- conversation: the summary, its session id, a
sample datatag for seeded ones, and a link to the trace if there is one. The summary opens the transcript. - status:
pending,acknowledged,breached(past its SLA) orresolved; aretroactivetag when it was raised by the after-the-fact scan. - owner: the role it is routed to
- context: how complete the hand-off is: the original request, a summary, what was tried, why it was blocked, and a customer reference. Anything missing is listed. A hand-off a person has to re-interview the customer for has failed even though it happened.
- due and reason
- acknowledge on pending rows (
POST /api/escalation/handoffs/{id}/acknowledge). A hand-off past its SLA becomesbreachedand appears in the attention queue as high.
Missed escalations
Conversations that met an escalation condition and never got a person, found after the fact by replaying them against the policy. At runtime there is nothing to see: the failure is the absence of an event. Each row has the turn count, the turn it qualified at, and the first two triggers.
Conversation transcript
/app/escalation/conversations/<session id> (GET /api/escalation/conversations/{id}) replays every turn against the policy. Cards say whether it qualified, whether it actually escalated, the turn count, and depth against the turn-depth limit. A red note appears when it qualified and no hand-off was raised. The transcript shows user and agent text per turn, tags for "claims resolved" and "escalated here", a trace link, and the triggers on each turn.
# user agent triggers
2 I want a refund reversed and I want to speak to a manager. I'm connecting you with a member of high explicit_request
our team who can take this further. the user asked for a human
escalated hereEscalation policy
Collapsed at the foot of the tab: Owner role (default support), SLA (minutes) (default 60), Mode (observe or enforce), and Conditions as JSON: explicit_request, repeated_failure, repeated_abstention, turn_depth, sentiment_below, regulated_topics, confidence_below. Fields you leave out fall back to the platform default. Saving sends PUT /api/escalation/policy. This edits the org default; per-agent policies exist in the API (?agent=) and the CLI, not here.
{
"explicit_request": true,
"repeated_failure": 2,
"repeated_abstention": 2,
"turn_depth": 8,
"sentiment_below": -0.6,
"regulated_topics": ["legal", "medical", "financial_advice", "complaint", "discrimination"],
"confidence_below": 0.35
}Common tasks
| You want to | Run |
|---|---|
| Scan recent conversations for missed escalations | agentfox report escalations --hours 24 |
| Raise hand-offs for the ones it finds | agentfox report escalations --hours 24 --apply |
| Set a per-agent escalation policy | agentfox declare escalation --agent support-triage --turn-depth 6 --sla-minutes 30 |
| Require sign-off for a tool | agentfox permit grant payments-ops payments.transfer --requires-approval --yes |
1 conversation(s) · 1 qualified for escalation · 0 missed (0.0%, target < 5%)What can go wrong
- Approve returns an error banner. Deciding needs owner, admin or security.
- An approval expired before anyone saw it. It was denied and the call blocked. Shorten the path to whoever decides, or narrow the grant so fewer calls need sign-off.
- "conditions must be valid JSON" on Save escalation policy: the JSON did not parse; nothing was saved.
- Missed escalations is empty. Either escalation works, or no conversation turns were recorded for the window.
Limits
- No CLI command to decide an approval; use the page or the HTTP routes.
- No reassignment of hand-offs and no resolve button; acknowledge is the only action.
- Escalation conditions are signals, not guarantees, which is why the policy ships in observe.