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

# Multi-talent requests

> Check a request that invokes several talent, and combine their decisions and obligations.

<Note>
  Sandbox and protocol reference. Multiple fictional talent can be named in a test check. Image
  and voice recognition, production review and remedies described below remain planned.
  Start with the [runnable quickstart](/quickstart) and [sandbox support matrix](/sandbox).
</Note>

A prompt can name two people, and a reference image can show a second face. Each protected talent is
decided separately against their own license or posture, and the request gets the most restrictive
answer.

## How a check decides

Every talent the request invokes gets its own entry in `talent[]`, with its own `decision`,
`license`, `license_revision` and `terms_digest`. The check's `decision` is the most restrictive of
them:

| Any talent is | The check is |
| - | - |
| `deny` | `deny` |
| `escalate`, and none is `deny` | `escalate` |
| `allow`, all of them | `allow` |

When more than one talent is `escalate`, each has their own escalation, named in
`talent[].escalation` and decided by that talent's side. The check becomes `allow` when every one
allows. It becomes `deny` at the first `deny`, or when the first escalation expires, and the
others are `cancelled`. While the check is open, its `escalation` shows the one that expires first.

Only an `allow` for everyone carries an `authorization`. Its token lists every talent and facet in
`tlp.talent` and `tlp.facets`, and every license that applied in `tlp.licenses`. See
[What the token and receipt carry](#what-the-token-and-receipt-carry).

## Example: one licensed, one not

This example runs in test mode, against the [sandbox](/sandbox) talent. Ridgeline's prompt names
Maya Okafor, whose license covers the use, and Iris Calder, who has licensed nothing to Ridgeline.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://app.reflections.ai/v1/checks \
    -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": "Maya Okafor and Iris Calder racing up a ridge trail in our new shoes" }],
      "use": {
        "modality": "image", "kind": "depiction", "purpose": "advertising",
        "commercial": true, "channels": ["social"], "territories": ["US"]
      },
      "end_user": { "id": "usr_48213" },
      "execution": { "provider": "example-image-provider", "model": "image-model-3", "candidates": 2 }
    }'
  ```

  ```typescript TypeScript theme={null}
  const check = await fetch("https://app.reflections.ai/v1/checks", {
    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: "Maya Okafor and Iris Calder racing up a ridge trail in our new shoes" }],
      use: {
        modality: "image", kind: "depiction", purpose: "advertising",
        commercial: true, channels: ["social"], territories: ["US"],
      },
      end_user: { id: "usr_48213" },
      execution: { provider: "example-image-provider", model: "image-model-3", candidates: 2 },
    }),
  }).then((r) => r.json());
  ```

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

  check = requests.post(
      "https://app.reflections.ai/v1/checks",
      headers={
          "Authorization": f"Bearer {os.environ['REFLECTIONS_KEY']}",
          "Reflections-Version": "2026-10-15",
          "Idempotency-Key": str(uuid.uuid4()),
      },
      json={
          "inputs": [{"role": "prompt", "text": "Maya Okafor and Iris Calder racing up a ridge trail in our new shoes"}],
          "use": {
              "modality": "image", "kind": "depiction", "purpose": "advertising",
              "commercial": True, "channels": ["social"], "territories": ["US"],
          },
          "end_user": {"id": "usr_48213"},
          "execution": {"provider": "example-image-provider", "model": "image-model-3", "candidates": 2},
      },
  ).json()
  ```
</CodeGroup>

```json theme={null}
{
  "id": "chk_9k1m3p5r7t9w1y3a5c7e9g1j3m",
  "object": "check",
  "livemode": false,
  "status": "denied",
  "decision": "deny",
  "talent": [
    {
      "talent": "tal_9t0wxyhk5zba2q8v1m3n7p4r6s",
      "name": "Maya Okafor",
      "matched_by": "name",
      "confidence": "high",
      "decision": "allow",
      "license": "lic_97bb8qgh1b9zk81anbjkmprxh4",
      "license_revision": "lrv_11knr0923nvdw36qe404d6c55d",
      "terms_digest": "sha256:4036ac3ffc579dc70d5a9132242e3cb588007d08bee1b2b92812b2d4e3ff7f4e"
    },
    {
      "talent": "tal_7q9s1v3w5y7a9c1e3g5h7k9m1n",
      "name": "Iris Calder",
      "matched_by": "name",
      "confidence": "high",
      "decision": "deny",
      "license": null,
      "license_revision": null,
      "terms_digest": null
    }
  ],
  "reasons": [
    {
      "code": "no_license",
      "stage": "check",
      "talent": "tal_7q9s1v3w5y7a9c1e3g5h7k9m1n",
      "term": "talent:tal_7q9s1v3w5y7a9c1e3g5h7k9m1n",
      "sentence": "Iris Calder has not licensed this use to Ridgeline."
    }
  ],
  "authorization": null,
  "remedy": { "type": "license_request", "talent": "tal_7q9s1v3w5y7a9c1e3g5h7k9m1n" }
}
```

Each reason names its `talent`, and `remedy.talent` names the talent the remedy is for. Show your
user which person blocked the request. They can remove Iris Calder from the prompt, which is a new
request and a new check, or Ridgeline can
[request a license](/api-reference/license-requests/request-a-license) from her with the denied
check's ID.

Do not send `exclude_talent` to drop a talent the prompt names on purpose. It is only for a person
your user confirmed they did not mean.

## Combine obligations

When every talent is `allow`, `obligations` holds what every license requires. Meet all of them:

* **`generation_instructions`**: pass every instruction to the model, for every talent.
* **`hold_until_release`**: one talent's hold holds the whole output, because the output shows
  everyone. In test mode, Maya Okafor with Jonah Reyes returns `allow` with a `hold_until_release`
  obligation, and the generation is `held` until Jonah Reyes's side releases it.
* **`disclosure`**: two disclosure obligations mean two labels. Show each `text`.
* **`usage_report`**: report every unit the `usage_report` obligations name. A
  [usage record](/build/reporting-usage) counts against one license, so for a generation made
  under several licenses, send one record per license with its `license` set. A record is rejected
  as `duplicate` when the same subject, license and units were already reported.

When an obligation carries a `term`, the address names the license it comes from.

## What the token and receipt carry

A token or receipt for several talent names each license that applied in `tlp.licenses`: one entry
per talent, in the order of `tlp.talent`, with the `license`, the signed `revision` and its
`terms_digest`. A talent whose posture allows the use without a license has no entry, so
`tlp.licenses` can be shorter than `tlp.talent`.

In test mode, a check for Maya Okafor and Jonah Reyes returns `allow`. Its authorization token
carries these two claims, shown here without the `tlp.` prefix:

```json theme={null}
{
  "talent": ["tal_9t0wxyhk5zba2q8v1m3n7p4r6s", "tal_4n6p8r0t2v4x6z8b0d2f4h6j8k"],
  "licenses": [
    {
      "talent": "tal_9t0wxyhk5zba2q8v1m3n7p4r6s",
      "license": "lic_97bb8qgh1b9zk81anbjkmprxh4",
      "revision": "lrv_11knr0923nvdw36qe404d6c55d",
      "terms_digest": "sha256:4036ac3ffc579dc70d5a9132242e3cb588007d08bee1b2b92812b2d4e3ff7f4e"
    },
    {
      "talent": "tal_4n6p8r0t2v4x6z8b0d2f4h6j8k",
      "license": "lic_31x87g772xdcz1n1za29601rbt",
      "revision": "lrv_csvxga5wmqeaakm4x24k7gyspx",
      "terms_digest": "sha256:ec5e8d04b128b903962774b30a822617b20b07d6e4b9ad7ddf8976a856f47ad6"
    }
  ]
}
```

The token is signed with the test key, so its header `kid` is `rfl-test-2026-10-docs3`.

Jonah Reyes's license holds every output, so the generation is `held` and gets its receipt when it
is released. In test mode, release it with
[`POST /v1/test_helpers/escalations/{escalation_id}/resolve`](/api-reference/test-helpers/resolve-an-escalation).
The receipt's `tlp.licenses` carries the same two entries. The receipt object from
[`GET /v1/receipts/{receipt_id}`](/api-reference/receipts/retrieve-a-receipt) lists them in
`licenses`, where each entry names its revision `license_revision`:

```json theme={null}
{
  "object": "receipt",
  "livemode": false,
  "kind": "generation",
  "decision": "allow",
  "talent": ["tal_9t0wxyhk5zba2q8v1m3n7p4r6s", "tal_4n6p8r0t2v4x6z8b0d2f4h6j8k"],
  "licenses": [
    {
      "talent": "tal_9t0wxyhk5zba2q8v1m3n7p4r6s",
      "license": "lic_97bb8qgh1b9zk81anbjkmprxh4",
      "license_revision": "lrv_11knr0923nvdw36qe404d6c55d",
      "terms_digest": "sha256:4036ac3ffc579dc70d5a9132242e3cb588007d08bee1b2b92812b2d4e3ff7f4e"
    },
    {
      "talent": "tal_4n6p8r0t2v4x6z8b0d2f4h6j8k",
      "license": "lic_31x87g772xdcz1n1za29601rbt",
      "license_revision": "lrv_csvxga5wmqeaakm4x24k7gyspx",
      "terms_digest": "sha256:ec5e8d04b128b903962774b30a822617b20b07d6e4b9ad7ddf8976a856f47ad6"
    }
  ]
}
```

Test receipts verify only with a test key.

## Name several talent explicitly

When your user picks people from a cast list, send them all in `talent`, up to 10 on a check and 4
on a session:

```json theme={null}
{
  "talent": ["tal_9t0wxyhk5zba2q8v1m3n7p4r6s", "tal_4n6p8r0t2v4x6z8b0d2f4h6j8k"],
  "inputs": [{ "role": "prompt", "text": "The two of them stretching at a trailhead" }]
}
```

Explicit IDs do not hide anyone else. If the inputs also name or show another protected talent,
the check adds them to `talent[]` and decides for them too.

## Narrow after disambiguation

A common name can match several talent, or a surname can match one weakly. Every candidate at
`medium` confidence or higher is treated as invoked, so an unintended match can deny a request your
user meant for someone else. When that happens:

<Steps>
  <Step title="Show the candidates">
    Call [`POST /v1/resolutions`](/api-reference/resolutions/resolve-talent) to get each candidate
    with the words that matched, and ask your user who they meant.
  </Step>

  <Step title="Send the answer on the check">
    Put the people they meant in `talent`, and the people they did not mean in `exclude_talent`.
  </Step>
</Steps>

See [Resolve talent](/build/resolving-talent) for the full flow.

## Talent the check did not name

A generation is checked only for the talent its check covered. Reflections does not look for other
people in the output, so the check has to carry everyone the request invokes: each name, and each
reference image or voice sample. When your user adds a person after the check, run a new check that
names them all; it is decided like any other.

## Related

<Columns cols={2}>
  <Card title="Decisions" icon="shield-check" href="/concepts/decisions">
    `allow`, `deny` and `escalate` at each stage.
  </Card>

  <Card title="Resolve talent" icon="magnifying-glass" href="/build/resolving-talent">
    Explicit IDs, disambiguation and the name index.
  </Card>

  <Card title="Obligations" icon="list-check" href="/concepts/obligations">
    Every obligation and how to meet it.
  </Card>

  <Card title="Submit outputs" icon="image" href="/build/submitting-outputs">
    Findings at the generation.
  </Card>
</Columns>


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