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

> ## Agent Instructions
> The OpenAPI document at /api-reference/openapi.yaml defines the target contract. Its x-reflections-availability field and each operation's availability notice identify supported test and live forms. Do not infer implementation from a schema or code sample. Live keys support account details, reads of the licenses the calling account holds as licensee or issued as talent (GET /v1/licenses?role=issuer), and GET /v1/generations?license=… for the state of outputs under such a license (summaries only: no prompt, reference, bytes or metadata). Live image checks, submissions and receipts are built but not activated: until activation, a live POST /v1/checks or /v1/generations returns 403 live_not_activated, which must not be retried. Discovery and JWKS are at the origin root (https://app.reflections.ai/.well-known/...), not under /v1.
> Three rules hold everywhere: a decision is never an error (deny and escalate are 200 responses, and a live check still evaluating is a 202 with status checking); generate nothing for a named talent without an allow; deliver nothing until the generation is allow.
> The sandbox accepts personal rk_test_ keys. Persist a separate Idempotency-Key and body for each logical /v1 POST; retry an unknown outcome with the same key and body. OAuth and Talent API MCP remain planned.

# Escalation

> Who reviews an escalated check or a held output, how long it can take, and what expiry means.

<Note>
  Sandbox and protocol reference. Sandbox escalations are controlled by test fixtures and the
  test helper, not sent to a real talent or reviewer. Production review queues and webhook
  delivery remain planned.
  Start with the [runnable quickstart](/quickstart) and [sandbox support matrix](/sandbox).
</Note>

An `escalate` decision means a person with authority must decide before a use starts or an output
is delivered. The response returns at once with an `escalation` object; nobody waits on an open
connection. Until the escalation resolves, you do not generate and you do not deliver.

## The escalation object

| Field | Meaning |
| - | - |
| `id` | `esc_…` |
| `reason` | Why a person must decide, below |
| `reviewer` | `talent`, `representative` or `reflections` |
| `status` | `open`, `allowed`, `denied`, `expired` or `cancelled` |
| `expires_at` | When an unanswered escalation becomes `deny` |
| `resolved_at`, `note` | When it was decided, and the reviewer's note if they wrote one |

When a request invokes several talent and more than one escalates, each has their own escalation,
named in `talent[].escalation`. The check is allowed only when every one allows, and denied at the
first `deny` or expiry. See [Multi-talent requests](/build/multi-talent).

## Why a request escalates

| `reason` | Stage | Meaning |
| - | - | - |
| `judgment` | Check | A rule the talent wrote needs a person to read it against this request |
| `unverifiable` | Check | A protected attribute could not be confirmed |
| `evaluation_unavailable` | Check | The check could not finish, for example when an evaluator timed out |
| `release_review` | Generation | The license has a person release every output |

Two things never escalate. A use no license covers is `deny` with a `license_request` remedy,
because a license request is a negotiation and a generation cannot wait on one. A use the talent
prohibits is `deny` with `talent_prohibits`.

## Who reviews

| License | Reviewer | Response target |
| - | - | - |
| Customer license, such as a brand's | The talent, or a representative with `review_outputs` | None; the escalation expires at `expires_at` |
| Platform license | Reflections trust and safety | One business day |

## At the check

```mermaid theme={null}
sequenceDiagram
    participant U as End user
    participant P as Your platform
    participant R as Reflections
    participant T as Talent
    P->>R: POST /v1/checks
    R-->>P: escalate, expires_at
    P-->>U: Waiting for approval
    R->>T: Review request
    T->>R: Allow
    R-->>P: check.resolved, allow
    P->>P: Generate under the authorization
```

Show your user a pending state and listen for
[`check.resolved`](/api-reference/webhooks/check-resolved). You may also poll
[`GET /v1/checks/{check_id}`](/api-reference/checks/retrieve-a-check), at most once every 5 seconds.

* On `allow`, the check's `authorization` covers exactly the request you escalated, bound by its
  `request_digest`. Changing the prompt or a reference needs a new check.
* On `deny`, the check's status is `denied`. The reason is `review_declined` when the reviewer
  declined, or `hold_expired` when nobody answered in time. `reasons[].sentence` says why in the
  license's words.
* If your user leaves, cancel the check with
  [`POST /v1/checks/{check_id}/cancel`](/api-reference/checks/cancel-a-check). The reviewer is told,
  and the check becomes `cancelled`.

## At the generation

Under a license with `release_policy: "per_output_review"`, every output that passes its checks
returns `escalate` with `status: "held"` and the `hold_until_release` obligation. Keep the clean
output private. If the generation carries `draft_watermark`, you may show a watermarked preview;
otherwise show nothing. Deliver only after
[`generation.released`](/api-reference/webhooks/generation-released). If the reviewer declines, the
generation ends `denied` with `review_declined`, sent as
[`generation.denied`](/api-reference/webhooks/generation-denied).

Files a held output needs are kept past the usual 24 hours. [Privacy](/concepts/privacy) says how
long.

## How long it can take

An escalation expires 7 days (`P7D`) after it opens, unless the license's `escalation_window` is
shorter. `expires_at` is the exact time.

* An expired check escalation ends the check with status `denied`, decision `deny` and the reason
  `hold_expired`, delivered as `check.resolved`. The escalation's own `status` is `expired`.
* An expired held output ends with status `expired`, decision `deny` and the reason
  `review_expired`, delivered as [`generation.expired`](/api-reference/webhooks/generation-expired).
  It can never be released.

A check's status `expired` means something else: an allowed check whose authorization expired
before it was used. An escalated check never ends `expired`.

A revocation while an escalation is open ends it: the check ends `denied` with `license_revoked`,
and a held output ends `denied` with `license_revoked`, sent as `generation.denied`, and can no
longer be released.

## Interactive products

A chat, a voice call or any product where a user is waiting should not sit on a review queue. Pass
`on_escalate: "deny"` on the check. Anything that would escalate returns `deny` at once with
`review_required` and a `retry_with_hold` remedy, so you can offer the user a reviewed path later.
[Sessions](/build/real-time-sessions) always behave this way, and a license that holds every output
for release cannot start a session (`session_not_permitted`).

## Example: a rule that needs a person

Ridgeline asks for Maya Okafor at a trailhead café. Her rule "Do not show alcohol or tobacco." needs
a person to judge the request:

```json theme={null}
{
  "id": "chk_r6s9t0wxyhk5zba2q8v1m3n7p4",
  "object": "check",
  "status": "escalated",
  "decision": "escalate",
  "reasons": [
    {
      "code": "prohibited_custom_term",
      "stage": "check",
      "talent": "tal_9t0wxyhk5zba2q8v1m3n7p4r6s",
      "term": "license:lic_k5zba2q8v1m3n7p4r6s9t0wxyh/terms.neverExtras",
      "sentence": "Do not show alcohol or tobacco."
    }
  ],
  "authorization": null,
  "escalation": {
    "id": "esc_t0wxyhk5zba2q8v1m3n7p4r6s9",
    "reason": "judgment",
    "reviewer": "talent",
    "status": "open",
    "expires_at": "2026-10-24T18:04:00Z",
    "resolved_at": null,
    "note": null
  }
}
```

When Maya Okafor's manager allows it, your endpoint receives:

```json theme={null}
{
  "id": "evt_4h6k8m0p2r4t6v8x0z2b4d6f8g",
  "object": "event",
  "type": "check.resolved",
  "sequence": 2,
  "data": {
    "object": {
      "id": "chk_r6s9t0wxyhk5zba2q8v1m3n7p4",
      "object": "check",
      "status": "allowed",
      "decision": "allow",
      "escalation": {
        "id": "esc_t0wxyhk5zba2q8v1m3n7p4r6s9",
        "reason": "judgment",
        "reviewer": "talent",
        "status": "allowed",
        "expires_at": "2026-10-24T18:04:00Z",
        "resolved_at": "2026-10-17T21:30:00Z",
        "note": "Fine as long as the cups hold coffee."
      },
      "authorization": { "token": "eyJhbGciOiJFUzI1NiIs…", "max_generations": 4, "generations_used": 0, "expires_at": "2026-10-17T21:45:00Z" }
    }
  }
}
```

The authorization covers the request exactly as it was escalated, until its `expires_at`.

## Related

<Columns cols={2}>
  <Card title="Handling escalations" icon="code" href="/build/handling-escalations">
    Pending states, webhooks, polling and `on_escalate`.
  </Card>

  <Card title="Decisions" icon="scale-balanced" href="/concepts/decisions">
    The three decisions and the check statuses.
  </Card>

  <Card title="Talent representatives" icon="user-tie" href="/guides/talent-representatives">
    Reviewing escalations for a talent.
  </Card>
</Columns>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.