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

# Brands and agencies

> Hold licenses from talent, accept them, connect the platforms you generate on, request new terms, and settle statements.

<Note>
  Planned Talent API integration guide. License acceptance, OAuth connections, license requests
  and settlement are not served through the current sandbox. This reference does not describe
  the separate portal’s current capabilities.
  Start with the [runnable quickstart](/quickstart) and [sandbox support matrix](/sandbox).
</Note>

A brand or agency is the licensee: the account that holds a talent's license and answers for every
use made under it. You do not generate through Reflections. You generate on platforms you connect,
and they check each use against your licenses. This guide covers accepting and reading licenses,
connecting platforms, asking for licenses, and statements.

An ad agency that signs licenses for a client is the licensee and works exactly as below. A
talent agency or manager that acts for talent is a representative; see
[Talent representatives](/guides/talent-representatives).

## Accept a license

A license starts with the talent's signature, in the Talent Portal. When the talent or their
representative signs one for you, you receive `license.issued` and the license is
`pending_acceptance`. Its `acceptance_url` is the hosted page where you read the signed document
and accept it. No endpoint can accept a license; a person on your side does it on that page.

```json theme={null}
{
  "id": "lic_k5zba2q8v1m3n7p4r6s9t0wxyh",
  "object": "license",
  "status": "pending_acceptance",
  "talent": "tal_9t0wxyhk5zba2q8v1m3n7p4r6s",
  "licensee": "acct_2q8v1m3n7p4r6s9t0wxyhk5zba",
  "issued_by": { "party": "talent", "account": null },
  "handle": "K7Q4M2",
  "current_revision": "lrv_p4r6s9t0wxyhk5zba2q8v1m3n7",
  "revision_number": 1,
  "terms_digest": "sha256:39f18c8bef02b5226b9feaa92e526ed30b81b91650d8a10b503281b169f44431",
  "acceptance_url": "https://app.reflections.ai/l/K7Q4M2/accept"
}
```

Until you accept, checks under it deny with `license_pending_acceptance`. After you accept, you
receive `license.accepted` and the license is `active`, or `scheduled` if its window has not
started. If you decline, you receive `license.declined`. The `handle` is a six-character name
people can read aloud on a call: Ridgeline's license from Maya Okafor is `K7Q4M2`.

## Read the terms

[`GET /v1/licenses/{license_id}`](/api-reference/licenses/retrieve-a-license) returns the license
with `scope`, the terms in normalized form. The signed document itself is a revision:
[`GET /v1/licenses/{license_id}/revisions/{revision_id}`](/api-reference/licenses/retrieve-a-license-revision)
returns `terms` exactly as signed, its `terms_digest`, the `signatures`, and `enforcement`, which
says for each term whether Reflections enforces it.

| `enforcement` | Meaning | Examples |
| - | - | - |
| `checked` | Reflections enforces it at the check, the generation or the session | Channels, territories, the talent's rules, `identity.samePerson` |
| `recorded` | Signed and shown, not enforced | `exclusivity`, `conflicts` |

You keep recorded terms yourself. If Maya Okafor's license names a competitor she must not appear
beside, no check stops that placement. See [What we enforce](/concepts/what-we-enforce).

To confirm you hold the signed document, compute the digest yourself: SHA-256 over the
`tlp-json-1` form of the revision's `terms`. [The terms digest](/concepts/licenses#the-terms-digest)
gives the rules and code that reproduces it. A plain `json.dumps(..., sort_keys=True)` is not
enough: it writes `1.0` and `1e-07` where `tlp-json-1` writes `1` and `1e-7`.

### Revisions

Each signed version of a license is a revision with its own `terms_digest`, and its terms never
change. A change request, an amendment or a renewal produces a new one. When the talent's side
signs it, you receive `license.revision_pending` with `pending_revision` naming it, and you accept
or decline it at the license's `acceptance_url`. The current revision stays in force until you
accept. Then you receive `license.revised` with `current_revision` pointing at the new revision,
and new checks use it at once. Authorizations already issued keep the revision they name, and every receipt names the
revision and digest its output was made under, so you can always show which terms applied.

## Connect the platforms you generate on

A platform can use your licenses only after you connect it. On the platform, choose to connect
your Reflections account. You are sent to Reflections, where you sign in and see the platform's
name, the scopes it asks for and which of your licenses it may use: all of them, or the ones you
list. When you approve, the platform receives `connection.created` and sends checks for you with
your account ID in `Reflections-Account`.

| Connection scope | Lets the platform |
| - | - |
| `checks:write`, `generations:write`, `sessions:write` | Check uses and outputs under your licenses |
| `licenses:read` | Read the licenses you let it use |
| `license_requests:write` | Ask talent for licenses on your behalf |
| `usage:write` | Report usage under your licenses |

List your connections, and end one when you stop working with a platform:

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://app.reflections.ai/v1/connections?role=licensee" \
    -H "Authorization: Bearer $REFLECTIONS_KEY" \
    -H "Reflections-Version: 2026-10-15"

  curl -X DELETE https://app.reflections.ai/v1/connections/con_av0baahbfheftkzxqbkns02qwf \
    -H "Authorization: Bearer $REFLECTIONS_KEY" \
    -H "Reflections-Version: 2026-10-15"
  ```

  ```typescript TypeScript theme={null}
  const headers = { Authorization: `Bearer ${process.env.REFLECTIONS_KEY}`, "Reflections-Version": "2026-10-15" };

  const connections = await fetch("https://app.reflections.ai/v1/connections?role=licensee", { headers }).then((r) =>
    r.json(),
  );

  await fetch("https://app.reflections.ai/v1/connections/con_av0baahbfheftkzxqbkns02qwf", { method: "DELETE", headers });
  ```

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

  headers = {"Authorization": f"Bearer {os.environ['REFLECTIONS_KEY']}", "Reflections-Version": "2026-10-15"}

  connections = requests.get(
      "https://app.reflections.ai/v1/connections", headers=headers, params={"role": "licensee"}
  ).json()

  requests.delete("https://app.reflections.ai/v1/connections/con_av0baahbfheftkzxqbkns02qwf", headers=headers)
  ```
</CodeGroup>

From that moment, the platform's checks for you fail with `account_not_connected`, and
authorizations it already holds stop working when they expire. A license can also restrict
platforms itself: with `scope.platforms.mode` set to `listed`, only the accounts it lists may run
checks under it.

## Request a license

When a check for you is denied for lack of a license, its `remedy` says what to ask for:

| `remedy.type` | Request |
| - | - |
| `license_request` | `kind: "new"` to `remedy.talent` |
| `license_change` | `kind: "change"` for `remedy.license` |

Pass the denied check's ID as `check` so the talent sees the use that prompted the request, use
`remedy.suggested_scope` as a starting `requested_scope`, and add an `offer` and a `note`. A
connected platform can send the request for you with `license_requests:write`.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://app.reflections.ai/v1/license_requests \
    -H "Authorization: Bearer $REFLECTIONS_KEY" \
    -H "Reflections-Version: 2026-10-15" \
    -H "Idempotency-Key: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d '{
      "kind": "new",
      "talent": "tal_7q9s1v3w5y7a9c1e3g5h7k9m1n",
      "check": "chk_9r1s3t5v7w9x1y3z5a7b9c1d3e",
      "requested_scope": {
        "facet_types": ["likeness"],
        "modalities": ["image"],
        "kinds": ["depiction"],
        "purposes": ["advertising"],
        "commercial": true,
        "channels": ["social", "web"],
        "territories": ["US"],
        "starts_at": "2026-11-01T00:00:00Z",
        "ends_at": "2027-01-31T23:59:59Z",
        "final_deliverables": 20
      },
      "offer": { "type": "flat", "flat": { "amount": 1500000, "currency": "usd" }, "schedule": "on_acceptance" },
      "note": "Winter trail campaign, three months, social and our site."
    }'
  ```

  ```typescript TypeScript theme={null}
  const request = await fetch("https://app.reflections.ai/v1/license_requests", {
    method: "POST",
    headers: { ...headers, "Idempotency-Key": crypto.randomUUID(), "Content-Type": "application/json" },
    body: JSON.stringify({
      kind: "new",
      talent: "tal_7q9s1v3w5y7a9c1e3g5h7k9m1n",
      check: "chk_9r1s3t5v7w9x1y3z5a7b9c1d3e",
      requested_scope: {
        facet_types: ["likeness"],
        modalities: ["image"],
        kinds: ["depiction"],
        purposes: ["advertising"],
        commercial: true,
        channels: ["social", "web"],
        territories: ["US"],
        starts_at: "2026-11-01T00:00:00Z",
        ends_at: "2027-01-31T23:59:59Z",
        final_deliverables: 20,
      },
      offer: { type: "flat", flat: { amount: 1500000, currency: "usd" }, schedule: "on_acceptance" },
      note: "Winter trail campaign, three months, social and our site.",
    }),
  }).then((r) => r.json());
  ```

  ```python Python theme={null}
  request = requests.post(
      "https://app.reflections.ai/v1/license_requests",
      headers={**headers, "Idempotency-Key": str(uuid.uuid4())},
      json={
          "kind": "new",
          "talent": "tal_7q9s1v3w5y7a9c1e3g5h7k9m1n",
          "check": "chk_9r1s3t5v7w9x1y3z5a7b9c1d3e",
          "requested_scope": {
              "facet_types": ["likeness"],
              "modalities": ["image"],
              "kinds": ["depiction"],
              "purposes": ["advertising"],
              "commercial": True,
              "channels": ["social", "web"],
              "territories": ["US"],
              "starts_at": "2026-11-01T00:00:00Z",
              "ends_at": "2027-01-31T23:59:59Z",
              "final_deliverables": 20,
          },
          "offer": {"type": "flat", "flat": {"amount": 1500000, "currency": "usd"}, "schedule": "on_acceptance"},
          "note": "Winter trail campaign, three months, social and our site.",
      },
  ).json()
  ```
</CodeGroup>

```json theme={null}
{
  "id": "lrq_pdgtby01k8ejkffqkpdwdv9w7b",
  "object": "license_request",
  "kind": "new",
  "status": "sent",
  "talent": "tal_7q9s1v3w5y7a9c1e3g5h7k9m1n",
  "licensee": "acct_2q8v1m3n7p4r6s9t0wxyhk5zba",
  "check": "chk_9r1s3t5v7w9x1y3z5a7b9c1d3e",
  "license": null,
  "license_revision": null
}
```

A request asks; it does not license anything. The talent, in the Talent Portal, or their
representative answers it and may change any term. If they sign, you receive
`license_request.signed`, whose `license_revision` is what they signed, then `license.issued` for a
new license or `license.revision_pending` for a change. Accept either on its acceptance page as
above. If they decline, you receive `license_request.declined` with their `decision_note`.
Withdraw a request they have not answered with
[`POST /v1/license_requests/{request_id}/withdraw`](/api-reference/license-requests/withdraw-a-license-request).

A `deny` with `talent_prohibits` has no remedy: the talent's posture rules the use out and no
license can be requested. A talent whose `posture.accepts_requests` is `false` takes no requests
through the API.

The proposed [license-request signing helper](/api-reference/test-helpers/sign-a-license-request)
would exercise signing and acceptance with fictional talent. License requests and this helper
are not served by the current sandbox.

## Statements and payments

Licenses with compensation are settled from statements. Each month you receive `statement.issued`
for each license you hold, and
[`GET /v1/statements?license=…`](/api-reference/statements/list-statements) lists them.

```json theme={null}
{
  "id": "stmt_8k2m4p6r8t0w2y4a6c8e0g2j4m",
  "object": "statement",
  "license": "lic_k5zba2q8v1m3n7p4r6s9t0wxyh",
  "status": "issued",
  "period": { "start": "2026-09-01T00:00:00Z", "end": "2026-10-01T00:00:00Z" },
  "currency": "usd",
  "lines": [
    { "description": "Delivered images", "units": "outputs", "reported": 38, "observed": 40, "billable": 40, "rate": { "amount": 5000, "currency": "usd" }, "amount": 200000 }
  ],
  "amount_due": 200000,
  "amount_paid": 0,
  "reconciliation": { "result": "under_reported", "detail": "38 outputs reported, 40 observed." },
  "due_at": "2026-10-31T00:00:00Z"
}
```

Each line bills the greater of what your platforms reported and what Reflections observed from
checks and generations. Reflections never moves money: you pay the talent's payee directly, then
record the payment so both sides see the same balance.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://app.reflections.ai/v1/statements/stmt_8k2m4p6r8t0w2y4a6c8e0g2j4m/payments \
    -H "Authorization: Bearer $REFLECTIONS_KEY" \
    -H "Reflections-Version: 2026-10-15" \
    -H "Idempotency-Key: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d '{ "amount": 200000, "currency": "usd", "paid_at": "2026-10-20T15:00:00Z", "method": "bank_transfer", "reference": "WIRE-55120" }'
  ```

  ```typescript TypeScript theme={null}
  await fetch("https://app.reflections.ai/v1/statements/stmt_8k2m4p6r8t0w2y4a6c8e0g2j4m/payments", {
    method: "POST",
    headers: { ...headers, "Idempotency-Key": crypto.randomUUID(), "Content-Type": "application/json" },
    body: JSON.stringify({
      amount: 200000,
      currency: "usd",
      paid_at: "2026-10-20T15:00:00Z",
      method: "bank_transfer",
      reference: "WIRE-55120",
    }),
  });
  ```

  ```python Python theme={null}
  requests.post(
      "https://app.reflections.ai/v1/statements/stmt_8k2m4p6r8t0w2y4a6c8e0g2j4m/payments",
      headers={**headers, "Idempotency-Key": str(uuid.uuid4())},
      json={"amount": 200000, "currency": "usd", "paid_at": "2026-10-20T15:00:00Z", "method": "bank_transfer", "reference": "WIRE-55120"},
  )
  ```
</CodeGroup>

The payment appears in `payments` with `id` `pay_vav8qg9xm8hqznbhb4xmm1k47f`. When the talent's
side confirms receipt, the payment gets `confirmed_at`. The statement becomes `paid` once
confirmed payments reach `amount_due`. See [Usage and settlement](/concepts/usage-and-settlement).

## When a talent narrows consent

A talent or representative can suspend a license during a dispute (`license.suspended`; checks
deny with `license_suspended` until `license.resumed`) or revoke it (`license.revoked`, with a
`revocation.created` for your platforms). What happens to outputs already released follows the
license's `post_revocation` term: `retain` keeps them usable within their scope and window,
`cease_distribution` stops new placements, and `withdraw` requires a takedown, only after a
revocation for cause. See [Revocation](/concepts/revocation).

## Related

<Columns cols={2}>
  <Card title="Licenses" icon="file-signature" href="/concepts/licenses">
    Revisions, digests and the terms a license can carry.
  </Card>

  <Card title="Parties" icon="users" href="/concepts/parties">
    Who answers for what: talent, licensees, platforms and end users.
  </Card>

  <Card title="Usage and settlement" icon="receipt" href="/concepts/usage-and-settlement">
    Usage records, reconciliation and statements.
  </Card>

  <Card title="Image and video platforms" icon="image" href="/guides/image-and-video-platforms">
    What the platforms you connect do with your licenses.
  </Card>
</Columns>


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