---
title: "Explain a governance decision"
method: GET
path: "/api/v1/decisions/{id}/explain"
tags: ["Decisions & Overrides"]
---

# Explain a governance decision

`GET /api/v1/decisions/{id}/explain`

## Request

- Base URL: `https://orchestrator.getaxonflow.com`
- URL: `https://orchestrator.getaxonflow.com/api/v1/decisions/{id}/explain`
- Auth: none declared. Required headers: `X-Tenant-ID`
- TypeScript: <https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api/docs/api/v1/decisions/:id/explain/get.ts> (a function with typed inputs; its response is checked against the schema)

Returns the explanation for a decision id (ADR-043): matched
policies, verdict and reason, risk level, override availability (and
any existing override id), the caller's 24h hit count for the first
matched policy, and policy version drift (version at decision time vs
latest). Tenant-scoped via `X-Tenant-ID`; a decision belonging to
another tenant returns 404 (not 403) so the endpoint is not an
existence oracle.

## Path parameters

- `id` string, required

## Headers

- `X-Tenant-ID` string, required
- `X-User-Email` string

## Response `200`

Decision explanation

- DecisionExplanation: ADR-043 decision explanation
  - `decision_id` string
  - `timestamp` string, date-time
  - `policy_matches` object[]: Always an array (`[]` when empty)
    - `policy_id` string
    - `policy_name` string
    - `action` string
    - `risk_level` string
    - `allow_override` boolean
    - `policy_description` string
  - `matched_rules` object[]: Per-rule match detail; omitted when unavailable
    - `policy_id` string
    - `rule_id` string
    - `rule_text` string
    - `matched_on` string
  - `decision` string: Verdict echoed verbatim from the audit row's policy_decision column (not normalized), so historical spellings can appear
  - `reason` string
  - `risk_level` string
  - `override_available` boolean: Always false from v11.0.0: session overrides are retired (PRD v11 §1.5, #4252), so explain offers none.
  - `override_existing_id` string: Not set from v11.0.0: explain offers no override, and the step gate no longer reads one (#4252).
  - `historical_hit_count_session` integer: Rolling-24h hit count for the first matched policy by this user
  - `policy_source_link` string
  - `tool_signature` string
  - `policy_version_at_decision` integer: Policy version recorded at decision time; omitted when unknown
  - `latest_policy_version` integer: Current head version of the first matched (static) policy
  - `context` object
  - `context_truncated` boolean

## Other responses

- `400`: Missing decision id
- `401`: Missing X-Tenant-ID header
- `403`: Not authorized to explain this decision (defensive tenant mismatch)
- `404`: Decision not found, past the retention window, belongs to another tenant, or belongs to another user while the caller is scoped to `own-rows`. All four return the same body (non-oracle); `X-Axonflow-Read-Scope` is what separates a scoping outcome from a genuinely missing decision — the "explain fails on a decision the platform produced seconds ago" symptom.
- `500`: Internal error

---

[API](https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api.md) - [All operations](https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api/llms.txt) - [OpenAPI document](https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api/revisions/f408ef3ec9dd?raw)
