> ## 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.

# Resolution

> How a request is matched to talent, and why no match is not permission.

<Note>
  Protocol reference. The current sandbox matches fictional talent by explicit ID or text.
  Standalone resolution, a downloadable name index and image or voice recognition remain
  planned.
  Start with the [runnable quickstart](/quickstart) and [sandbox support matrix](/sandbox).
</Note>

**Resolution** finds which talent a request invokes. Every check resolves its inputs, including
when you name talent yourself. Resolution never clears anything: it says who is in a request, and
the license decides what may be done.

## How a request invokes a talent

| How the request invokes them | `matched_by` | Typical `confidence` |
| - | - | - |
| You pass the talent's ID in `talent`, from a cast picker or catalog | `explicit` | `exact` |
| The name or a confirmed alias appears in text | `name`, `alias` | `high`, `medium` or `low` |
| An indirect reference, such as "the host of Ridge Runners" | `indirect` | Usually `medium` |
| A reference image or voice sample of the talent | `reference_image`, `voice_sample` | Depends on the match score |
| A description with no name or credit | Not resolved | |

Names, aliases, transliterations and credits come from [Atlas](/concepts/atlas). Reference media is
matched against the face and voice of every talent with `protection.references` on. A description
alone is not resolved, and outputs are checked only for the talent the check covered; see
[Generation checks](/concepts/generation-checks#which-talent-are-checked).

## Matching is inclusive

A check treats every candidate at `medium` confidence or higher as invoked, and its decision is the
most restrictive across them. This produces false positives for common names: a prompt about a
different person who shares a name with a protected talent is checked against that talent too.

To remove the friction, narrow the check:

* Pass `talent` with explicit IDs when your product already knows who the user means. A name in
  the inputs that a talent in `talent` carries counts as that talent, not as a match for someone
  else who shares it.
* Pass `exclude_talent` with the IDs of talent your user confirmed they did not mean.

The alternative, matching only when certain, would let a misspelled or renamed prompt through, and
that failure would land on the talent. Inclusive matching puts the cost on a confirmation step in
your product instead.

## No match is not permission

When no protected talent is found, a check returns `allow` with `talent: []`. That means only that
the request names no one Reflections protects. It is not a license for any person the output
shows. A resolution with `status: "none"` means the same thing.

## Use kind

Resolution also classifies how the inputs use a talent, as `use_kind`:

| `use_kind` | Meaning | License needed |
| - | - | - |
| `depiction` | The talent is shown or heard | Yes |
| `impersonation` | Speaks or writes as the talent | Yes |
| `endorsement` | Implies the talent approves something | Yes |
| `reference` | Talked about, without synthesis, such as a news question | No |

This keeps an assistant from refusing ordinary questions about public figures. It is a
classification for routing, not a legal finding. The `use.kind` you send on a check must describe
what your model will produce.

## Resolving before the check

Checks resolve automatically, so most platforms never call resolution directly. Call
[`POST /v1/resolutions`](/api-reference/resolutions/resolve-talent) when your own interface needs the
candidates, for example to ask "did you mean Maya Okafor?" before you check.

A resolution's `status` is `matched`, `ambiguous` (more than one candidate, or one weak candidate)
or `none`. Each candidate carries `matched_by`, `confidence`, the `span` of input text that matched,
whether the licensee holds an active license for that talent (`licensed`), and the talent's
`declaration` for the use.

## The name index

If you do not want to send every prompt, download the signed
[name index](/api-reference/talent/download-the-name-index) and match names on your side. It holds
every protected talent's names, aliases and transliterations, normalized with `nfkc_casefold`.
Normalize prompt text the same way before you match. An entry with `common: true` is a name common
enough that a match alone is weak evidence.

Verify the index's `signature` against the JWKS before you use it, refresh it at least hourly with
`If-None-Match`, and stop matching against it after `expires_at`. A local match tells you to send a
check. No local match does not clear a prompt: indirect references and reference media need the
server. See [Privacy](/concepts/privacy) for the prompt modes that pair with local matching.

## Example: a podcast intro

<CodeGroup>
  ```bash cURL theme={null}
  curl https://app.reflections.ai/v1/resolutions \
    -H "Authorization: Bearer rk_test_…" \
    -H "Reflections-Version: 2026-10-15" \
    -H "Idempotency-Key: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d '{
      "inputs": [{ "role": "prompt", "text": "A podcast intro read by Maya Okafor" }],
      "modality": "speech"
    }'
  ```

  ```typescript TypeScript theme={null}
  const resolution = await fetch("https://app.reflections.ai/v1/resolutions", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.REFLECTIONS_KEY}`,
      "Reflections-Version": "2026-10-15",
      "Idempotency-Key": crypto.randomUUID(),
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      inputs: [{ role: "prompt", text: "A podcast intro read by Maya Okafor" }],
      modality: "speech",
    }),
  }).then((r) => r.json());
  ```

  ```python Python theme={null}
  import os, uuid, requests

  resolution = requests.post(
      "https://app.reflections.ai/v1/resolutions",
      headers={
          "Authorization": f"Bearer {os.environ['REFLECTIONS_KEY']}",
          "Reflections-Version": "2026-10-15",
          "Idempotency-Key": str(uuid.uuid4()),
      },
      json={
          "inputs": [{"role": "prompt", "text": "A podcast intro read by Maya Okafor"}],
          "modality": "speech",
      },
  ).json()
  ```
</CodeGroup>

```json theme={null}
{
  "id": "res_2d4f6h8k0m2p4r6t8v0x2z4b6c",
  "object": "resolution",
  "status": "matched",
  "candidates": [
    {
      "talent": "tal_9t0wxyhk5zba2q8v1m3n7p4r6s",
      "name": "Maya Okafor",
      "matched_by": "name",
      "confidence": "high",
      "span": { "input": 0, "start": 24, "end": 35, "text": "Maya Okafor" },
      "licensed": true,
      "declaration": "license_required"
    }
  ],
  "use_kind": "impersonation"
}
```

The app shows "Maya Okafor" as the voice, then sends the check with
`talent: ["tal_9t0wxyhk5zba2q8v1m3n7p4r6s"]` and the voice facet in `facet`, so the check skips
resolution and decides against her voice license.

## Related

<Columns cols={2}>
  <Card title="Atlas" icon="sitemap" href="/concepts/atlas">
    The names and credits resolution matches against.
  </Card>

  <Card title="Resolving talent" icon="code" href="/build/resolving-talent">
    Explicit IDs, autocomplete, disambiguation and the name index.
  </Card>

  <Card title="Several talent" icon="users" href="/build/multi-talent">
    The most restrictive rule across talent.
  </Card>

  <Card title="Resolve talent" icon="magnifying-glass" href="/api-reference/resolutions/resolve-talent">
    The endpoint reference.
  </Card>
</Columns>


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