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

# Privacy

> What your platform sends, what Reflections keeps and for how long, and the prompt modes that send less.

<Note>
  The sandbox is for synthetic test data. The initial live image implementation has the local
  retention limits below in its deployed code. Signing activation, observed purges and supplier
  processing guarantees require separate evidence. Broader protocol features remain planned.
  Start with the [runnable quickstart](/quickstart) and [sandbox support matrix](/sandbox).
</Note>

A check needs to see the request a model will receive, and a generation check needs the output.
This page lists what you send, how long Reflections keeps each kind of data, and how to send less
when your product requires it.

## Initial live image implementation

The [initial live image journey](/api-reference/introduction#initial-live-image-implementation)
accepts full text inputs and one inline PNG. It does not accept end-user IDs, redacted or withheld
prompt modes, `retention: "none"`, uploaded files, reference exports or sessions.

These limits apply to Reflections-managed API records and copies:

| Data | Local retention |
| - | - |
| Raw check request text | Deleted when the check becomes terminal, including denials; expired requests are also purged |
| Private check and generation details, including execution and metadata | While pending, then 24 hours after the terminal boundary; a check remains linked to its pending generation. Detail reads return `404` after this window |
| Idempotency key and full-body retry digest | 24 hours from creation |
| Unclaimed submitted PNG and derivatives | At most 24 hours from intake |
| Image awaiting talent release | Seven-day review window, with at most 24 further hours; a decision can shorten that deadline to 24 hours after the decision |
| Evaluator observations and diagnostics | 30 days after the terminal boundary; no raw provider response or evaluator prose is retained |
| Compact provenance, including request, terms and artifact digests | Through the signed license end plus 3 years |
| Signed receipts and usage records | 7 years |
| Generation summaries listed by `GET /v1/generations?license=…` (status, decision, receipt ID, release status, times) | Kept with the compact provenance record; no deletion is scheduled. They stay listable after the private details window, when the generation's detail read returns `404` |

Public receipts and receipt verification expose a defined signed projection and current use
status. They do not expose prompt text, execution job IDs or private metadata. Digest lookup
requires the owning account's live key. Private files can expire before their provenance or
receipt records; a retained receipt does not restore the file.

Reflections' deletion of its own records does not prove deletion by an upstream evaluator or
model provider. Supplier agreements, retention settings and processing evidence must be verified
separately before activation. No upstream account-level zero-data-retention guarantee has been
verified for this implementation.

## Broader protocol: what you send

| Data | Sent in | Used to |
| - | - | - |
| Prompt, system text, rewritten prompt, scripts, conversation context | Check `inputs` | Find the talent and check the talent's rules |
| References, voice samples, masks | Check `inputs` | Match faces and voices, and check licensed changes |
| Outputs | Generation `artifact` | Check the license's rules on the bytes |
| Audio windows, transcripts, frames | Session samples | Check a live session |
| End-user ID | `end_user.id` | Attribute each use, and apply per-end-user limits |

Send the end user as a stable ID scoped to your platform, never an email or a name. Reflections
stores only an HMAC of it, and authorization tokens carry that HMAC in the `eu` claim. Never put
personal data in `metadata`.

## Broader protocol retention limits

| Data | Kept |
| - | - |
| Prompt text on `allow` | Deleted once the check is decided; the receipt keeps the `request_digest` |
| Prompt text on `deny` or `escalate` | 30 days, for review and disputes |
| Artifacts on `allow` | Deleted within 24 hours; `sha256` and fingerprint kept for the license term plus 3 years |
| Artifacts held for release | Until release or expiry, plus 30 days |
| Embeddings of references and voice samples | Not stored unless they match; matches kept 90 days |
| Receipts, usage records, statements | 7 years |
| End-user HMACs | 13 months |
| Talent face and voice templates | While protection is on; deleted within 30 days after |

Uploaded files expire after 24 hours unless a held generation needs them, and events are kept 30
days.

With `prompt_handling.retention: "none"`, prompt text is deleted as soon as the check is decided,
whatever the decision. A reviewer of an escalation and anyone handling a later dispute then see the
structured `use` and the digests, not the text.

## Broader protocol prompt modes

`prompt_handling.mode` sets how much prompt text a check receives.

| Mode | What Reflections receives | What it costs you |
| - | - | - |
| `full` (default) | The text as your model receives it | Nothing extra; every rule is checked before you generate |
| `redacted` | The text after you removed personal data | Your redaction step; the check decides on the redacted text |
| `withheld` | No text: only structured fields and media digests | Rules that need the text are checked on the output instead, so you may pay for a generation that is then denied |

**Redacted.** Remove personal data about anyone other than the talent: email addresses, phone
numbers, street addresses, account numbers, and the names of private people. Keep the talent's name
and every word that describes what the output should show, because the talent's rules are checked
against them. [Privacy modes](/build/privacy-modes) gives the patterns.

**Withheld.** With no text, Reflections cannot find a talent from the prompt, follow an indirect
reference or match a reference image it only knows by digest. Match names on your side with the
[name index](/concepts/resolution#the-name-index) and pass the talent in `talent`. Scope terms such
as channels, territories and purpose are still checked at the check, because they come from `use`.
The talent's own rules are checked on the output.

## Who is responsible

The protocol assigns the following responsibilities; this text is not evidence that a particular
supplier agreement or production processing setup has been qualified.

For prompts and outputs, Reflections is a processor for your platform under a data processing
agreement. For talent identity data, such as face and voice templates, Reflections is a controller
acting for the talent.

## Example: a birthday message

A Northlight user asks for "Maya Okafor wishing my sister Dana Whitfield a happy birthday, her
number is 555-0142". Northlight removes the private name and the number, and asks Reflections not to
keep the text:

<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 '{
      "talent": ["tal_9t0wxyhk5zba2q8v1m3n7p4r6s"],
      "facet": "fct_v1m3n7p4r6s9t0wxyhk5zba2q8",
      "inputs": [{ "role": "script", "text": "Happy birthday, [name]! Have a great run today." }],
      "use": {
        "modality": "speech", "kind": "impersonation", "purpose": "personal",
        "commercial": false, "channels": ["in_app"], "territories": ["US"]
      },
      "end_user": { "id": "usr_48213" },
      "prompt_handling": { "mode": "redacted", "retention": "none" }
    }'
  ```

  ```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({
      talent: ["tal_9t0wxyhk5zba2q8v1m3n7p4r6s"],
      facet: "fct_v1m3n7p4r6s9t0wxyhk5zba2q8",
      inputs: [{ role: "script", text: "Happy birthday, [name]! Have a great run today." }],
      use: {
        modality: "speech", kind: "impersonation", purpose: "personal",
        commercial: false, channels: ["in_app"], territories: ["US"],
      },
      end_user: { id: "usr_48213" },
      prompt_handling: { mode: "redacted", retention: "none" },
    }),
  }).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={
          "talent": ["tal_9t0wxyhk5zba2q8v1m3n7p4r6s"],
          "facet": "fct_v1m3n7p4r6s9t0wxyhk5zba2q8",
          "inputs": [{"role": "script", "text": "Happy birthday, [name]! Have a great run today."}],
          "use": {
              "modality": "speech", "kind": "impersonation", "purpose": "personal",
              "commercial": False, "channels": ["in_app"], "territories": ["US"],
          },
          "end_user": {"id": "usr_48213"},
          "prompt_handling": {"mode": "redacted", "retention": "none"},
      },
  ).json()
  ```
</CodeGroup>

The check is decided on the redacted script, the text is deleted once it is decided, and the
receipt keeps only the `request_digest`. Northlight fills in the sister's name only in what it sends
to the voice model, and still submits the output as a generation, where the spoken words are
checked.

## Related

<Columns cols={2}>
  <Card title="Privacy modes" icon="code" href="/build/privacy-modes">
    Full, redacted and withheld prompts in practice.
  </Card>

  <Card title="Resolution" icon="magnifying-glass" href="/concepts/resolution">
    Matching names locally with the name index.
  </Card>

  <Card title="Privacy in the Standard" icon="book" href="/standard/privacy">
    Data minimization on both sides.
  </Card>
</Columns>


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