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

# Text integration design

> Planned assistant workflow for resolving references, impersonation and endorsement before a check.

<Note>
  Planned design guide. This workflow starts with standalone resolution, which is not served by
  the current Talent API sandbox. The examples describe the target integration; use explicit
  fictional talent IDs in current sandbox checks.
  Start with the [runnable quickstart](/quickstart) and [sandbox support matrix](/sandbox).
</Note>

An assistant meets real people in three ways. A question about a public figure is a
**reference** and needs no license. Writing as the talent is **impersonation**. Text that implies
the talent approves something is **endorsement**. The last two need a check before the model
writes anything. These examples describe all three paths using fictional talent; standalone
resolution must be implemented before this complete workflow can run.

| What the user asks | `use.kind` | License needed |
| - | - | - |
| "What races has Maya Okafor won this year?" | `reference` | No |
| "Write my toast as if Iris Calder were giving it" | `impersonation` | Yes |
| "Write a caption where Dev Marlow says he loves our cold brew" | `endorsement` | Yes |

<Steps>
  <Step title="Export your test key">
    ```bash theme={null}
    export REFLECTIONS_KEY="rk_test_…"
    ```
  </Step>

  <Step title="Classify a question about a public figure">
    If your product cannot tell how a message uses a person, resolve it. The resolution names the
    talent and classifies the use. It never clears a use; it tells you what to send next.

    <CodeGroup>
      ```bash cURL theme={null}
      curl https://app.reflections.ai/v1/resolutions \
        -H "Authorization: Bearer $REFLECTIONS_KEY" \
        -H "Reflections-Version: 2026-10-15" \
        -H "Content-Type: application/json" \
        -d '{
          "inputs": [{ "role": "prompt", "text": "What races has Maya Okafor won this year?" }],
          "modality": "text"
        }'
      ```

      ```typescript TypeScript theme={null}
      const api = "https://app.reflections.ai/v1";
      const auth = {
        Authorization: `Bearer ${process.env.REFLECTIONS_KEY}`,
        "Reflections-Version": "2026-10-15",
      };
      const json = { ...auth, "Content-Type": "application/json" };

      async function ok(res: Response) {
        if (!res.ok) throw new Error(`${res.status} ${await res.text()}`); // the problem details
        return res.json();
      }

      const resolution = await fetch(`${api}/resolutions`, {
        method: "POST",
        headers: json,
        body: JSON.stringify({
          inputs: [{ role: "prompt", text: "What races has Maya Okafor won this year?" }],
          modality: "text",
        }),
      }).then(ok);
      ```

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

      API = "https://app.reflections.ai/v1"
      auth = {
          "Authorization": f"Bearer {os.environ['REFLECTIONS_KEY']}",
          "Reflections-Version": "2026-10-15",
      }

      def ok(res):
          if not res.ok:
              raise RuntimeError(f"{res.status_code} {res.text}")  # the problem details
          return res.json()

      resolution = ok(requests.post(
          f"{API}/resolutions",
          headers=auth,
          json={
              "inputs": [{"role": "prompt", "text": "What races has Maya Okafor won this year?"}],
              "modality": "text",
          },
      ))
      ```
    </CodeGroup>

    ```json theme={null}
    {
      "id": "res_mtgwtawk8srbgwdg03zdesn26x",
      "object": "resolution",
      "livemode": false,
      "status": "matched",
      "candidates": [
        {
          "talent": "tal_9t0wxyhk5zba2q8v1m3n7p4r6s",
          "name": "Maya Okafor",
          "matched_by": "name",
          "confidence": "high",
          "span": { "input": 0, "start": 15, "end": 26, "text": "Maya Okafor" }
        }
      ],
      "use_kind": "reference"
    }
    ```

    `use_kind: "reference"` means the message talks about her without synthesis. Answer it as
    usual. `use_kind` is a classification for routing, not a legal finding.
  </Step>

  <Step title="Check a request to write as a talent">
    Send the request the model will receive: your system prompt, the conversation it sees and the
    user's message. Leave out `talent`, so Reflections resolves the person from the text. Pass
    `on_escalate: "deny"`, because a chat cannot wait for a person to review.

    <CodeGroup>
      ```bash cURL theme={null}
      curl https://app.reflections.ai/v1/checks \
        -H "Authorization: Bearer $REFLECTIONS_KEY" \
        -H "Reflections-Version: 2026-10-15" \
        -H "Idempotency-Key: $(uuidgen)" \
        -H "Content-Type: application/json" \
        -d '{
          "inputs": [
            { "role": "system", "text": "You are a writing assistant. Be warm and concise." },
            { "role": "context", "text": "User: My brother is getting married in June." },
            { "role": "prompt", "text": "Write my toast as if Iris Calder were giving it." }
          ],
          "use": { "modality": "text", "kind": "impersonation", "purpose": "personal", "commercial": false },
          "end_user": { "id": "usr_48213" },
          "execution": { "provider": "example-llm-provider", "model": "chat-model-4", "candidates": 1 },
          "on_escalate": "deny"
        }'
      ```

      ```typescript TypeScript theme={null}
      async function check(inputs: object[], use: object) {
        return fetch(`${api}/checks`, {
          method: "POST",
          headers: { ...json, "Idempotency-Key": crypto.randomUUID() },
          body: JSON.stringify({
            inputs,
            use,
            end_user: { id: "usr_48213" },
            execution: { provider: "example-llm-provider", model: "chat-model-4", candidates: 1 },
            on_escalate: "deny",
          }),
        }).then(ok);
      }

      const impersonation = await check(
        [
          { role: "system", text: "You are a writing assistant. Be warm and concise." },
          { role: "context", text: "User: My brother is getting married in June." },
          { role: "prompt", text: "Write my toast as if Iris Calder were giving it." },
        ],
        { modality: "text", kind: "impersonation", purpose: "personal", commercial: false },
      );
      ```

      ```python Python theme={null}
      def check(inputs: list, use: dict) -> dict:
          return ok(requests.post(
              f"{API}/checks",
              headers={**auth, "Idempotency-Key": str(uuid.uuid4())},
              json={
                  "inputs": inputs,
                  "use": use,
                  "end_user": {"id": "usr_48213"},
                  "execution": {"provider": "example-llm-provider", "model": "chat-model-4", "candidates": 1},
                  "on_escalate": "deny",
              },
          ))

      impersonation = check(
          [
              {"role": "system", "text": "You are a writing assistant. Be warm and concise."},
              {"role": "context", "text": "User: My brother is getting married in June."},
              {"role": "prompt", "text": "Write my toast as if Iris Calder were giving it."},
          ],
          {"modality": "text", "kind": "impersonation", "purpose": "personal", "commercial": False},
      )
      ```
    </CodeGroup>

    Reflections found Iris Calder in the prompt. She requires a license and you hold none:

    ```json theme={null}
    {
      "id": "chk_cy6pqtxvqyzyy8pw0wj51zpjpq",
      "object": "check",
      "livemode": false,
      "status": "denied",
      "decision": "deny",
      "talent": [
        {
          "talent": "tal_7q9s1v3w5y7a9c1e3g5h7k9m1n",
          "name": "Iris Calder",
          "matched_by": "name",
          "confidence": "high",
          "decision": "deny",
          "license": null
        }
      ],
      "reasons": [
        {
          "code": "no_license",
          "stage": "check",
          "talent": "tal_7q9s1v3w5y7a9c1e3g5h7k9m1n",
          "term": "talent:tal_7q9s1v3w5y7a9c1e3g5h7k9m1n",
          "sentence": "Iris Calder licenses her name and persona only by signed agreement."
        }
      ],
      "remedy": { "type": "license_request", "talent": "tal_7q9s1v3w5y7a9c1e3g5h7k9m1n" },
      "authorization": null
    }
    ```

    Do not write as her. Tell the user why with `reasons[].sentence`, and offer what you can do
    instead, such as a toast in your assistant's own voice. The `license_request` remedy is for a
    licensee, such as a brand you work for. End users are never licensees, so do not offer it to a
    consumer.
  </Step>

  <Step title="Check a request that implies approval">
    Text that says or implies the talent approves a product is `endorsement`. This one is for a
    marketer's social post, so it is commercial.

    <CodeGroup>
      ```bash cURL theme={null}
      curl https://app.reflections.ai/v1/checks \
        -H "Authorization: Bearer $REFLECTIONS_KEY" \
        -H "Reflections-Version: 2026-10-15" \
        -H "Idempotency-Key: $(uuidgen)" \
        -H "Content-Type: application/json" \
        -d '{
          "inputs": [
            { "role": "system", "text": "You are a writing assistant. Be warm and concise." },
            { "role": "prompt", "text": "Write an Instagram caption where Dev Marlow says our cold brew is the only coffee he drinks." }
          ],
          "use": {
            "modality": "text",
            "kind": "endorsement",
            "purpose": "advertising",
            "commercial": true,
            "channels": ["social"],
            "territories": ["US"]
          },
          "end_user": { "id": "usr_48213", "type": "business" },
          "execution": { "provider": "example-llm-provider", "model": "chat-model-4", "candidates": 1 },
          "on_escalate": "deny"
        }'
      ```

      ```typescript TypeScript theme={null}
      const endorsement = await check(
        [
          { role: "system", text: "You are a writing assistant. Be warm and concise." },
          {
            role: "prompt",
            text: "Write an Instagram caption where Dev Marlow says our cold brew is the only coffee he drinks.",
          },
        ],
        {
          modality: "text",
          kind: "endorsement",
          purpose: "advertising",
          commercial: true,
          channels: ["social"],
          territories: ["US"],
        },
      );
      ```

      ```python Python theme={null}
      endorsement = check(
          [
              {"role": "system", "text": "You are a writing assistant. Be warm and concise."},
              {
                  "role": "prompt",
                  "text": "Write an Instagram caption where Dev Marlow says our cold brew is the only coffee he drinks.",
              },
          ],
          {
              "modality": "text",
              "kind": "endorsement",
              "purpose": "advertising",
              "commercial": True,
              "channels": ["social"],
              "territories": ["US"],
          },
      )
      ```
    </CodeGroup>

    Dev Marlow's posture prohibits every commercial use. There is no remedy, because no license
    can be requested:

    ```json theme={null}
    {
      "id": "chk_d3v9m4r1w6q2k8n5p0s7t3x1za",
      "object": "check",
      "livemode": false,
      "status": "denied",
      "decision": "deny",
      "talent": [
        {
          "talent": "tal_2b4d6f8h0j2k4m6n8p0q2r4s6t",
          "name": "Dev Marlow",
          "matched_by": "name",
          "confidence": "high",
          "decision": "deny",
          "license": null
        }
      ],
      "reasons": [
        {
          "code": "talent_prohibits",
          "stage": "check",
          "talent": "tal_2b4d6f8h0j2k4m6n8p0q2r4s6t",
          "term": "talent:tal_2b4d6f8h0j2k4m6n8p0q2r4s6t",
          "sentence": "Dev Marlow does not take part in advertising or endorsements made with AI."
        }
      ],
      "remedy": null,
      "authorization": null
    }
    ```

    Refuse the endorsement and show the sentence. You can still write a caption that does not
    name him or speak for him.
  </Step>

  <Step title="Answer from the decision">
    Route every reply through one function. With `on_escalate: "deny"` a check never returns
    `escalate`, so there are two branches.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      type Reply = { text: string; checkId?: string; instructions?: string[] };

      function planReply(check: any): Reply {
        if (check.decision !== "allow") {
          const sentence = check.reasons[0]?.sentence ?? "This request is not licensed.";
          return { text: `I can't write that. ${sentence}` };
        }
        const instructions = check.obligations
          .filter((o: any) => o.type === "generation_instructions")
          .flatMap((o: any) => o.instructions);
        // Generate with these instructions in the system prompt, then submit the reply as a generation.
        return { text: "", checkId: check.id, instructions };
      }
      ```

      ```python Python theme={null}
      def plan_reply(check: dict) -> dict:
          if check["decision"] != "allow":
              sentence = check["reasons"][0]["sentence"] if check["reasons"] else "This request is not licensed."
              return {"text": f"I can't write that. {sentence}"}
          instructions = [
              line
              for o in check["obligations"]
              if o["type"] == "generation_instructions"
              for line in o["instructions"]
          ]
          # Generate with these instructions in the system prompt, then submit the reply as a generation.
          return {"text": "", "check_id": check["id"], "instructions": instructions}
      ```
    </CodeGroup>
  </Step>
</Steps>

## When a check allows

With a license that covers the use, the check returns `allow`. Generate with every
`generation_instructions` entry in the system prompt, keep the whole reply on your server, and
submit it as a generation before you show any of it. Text returns its decision in the response.

<CodeGroup>
  ```bash cURL theme={null}
  CHECK="<the allowed check ID>"
  REPLY="Here's to June, and to the two of you."

  curl https://app.reflections.ai/v1/generations \
    -H "Authorization: Bearer $REFLECTIONS_KEY" \
    -H "Reflections-Version: 2026-10-15" \
    -H "Idempotency-Key: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d "{
      \"check\": \"$CHECK\",
      \"candidate_index\": 0,
      \"artifact\": {
        \"data\": \"$(printf '%s' "$REPLY" | base64 | tr -d '\n')\",
        \"sha256\": \"$(printf '%s' "$REPLY" | shasum -a 256 | cut -d' ' -f1)\",
        \"media_type\": \"text/plain\"
      },
      \"execution\": { \"provider\": \"example-llm-provider\", \"model\": \"chat-model-4\" }
    }"
  ```

  ```typescript TypeScript theme={null}
  import { createHash } from "node:crypto";

  async function submitReply(checkId: string, reply: string) {
    const bytes = Buffer.from(reply, "utf8");
    const generation = await fetch(`${api}/generations`, {
      method: "POST",
      headers: { ...json, "Idempotency-Key": crypto.randomUUID() },
      body: JSON.stringify({
        check: checkId,
        candidate_index: 0,
        artifact: {
          data: bytes.toString("base64"),
          sha256: createHash("sha256").update(bytes).digest("hex"),
          media_type: "text/plain",
        },
        execution: { provider: "example-llm-provider", model: "chat-model-4" },
      }),
    }).then(ok);
    return generation.decision === "allow" ? reply : null;
  }
  ```

  ```python Python theme={null}
  import base64, hashlib

  def submit_reply(check_id: str, reply: str) -> str | None:
      data = reply.encode("utf-8")
      generation = ok(requests.post(
          f"{API}/generations",
          headers={**auth, "Idempotency-Key": str(uuid.uuid4())},
          json={
              "check": check_id,
              "candidate_index": 0,
              "artifact": {
                  "data": base64.b64encode(data).decode(),
                  "sha256": hashlib.sha256(data).hexdigest(),
                  "media_type": "text/plain",
              },
              "execution": {"provider": "example-llm-provider", "model": "chat-model-4"},
          },
      ))
      return reply if generation["decision"] == "allow" else None
  ```
</CodeGroup>

Show the reply only when the function returns it, with the `disclosure` text if the check carried
one. Do not stream an impersonation reply to the user token by token: the generation check needs
the complete text first.

<Tip>
  Chat messages carry personal data. Set `prompt_handling.mode` to `redacted` after you strip it,
  or keep text out of Reflections with the local [name index](/build/resolving-talent). See
  [Privacy modes](/build/privacy-modes).
</Tip>

## Next

<Columns cols={2}>
  <Card title="LLM assistants" icon="message" href="/guides/llm-assistants">
    Names, personas and knowledge in text, in production.
  </Card>

  <Card title="Resolution" icon="magnifying-glass" href="/concepts/resolution">
    How text is matched to talent, and why no match is not permission.
  </Card>

  <Card title="Decisions" icon="scale-balanced" href="/concepts/decisions">
    `allow`, `deny` and `escalate`, and what each requires.
  </Card>

  <Card title="Binding the request" icon="link" href="/build/binding-the-request">
    Sending the system prompt and context the model receives.
  </Card>
</Columns>


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