Skip to content

Decisions & Escalations

Decisions & Escalations

Query the immutable decision log and manage the human review queue.

GET /v1/enforce/decisions List decisions with pagination and filtering by action_type or decision. GET /v1/enforce/decisions/{decision_id} Get a single decision with full details. GET /v1/enforce/escalations List pending escalations (actions awaiting human review). POST /v1/enforce/escalations/{id}/resolve Approve or reject an escalated action. Body: {"resolution": "approved"|"rejected"}. Immediately unblocks any SDK call polling this escalation. GET /v1/enforce/escalations/{id}/status Poll the resolution status of a pending escalation. Returns {"ok": true, "status": "pending"|"approved"|"rejected"}. Called automatically by the SDK's wait_for_escalation() method every escalation_poll_interval seconds.

Query Decisions

# Get all blocked decisions
blocked = requests.get(
    "https://www.xybern.com/api/v1/enforce/decisions",
    headers={"X-API-Key": API_KEY},
    params={"decision": "block", "per_page": 20}
).json()

for d in blocked["decisions"]:
    print(f"{d['decision_id']}: {d['action_type']} → {d['decision']} "
          f"(trust: {d['trust_score']}, {d['latency_ms']}ms)")

# Resolve an escalation
requests.post(
    f"https://www.xybern.com/api/v1/enforce/escalations/{esc_id}/resolve",
    headers={"X-API-Key": API_KEY},
    json={"resolution": "approved", "reason": "Reviewed and safe to proceed"}
)

Stats Endpoint

GET /v1/enforce/stats Aggregate enforcement statistics: decisions, block rate, avg latency, agent counts.

Plain-language explanations

Every decision carries two accounts of why it was made. The technical reasoning is what the engine recorded (metadata.amount > 50000 (actual 143000) (rule 'Mandate · …')); it is what the receipt and the offline verifier use, and it never changes. The explanation is the same facts written for a person:

The agent tried to settle a motor claim for SAR 143,000 based on a workshop estimate that matched the damage report. This was held for a person because the amount exceeds the SAR 50,000 limit for automatic settlement, so a human claims approver must review it.

The explanation is produced in two layers. A deterministic rewrite runs on every decision (instant, no model, works offline) and names the mandate by its full outcome text. When a model is configured, the record dialog asks for a polished version, which is cached on the decision so each decision costs one model call at most. The dialog shows the explanation with the technical reasoning under Technical detail; escalation cards show the agent's own stated reason (sent by the SDK as agent_reasoning) together with the reason the action was held.

GET /api/sentinel/enforcement/decisions/<decision_id>/explain?workspace_id=...&lang=en|ar

Returns text, source (model or rules) and model. lang=ar produces the explanation in Arabic. Settings: XYBERN_EXPLAIN_MODEL (default deepseek-chat on Cloud; any model the deployment's provider serves), XYBERN_EXPLAIN=0 to disable the model layer and keep the deterministic text. The explanation is descriptive only: it is generated from the recorded facts and can never alter the decision, the receipt or the vault entry.