> ## Documentation Index
> Fetch the complete documentation index at: https://docs.visiqlabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API Reference

> Complete REST API reference for the retrieval-governance endpoints.

Retrieval-governance endpoints are mounted under `/recall/*`, with the versioned audit-log read at `/v1/recall/audit-log`. The base URL is `https://api.visiqlabs.com`.

Action governance and retrieval governance share a single rule engine — each rule declares which facets it applies to. The endpoints on this page are the retrieval facet: they govern what your agents can **see** (retrieved documents, tool results, rendered prompts), with decisions `allow`, `deny`, `redact`, and `escalate`.

## Authentication

All endpoints require a Bearer credential: `Authorization: Bearer <key>`. Requests without a valid credential receive `401 Unauthorized`.

Two credential audiences exist:

* **Harness keys** — the operational credential your SDK or harness runs with. Either a `vq_prod_...` / `vq_test_...` key minted in the dashboard under **Settings → Harness Keys**, or the `allow_...` key returned once by `POST /allow/agents`. On the retrieval surface the operational allowlist covers exactly `POST /recall/evaluate` and `GET /recall/rules/bundle` — calling any other endpoint on this page with a harness key returns `403 {"error": "harness_key_not_permitted"}`.
* **Management keys** — general automation credentials governed by explicit permission grants. They can call every endpoint on this page. Management keys are **launching soon**: they are visible in the dashboard under **Settings → API Keys**, but creating one is not yet enabled. Until then, drive the management endpoints from the dashboard, which authenticates with your session.

Scoped keys are additionally checked against their granted scopes. Evaluation requires `rules:evaluate` (or the legacy `recall:write`); reads require `rules:read` (or the legacy `recall:read`); rule mutations require `rules:write` (or the legacy `recall:write`); `full_access` satisfies everything. A key without the required scope receives `403 {"error": "insufficient_scope"}` listing the required and granted scopes.

### Rate limiting

Every API-key request passes a per-key sliding-window rate limit (default 600 requests per 60 seconds). Responses carry `X-RateLimit-Limit` and `X-RateLimit-Remaining` headers; exceeding the window returns `429` with a `Retry-After` header and body `{"error": "rate_limited", "detail": "API key rate limit exceeded.", "retryAfter": <seconds>}`.

### List responses

Every list endpoint on this page returns the same envelope:

```json theme={null}
{
  "data": [ ... ],
  "total": 128,
  "page": 1,
  "pageSize": 50
}
```

Pagination is controlled by `page` (default `1`) and `limit` (default `50`, max `100`) query parameters.

***

## Evaluation

The endpoints your SDK or harness calls at runtime. Both accept a harness key. The SDK evaluates most retrievals locally from the cached rule bundle — the server and the SDK run the same policy interpreter over the same rule source, so local and remote decisions agree by construction.

***

### POST /recall/evaluate

Evaluate a retrieval against your suppression rules and return a decision.

**Scope:** `rules:evaluate` or `recall:write`

**Request body:**

```json theme={null}
{
  "agent_id": "research-bot",
  "operation": "retrieve",
  "resource_type": "document",
  "resource_metadata": {
    "classification": "confidential",
    "department": "finance"
  },
  "surface": "public_channel",
  "query": "quarterly revenue figures"
}
```

| Field               | Type                                         | Required | Description                                                                                   |
| ------------------- | -------------------------------------------- | -------- | --------------------------------------------------------------------------------------------- |
| `agent_id`          | `string`                                     | Yes      | Identifier of the agent making the retrieval (1–255 chars)                                    |
| `operation`         | `retrieve` \| `tool_call` \| `prompt_render` | Yes      | Which hook produced the content: a retriever, a tool result, or prompt assembly               |
| `resource_type`     | `string`                                     | No       | Resource type being accessed. Default `document` (1–255 chars)                                |
| `resource_metadata` | `object`                                     | No       | Metadata about the resource (classification, department, …). Default `{}`                     |
| `trust_tier`        | `string`                                     | No       | Fallback trust tier — see the note below (max 50 chars)                                       |
| `surface`           | `string`                                     | No       | Delivery surface for the output, matched exactly against surface-scoped rules (max 100 chars) |
| `query`             | `string`                                     | No       | The retrieval query string (max 2000 chars)                                                   |
| `telemetry`         | `object`                                     | No       | Opaque client telemetry, attached to the decision's signed record envelope                    |

<Note>
  Agent attributes are **server-authoritative**. The platform looks up `agent_id` in the agent registry and hydrates its assigned trust tier, categories, and business function into rule input (`input.agent.*`). An assigned trust tier **overrides** the request's `trust_tier` — the body value is only a fallback for agents with no assigned tier, so varying it in a request cannot spoof a tier-gated rule. Unlike the action facet's evaluate endpoint, this endpoint does not auto-provision unknown agents.
</Note>

<Note>
  For an agent in **monitor mode**, a non-allow decision is normalized to `allow` before it is recorded and returned, with `reason_code: "MONITOR_MODE"` and the engine's original reason preserved behind a `Monitor: ` prefix.
</Note>

**Response:**

```json theme={null}
{
  "decision_id": "550e8400-e29b-41d4-a716-446655440000",
  "decision": "redact",
  "reason_code": "POLICY_DENY",
  "reason": "Matched rule: Mask account numbers for support agents",
  "redaction_rules": [
    { "field": "ssn", "mode": "full", "replacement": "[REDACTED]" },
    { "field": "account_number", "mode": "partial", "keepLast": 4, "maskChar": "*" }
  ]
}
```

| Field             | Type                                        | Description                                                                                |
| ----------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `decision_id`     | `string` (UUID)                             | Unique ID for this decision — used for audit and the retained-original reveal              |
| `decision`        | `allow` \| `deny` \| `redact` \| `escalate` | The suppression outcome                                                                    |
| `reason_code`     | `string`                                    | Machine-readable reason — see the table below                                              |
| `reason`          | `string`                                    | Human-readable explanation                                                                 |
| `redaction_rules` | `array`                                     | Present on a `redact` decision (and on an `escalate` whose fallback is `mask`) — see below |
| `hitl_fallback`   | `deny` \| `mask`                            | Present on an `escalate` decision — see below                                              |

**Decision semantics:**

* `allow` — the content passes to the agent unchanged.
* `deny` — the content is suppressed; the agent never sees it.
* `redact` — the content proceeds with the accompanying `redaction_rules` applied.
* `escalate` — hold the content for human review; if no human approves in time, fall back per `hitl_fallback`.

**Redaction directives.** Each entry in `redaction_rules` may include `field`, `pattern`, `replacement`, `mode` (`full` | `partial` | `email` | `custom`), `keepFirst`, `keepLast`, `maskChar`, and `keepPattern`. `full` replaces the whole match (the default); `partial` keeps the first/last characters; `email` keeps the first character of the local part plus the domain; `custom` keeps every `keepPattern` match and masks the rest. A `redact` decision from a rule whose directives resolve to nothing passes content through unchanged — unless the rule is flagged fail-closed, in which case the decision is downgraded to `deny` server-side.

**The `escalate` decision** tells your harness to hold the content for human review — this endpoint does not itself notify approvers; register the approval through the human-in-the-loop flow (see [Human-in-the-Loop](/rules/action/hitl)). `hitl_fallback` tells the harness what to do when no human responds in time: `deny` (the default, fail-closed) or `mask` (proceed with the accompanying `redaction_rules` applied).

**Reason codes:**

| Code                  | Meaning                                                                                                 |
| --------------------- | ------------------------------------------------------------------------------------------------------- |
| `POLICY_ALLOW`        | A matched rule's conditions evaluated to allow                                                          |
| `POLICY_DENY`         | A matched rule's conditions produced a non-allow outcome (deny, redact, or escalate)                    |
| `TIER_MISMATCH`       | The agent's trust tier is below the minimum required by the rule or resource                            |
| `PRINCIPAL_EXCLUDED`  | The agent is on the rule's (or resource's) principal exclusion list                                     |
| `SURFACE_RESTRICTION` | A surface-scoped rule applied to this delivery surface and did not allow it                             |
| `AUDIENCE_EXPANSION`  | Reserved — defined in the decision vocabulary but not currently emitted by the engine                   |
| `DEFAULT_DENY`        | No rule matched — the retrieval facet fails closed                                                      |
| `EMERGENCY_BYPASS`    | An active [emergency bypass](/rules/retrieval/emergency-bypass) on the matched rule allowed the request |
| `MONITOR_MODE`        | The agent runs in monitor mode — a non-allow decision was recorded but returned as `allow`              |

<Note>
  The response carries no receipt ID — tamper evidence is asynchronous. For organizations with artifact signing enabled, each decision emits a record envelope that receives an Ed25519 receipt off the request path, is Merkle-batched under a KMS-signed root with an RFC 3161 timestamp, and lands in a hash-chained checkpoint log. Verify a decision's record envelope server-side via `GET /record/envelopes/:id/verify` (`:id` is the record envelope's UUID, not the `decision_id` returned here).
</Note>

<Note>
  **Minimum-necessary at rest:** the decision is evaluated against the real values, but the persisted copies of `resource_metadata` and `query` are masked — first by the matched rule's redaction directives, then by an always-on value-shape redaction floor. The raw original is discarded by default; organizations can opt in to retaining it encrypted, revealable only via the [retained-original endpoint](#get-recalldecisionsidunmask).
</Note>

**Status codes:** `200 OK`, `400 Bad Request`, `401 Unauthorized`, `403 Forbidden` (insufficient scope), `429 Too Many Requests`, `500 Internal Server Error`

**Example:**

```bash theme={null}
curl -X POST https://api.visiqlabs.com/recall/evaluate \
  -H "Authorization: Bearer $VISIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "research-bot",
    "operation": "retrieve",
    "resource_type": "document",
    "resource_metadata": { "classification": "confidential" }
  }'
```

***

### GET /recall/rules/bundle

Fetch your enabled retrieval rules as one bundle for local evaluation. Clients can cache this bundle and revalidate it with `If-None-Match`, so retrieval decisions on the hot path never wait on the network. (The VisIQ SDK consumes the unified `GET /rules/bundle`, which carries both facets.)

**Request headers:**

| Header          | Description                                                                                     |
| --------------- | ----------------------------------------------------------------------------------------------- |
| `If-None-Match` | ETag from a previous response. The server returns `304 Not Modified` if the bundle is unchanged |

**Response:**

```json theme={null}
{
  "version": "a3b4c5d6e7f8...",
  "rules": [
    {
      "id": "rule-uuid",
      "name": "Deny tier3 from confidential",
      "rego_source": "package recall.rules.tier3_guard\n\ndefault decision = \"deny\"\n...",
      "trust_tier": "tier3",
      "surface": null,
      "principal_exclusions": [],
      "priority": 50
    }
  ]
}
```

| Field     | Description                                                                                                                                                                                               |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `version` | SHA-256 hash of the serialized rules array — changes whenever any included rule changes                                                                                                                   |
| `rules`   | Enabled retrieval rules, sorted by priority descending. Each carries its full `rego_source` for local evaluation. Rules without policy source are excluded from the bundle (they are not yet enforceable) |

**Response headers:**

| Header          | Value                                       |
| --------------- | ------------------------------------------- |
| `ETag`          | `"<sha256-version>"` (quoted, per RFC 7232) |
| `Cache-Control` | `private, max-age=60`                       |

**Status codes:** `200 OK`, `304 Not Modified`, `401 Unauthorized`, `403 Forbidden` (insufficient scope), `500 Internal Server Error`

***

## Rules

Rule management. These are management endpoints — a harness key receives `403 harness_key_not_permitted` here. The recommended authoring path is the dashboard's rule editor (natural-language compile, visual condition builder, and a Simulate panel that checks a new rule against your recent traffic — rules projected to inhibit more than 5% of it are blocked). These endpoints are the programmatic equivalent.

***

### GET /recall/rules

List retrieval rules, sorted by priority descending — the same order the engine evaluates them in (first match wins).

**Query parameters:** `page` (default `1`), `limit` (default `50`, max `100`)

**Response:**

```json theme={null}
{
  "data": [
    {
      "id": "rule-uuid",
      "name": "Deny tier3 from confidential",
      "description": "Prevent low-trust agents from accessing sensitive data",
      "natural_language": "Deny tier3 agents from internal, confidential, or restricted data",
      "priority": 50,
      "enabled": true,
      "trust_tier": "tier3",
      "surface": null,
      "principal_exclusions": null,
      "bypass_active": false,
      "bypass_reason": null,
      "bypass_activated_by": null,
      "bypass_activated_at": null,
      "bypass_expires_at": null,
      "created_at": "2026-07-03T10:00:00Z",
      "updated_at": "2026-07-03T10:00:00Z"
    }
  ],
  "total": 8,
  "page": 1,
  "pageSize": 50
}
```

The list omits `rego_source`; fetch a single rule to read the policy source.

**Status codes:** `200 OK`, `400 Bad Request`, `401 Unauthorized`, `500 Internal Server Error`

***

### POST /recall/rules

Create a retrieval rule.

**Request body:**

```json theme={null}
{
  "name": "Deny tier3 from confidential",
  "description": "Prevent low-trust agents from accessing sensitive data",
  "rego_source": "package recall.rules.tier3_guard\n\ndefault decision = \"deny\"\n\ndecision = \"deny\" if {\n  input.resource_metadata.classification in [\"internal\", \"confidential\", \"restricted\"]\n}",
  "natural_language": "Deny tier3 agents from accessing internal, confidential, or restricted data",
  "priority": 50,
  "enabled": true,
  "trust_tier": "tier3"
}
```

| Field                  | Type       | Required | Description                                                                                                                                               |
| ---------------------- | ---------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`                 | `string`   | Yes      | Rule name (max 255 chars)                                                                                                                                 |
| `description`          | `string`   | No       | Description (max 1000 chars)                                                                                                                              |
| `rego_source`          | `string`   | Yes      | Policy source (see note below)                                                                                                                            |
| `natural_language`     | `string`   | No       | The plain-English intent the policy was compiled from (max 2000 chars)                                                                                    |
| `priority`             | `number`   | No       | Evaluation priority — higher runs first. Default `0`                                                                                                      |
| `enabled`              | `boolean`  | No       | Whether the rule is active. Default `true`                                                                                                                |
| `trust_tier`           | `string`   | No       | Minimum trust tier this rule requires (`tier1` is highest trust, `tier3` lowest). Agents below the minimum are denied with `TIER_MISMATCH` (max 50 chars) |
| `surface`              | `string`   | No       | Exact-match surface restriction — the rule applies only to retrievals delivered to this surface (max 100 chars)                                           |
| `principal_exclusions` | `string[]` | No       | Agent IDs this rule excludes — an excluded agent is denied with `PRINCIPAL_EXCLUDED`                                                                      |

<Note>
  `rego_source` holds the rule's policy source in the platform's Rego-subset condition language (equality, inequality, set membership, `startswith`/`endswith`/`contains`, regex, counts, and numeric comparisons). The decision (`allow`, `deny`, `redact`, `escalate`) is derived from the policy body. Prefer `POST /recall/rules/compile` — the compiler drafts and validates the policy against the live evaluation engine.
</Note>

**Response (201):** the created rule object, including `rego_source`.

**Status codes:** `201 Created`, `400 Bad Request`, `401 Unauthorized`, `500 Internal Server Error`

***

### POST /recall/rules/compile

Compile a natural-language description into a retrieval rule. The compiler reads your existing rules for context, drafts the policy source, and validates it against the live evaluation engine.

**Rate limit:** 10 compile requests per minute per organization — exceeding it returns `429`.

**Query parameters:** `stream=true` — stream the compile over Server-Sent Events instead of a single JSON response (also triggered by `Accept: text/event-stream`).

**Request body:**

```json theme={null}
{
  "prompt": "Deny tier3 agents from accessing any document classified as internal, confidential, or restricted"
}
```

| Field     | Type     | Required | Description                                                  |
| --------- | -------- | -------- | ------------------------------------------------------------ |
| `prompt`  | `string` | Yes      | Plain-English rule description (max 4000 chars)              |
| `nodeRef` | `string` | No       | Rule-graph node reference for editor context (max 255 chars) |

**Response:**

```json theme={null}
{
  "rego_source": "package recall.rules.tier3_guard\n\ndefault decision = \"deny\"\n...",
  "natural_language": "This rule denies tier3 agents access to internal, confidential, or restricted documents...",
  "name": "Compiled retrieval rule",
  "description": "AI-compiled suppression rule",
  "suggested_priority": 50
}
```

**Streaming response** (`?stream=true`): a `text/event-stream` of JSON events —

```
data: {"type":"text","content":"Drafting the policy..."}

data: {"type":"done","result":{"rego_source":"...","natural_language":"...","name":"...","description":"...","suggested_priority":50}}
```

On failure the stream emits `{"type":"error","message":"..."}` and closes.

**Status codes:** `200 OK`, `400 Bad Request`, `401 Unauthorized`, `422 Unprocessable Entity` (the compiler could not produce a valid policy — refine the prompt), `429 Too Many Requests`, `500 Internal Server Error`, `503 Service Unavailable` (rule compilation is not configured in this environment)

***

### GET /recall/rules/:id

Get a single rule by UUID, including `rego_source`.

**Status codes:** `200 OK`, `400 Bad Request`, `401 Unauthorized`, `404 Not Found`, `500 Internal Server Error`

***

### PUT /recall/rules/:id

Update a rule. Same fields as `POST /recall/rules`, all optional; at least one is required. Only provided fields change. `trust_tier`, `surface`, and `principal_exclusions` accept `null` to clear.

**Response:** the updated rule object.

**Status codes:** `200 OK`, `400 Bad Request`, `401 Unauthorized`, `404 Not Found`, `500 Internal Server Error`

***

### DELETE /recall/rules/:id

Delete a rule permanently.

**Response:** `{ "deleted": true }`

**Status codes:** `200 OK`, `400 Bad Request`, `401 Unauthorized`, `404 Not Found`, `500 Internal Server Error`

***

## Emergency Bypass

Temporarily suspend one rule's enforcement during an incident — see [Emergency Bypass](/rules/retrieval/emergency-bypass) for when and why. Bypasses auto-expire; expiry is checked at evaluation time, so there is no revocation lag. Both activation and deactivation are written to the audit log with reason code `EMERGENCY_BYPASS`.

***

### POST /recall/rules/:id/bypass

Activate an emergency bypass on a rule. While active, any request that reaches the bypassed rule in the priority cascade returns `allow` with reason code `EMERGENCY_BYPASS` — even requests the rule's conditions would not have matched — and lower-priority rules never run for it. Higher-priority rules keep enforcing.

**Request body:**

```json theme={null}
{
  "reason": "Incident INC-2041: unblocking on-call diagnosis",
  "duration_minutes": 30
}
```

| Field              | Type     | Required | Description                                                         |
| ------------------ | -------- | -------- | ------------------------------------------------------------------- |
| `reason`           | `string` | Yes      | Why the bypass is needed (min 10 chars) — recorded in the audit log |
| `duration_minutes` | `number` | Yes      | One of `15`, `30`, `60`, `120`. The bypass expires automatically    |

**Response:** the full rule object with the bypass fields populated:

```json theme={null}
{
  "id": "rule-uuid",
  "name": "Deny tier3 from confidential",
  "bypass_active": true,
  "bypass_reason": "Incident INC-2041: unblocking on-call diagnosis",
  "bypass_activated_by": "user-uuid",
  "bypass_activated_at": "2026-07-03T10:30:00Z",
  "bypass_expires_at": "2026-07-03T11:00:00Z",
  "...": "..."
}
```

**Status codes:** `200 OK`, `400 Bad Request`, `401 Unauthorized`, `404 Not Found`, `409 Conflict` (a bypass is already active on this rule), `500 Internal Server Error`

***

### DELETE /recall/rules/:id/bypass

Deactivate an active bypass before it expires.

**Response:** the full rule object with the bypass fields cleared.

**Status codes:** `200 OK`, `400 Bad Request`, `401 Unauthorized`, `404 Not Found`, `409 Conflict` (no active bypass on this rule), `500 Internal Server Error`

***

## Audit Log

***

### GET /v1/recall/audit-log

Query the retrieval decision audit log. Every decision made through this endpoint — allow and deny, including monitor-mode observations and bypass activations — is recorded. (Locally evaluated SDK decisions surface through the runtime telemetry pipeline instead.) The log is append-only; no writes are accepted via this API.

<Note>
  Tamper evidence comes from the signed record pipeline, not from the query API: for organizations with artifact signing enabled, each decision emits a record envelope that receives an asynchronous Ed25519 receipt, is Merkle-batched under a KMS-signed root with an RFC 3161 timestamp, and lands in a hash-chained checkpoint log. Verify a decision's record envelope server-side via `GET /record/envelopes/:id/verify`.
</Note>

**Scope:** `rules:read` or `recall:read` (management keys / dashboard session — harness keys cannot query the log)

**Query parameters:**

| Parameter     | Type              | Description                                                                                                                                                         |
| ------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `agent_id`    | `string`          | Filter by agent logical ID                                                                                                                                          |
| `operation`   | `string`          | `retrieve` \| `tool_call` \| `prompt_render` \| `emergency_bypass_activate` \| `emergency_bypass_deactivate`                                                        |
| `decision`    | `string`          | `allow` \| `deny` \| `redact` \| `escalate`                                                                                                                         |
| `reason_code` | `string`          | `TIER_MISMATCH` \| `PRINCIPAL_EXCLUDED` \| `POLICY_DENY` \| `AUDIENCE_EXPANSION` \| `SURFACE_RESTRICTION` \| `POLICY_ALLOW` \| `DEFAULT_DENY` \| `EMERGENCY_BYPASS` |
| `start_date`  | ISO 8601 datetime | Lower bound on `created_at`                                                                                                                                         |
| `end_date`    | ISO 8601 datetime | Upper bound on `created_at`                                                                                                                                         |
| `page`        | `number`          | Page index (default `1`)                                                                                                                                            |
| `limit`       | `number`          | Records per page (default `50`, max `100`)                                                                                                                          |

Rows recorded under monitor mode carry `reason_code: "MONITOR_MODE"`; it is not yet accepted as a filter value.

**Response:**

```json theme={null}
{
  "data": [
    {
      "id": "audit-entry-uuid",
      "agent_id": "research-bot",
      "operation": "retrieve",
      "resource_type": "document",
      "decision": "deny",
      "reason_code": "TIER_MISMATCH",
      "reason": "agent trust tier 'tier3' (level 1) is below the required minimum 'tier2' (level 2)",
      "rule_id": "rule-uuid",
      "receipt_id": null,
      "metadata": {
        "surface": "public_channel",
        "trust_tier": "tier3",
        "query": "quarterly revenue",
        "resource_metadata": { "classification": "confidential" },
        "floor_detectors": [],
        "decision_id": "decision-uuid"
      },
      "created_at": "2026-07-03T10:30:00Z"
    }
  ],
  "total": 1247,
  "page": 1,
  "pageSize": 50
}
```

`metadata.query` and `metadata.resource_metadata` are the **masked** persisted copies (rule directives plus the always-on redaction floor); `metadata.floor_detectors` names any floor detectors that fired, and `metadata.decision_id` is the underlying decision's ID for the retained-original reveal below. `receipt_id` is a legacy column and is `null` on new rows — receipts flow through the record pipeline instead.

**Status codes:** `200 OK`, `400 Bad Request`, `401 Unauthorized`, `403 Forbidden` (insufficient scope), `429 Too Many Requests`, `500 Internal Server Error`

***

## Retained Originals

***

### GET /recall/decisions/:id/unmask

Reveal the retained, encrypted original payload for one retrieval decision. Persisted copies are masked at rest; when your organization has enabled retained-original storage (off by default), the raw `resource_metadata` and `query` are additionally stored AES-256-GCM-encrypted, and this endpoint decrypts them.

This is a management endpoint gated on the `payloads:unmask` permission — only explicitly-authorized roles can ever see the original values, and every reveal is logged with who requested it. Harness keys receive `403`.

**Path parameter:** `:id` — the decision UUID (`decision_id` from `POST /recall/evaluate`, or `metadata.decision_id` on an audit-log row)

**Response:**

```json theme={null}
{
  "decision_id": "decision-uuid",
  "original": {
    "resource_metadata": { "classification": "confidential", "account_number": "4111111111111111" },
    "query": "quarterly revenue figures"
  }
}
```

**Status codes:** `200 OK`, `400 Bad Request` (invalid UUID), `401 Unauthorized`, `403 Forbidden`, `404 Not Found` (unknown decision, or retention is disabled for your organization), `500 Internal Server Error`
