# Explain API

> Turn a policy decision into a plain-language explanation with POST /v1/explain, and retrieve it later with GET /v1/explain/{violation_id}.

Source: https://docs.atensecurity.com/docs/api-reference/explain

Explanations are deterministic: the enforcer builds them from templates and the
decision fields, with no model in the loop, so the same input always gives the
same text. Use them to tell a user why an action was refused and what to do
next. Both endpoints use your unified tenant API base URL and a runtime key; see
[Authentication](https://docs.atensecurity.com/docs/api-reference#connection-and-authentication).

## Create an explanation

**POST** `/v1/explain`

Builds an explanation from the decision fields you send, stores it under the
`tenant_id` and `violation_id`, and returns it. It does not look the decision
up, so pass the values from the `EnforcementDecision` you received.

### Body parameters

All fields are optional. Unset values fall back to neutral defaults.

- `tenant_id` (string): Your Thoth tenant identifier.
- `violation_id` (string): The `violation_id` from a `BLOCK` decision. Used to store and retrieve the explanation.
- `session_id` (string): Session the decision belongs to.
- `agent_id` (string): Agent that attempted the action.
- `user_id` (string): User who initiated the action.
- `tool_name` (string): Tool the agent tried to call.
- `decision` ("ALLOW" | "STEP_UP" | "MODIFY" | "DEFER" | "BLOCK"): The decision to explain. Defaults to `BLOCK`.
- `decision_reason_code` (string): Reason code from the decision.
- `action_classification` (string): Action class, which selects the wording.
- `risk_score` (number): Risk score from the decision.
- `severity` (string): Severity to report.
- `regulatory_regimes` (string[]): Regimes to name in the regulatory context.
- `matched_rule_ids` (string[]): Rules that matched.
- `matched_control_ids` (string[]): Controls that matched.
- `policy_references` (string[]): Policy references to cite.
- `approved_scope` (string[]): The scope supplied with the original call.
- `step_up_timeout_seconds` (integer): Hold timeout, for step-up explanations.
- `occurred_at` (string (ISO 8601)): When the action happened. Defaults to now.

### Returns

- `what_happened` (string): What the agent tried to do, and when.
- `why_it_was_blocked` (string): Why policy stopped it.
- `what_to_do_next` (string): What the user can do, including how to request an exception.
- `business_impact` (string): The risk the policy guards against.
- `request_url` (string): Link to request a policy exception, when the dashboard is configured.
- `severity` (string): Reported severity.
- `regulatory_context` (string | null): Regulatory framing, when regimes apply.

The response also echoes the identifiers and decision fields you sent
(`violation_id`, `tenant_id`, `session_id`, `agent_id`, `user_id`, `tool_name`,
`decision`, `decision_reason_code`, `action_classification`, `risk_score`,
`regulatory_regimes`, `matched_rule_ids`, `matched_control_ids`,
`policy_references`) and a `timestamp`.

```bash title="Request"
curl -X POST "$THOTH_API_URL/v1/explain" \
  -H "X-API-Key: $THOTH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "acme-corp",
    "violation_id": "550e8400-e29b-41d4-a716-446655440000",
    "session_id": "550e8400-e29b-41d4-a716-446655440001",
    "agent_id": "invoice-processor-v2",
    "user_id": "user-123",
    "tool_name": "submit_payment",
    "decision": "BLOCK",
    "decision_reason_code": "scope_violation",
    "risk_score": 0.91,
    "approved_scope": ["search_docs", "read_invoice"],
    "occurred_at": "2026-03-13T10:00:00Z"
  }'
```

```json title="200 OK"
{
  "violation_id": "550e8400-e29b-41d4-a716-446655440000",
  "tenant_id": "acme-corp",
  "session_id": "550e8400-e29b-41d4-a716-446655440001",
  "agent_id": "invoice-processor-v2",
  "user_id": "user-123",
  "tool_name": "submit_payment",
  "what_happened": "Your agent `invoice-processor-v2` attempted to execute a command using `submit_payment` at 2026-03-13 10:00:00 UTC.",
  "why_it_was_blocked": "This execute action violated a configured policy guardrail.",
  "what_to_do_next": "If this action is required for your workflow, submit a policy exception request. Reference `550e8400-e29b-41d4-a716-446655440000`. Target response time is 24 hours.",
  "business_impact": "This action could execute privileged behavior outside approved automation boundaries.",
  "request_url": "https://app.atensecurity.com/thoth/admin/policy-exceptions?violation_id=550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2026-03-13T10:00:01Z",
  "severity": "medium",
  "regulatory_context": null,
  "decision": "BLOCK",
  "decision_reason_code": "scope_violation",
  "action_classification": null,
  "risk_score": 0.91,
  "regulatory_regimes": [],
  "matched_rule_ids": [],
  "matched_control_ids": [],
  "policy_references": []
}
```

Generated by the enforcer's explainer from the request above. Wording varies
with `action_classification`, `decision` and your organization's settings.

## Retrieve an explanation

**GET** `/v1/explain/{violation_id}`

Returns a stored explanation. Returns `404` if none exists for that violation
and tenant.

### Path parameters

- `violation_id` (string, required): The violation the explanation was created for.

### Query parameters

- `tenant_id` (string, required): Your Thoth tenant identifier.

### Returns

The same explanation object returned when it was created.

```bash title="Request"
curl "$THOTH_API_URL/v1/explain/550e8400-e29b-41d4-a716-446655440000?tenant_id=acme-corp" \
  -H "X-API-Key: $THOTH_API_KEY"
```

```json title="404 Not Found"
{
  "detail": "human explanation not found"
}
```
