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

# Usage and settlement

> Usage records, reconciliation against observed use, monthly statements, payments and audit.

<Note>
  Planned protocol reference. Usage reconciliation, statements and payment recording are not
  served by the current Talent API sandbox.
  Start with the [runnable quickstart](/quickstart) and [sandbox support matrix](/sandbox).
</Note>

Licenses that carry compensation are settled from usage your platform reports. Reflections
reconciles each report against its own record of checks and generations, and computes a monthly
statement per license. Reflections never moves money: the licensee pays the talent's payee
directly and records the payment.

## Usage records

Report usage with [`POST /v1/usage_records`](/api-reference/usage-records/report-usage), up to 1,000
records per call. Each record names what it settles:

| Field | Meaning |
| - | - |
| `subject` | The generation (`gen_…`) or session (`ses_…`) this record settles |
| `units` | `outputs`, `seconds`, `characters`, `placements` or `impressions` |
| `quantity` | How many units |
| `occurred_at` | When the use happened |
| `placement` | For `placements` and `impressions`: the `channel`, `territory` and `url` |
| `correction_of` | The `ur_…` record this one replaces |

The units come from the license: the `usage_report` obligation names the `units` to report and the
time `within` which to report them. Send each call with an `Idempotency-Key`, and retry an unknown
outcome with the same key.

## Reconciliation

Each record is accepted or rejected on its own. A rejected record carries a `rejection.code`:

| Code | Meaning |
| - | - |
| `unknown_subject` | No generation or session with that ID under your licenses |
| `exceeds_observed` | More units than Reflections recorded for that subject |
| `outside_scope` | The placement is outside the license's channels or territories |
| `duplicate` | The subject's usage was already reported |
| `period_closed` | The statement for that period was already issued |

Reflections records outputs, seconds and characters itself, from the checks and generations it
decided, so under-reporting is visible without trusting the report. Placements and impressions
happen after delivery, where Reflections cannot see them; for those units it relies on what you
report.

To fix a record, send a new one with `correction_of` set to the record it replaces.

## Statements

A **statement** covers one license for one month. You receive
[`statement.issued`](/api-reference/webhooks/statement-issued) when it is final in the proposed
workflow. Daily test statements are planned; the current sandbox produces no statements.

| Field | Meaning |
| - | - |
| `status` | `open` (the month is running), `issued` (final), `paid` (the talent's side confirmed payment) or `disputed` |
| `lines[]` | Per unit: `reported` by you, `observed` by Reflections, and `billable`, the greater of the two, with its `rate` and `amount` |
| `amount_due`, `amount_paid` | Integers in the currency's smallest unit |
| `reconciliation.result` | `matched`, `under_reported` or `over_reported` |
| `due_at`, `document_url` | When payment is due, and the statement as a document |

Amounts follow the license's `compensation`: a flat fee, a per-unit rate, a revenue share, a
minimum and a schedule. See [Licenses](/concepts/licenses#compensation).

## Payments

The licensee pays the talent's payee by its own means, then records the payment with
[`POST /v1/statements/{statement_id}/payments`](/api-reference/statements/record-a-payment):
`amount`, `currency`, `paid_at`, `method` (`bank_transfer`, `card`, `check` or `other`) and your
`reference`. The talent's side confirms receipt in the Talent Portal or with
[`POST /v1/statements/{statement_id}/payments/{payment_id}/confirm`](/api-reference/statements/confirm-a-payment),
which sets `confirmed_at` on the payment. Until then, a recorded payment is what you stated.

If the talent's side finds a statement wrong, it
[disputes it](/api-reference/statements/dispute-a-statement): the statement becomes `disputed`,
with `dispute.reason`, and you receive [`statement.disputed`](/api-reference/webhooks/statement-disputed).
Correct your usage records or record the payment, and the statement is reissued.

## Audit

* The talent, and representatives with `receive_statements`, read every usage record and generation
  under their licenses.
* Receipts, usage records and statements are kept 7 years.
* Licensees keep their own usage logs for 3 years.
* On a talent's audit request, Reflections may ask your platform for its generation logs for a
  stated period.

## Example: a month of voice lines

Northlight's voice license pays Maya Okafor per second of speech. After a 3.4-second line is allowed,
Northlight reports it:

<CodeGroup>
  ```bash cURL theme={null}
  curl https://app.reflections.ai/v1/usage_records \
    -H "Authorization: Bearer rk_test_…" \
    -H "Reflections-Version: 2026-10-15" \
    -H "Idempotency-Key: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d '{
      "records": [
        { "subject": "gen_2q8v1m3n7p4r6s9t0wxyhk5zba", "units": "seconds", "quantity": 3.4, "occurred_at": "2026-10-17T18:06:03Z" }
      ]
    }'
  ```

  ```typescript TypeScript theme={null}
  const records = await fetch("https://app.reflections.ai/v1/usage_records", {
    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({
      records: [
        { subject: "gen_2q8v1m3n7p4r6s9t0wxyhk5zba", units: "seconds", quantity: 3.4, occurred_at: "2026-10-17T18:06:03Z" },
      ],
    }),
  }).then((r) => r.json());
  ```

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

  records = requests.post(
      "https://app.reflections.ai/v1/usage_records",
      headers={
          "Authorization": f"Bearer {os.environ['REFLECTIONS_KEY']}",
          "Reflections-Version": "2026-10-15",
          "Idempotency-Key": str(uuid.uuid4()),
      },
      json={
          "records": [
              {"subject": "gen_2q8v1m3n7p4r6s9t0wxyhk5zba", "units": "seconds", "quantity": 3.4, "occurred_at": "2026-10-17T18:06:03Z"}
          ]
      },
  ).json()
  ```
</CodeGroup>

```json theme={null}
{
  "object": "list",
  "data": [
    {
      "id": "ur_9m1p3r5t7v9x1z3b5d7f9h1k3n",
      "object": "usage_record",
      "status": "accepted",
      "rejection": null,
      "subject": "gen_2q8v1m3n7p4r6s9t0wxyhk5zba",
      "units": "seconds",
      "quantity": 3.4,
      "occurred_at": "2026-10-17T18:06:03Z",
      "license": "lic_ba2q8v1m3n7p4r6s9t0wxyhk5z",
      "statement": "stmt_8n0q2s4w6y8a0c2e4g6j8m0p2r"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

September's statement for the same license shows a small gap between what Northlight reported and
what Reflections observed:

```json theme={null}
{
  "id": "stmt_5p7r9t1v3x5z7b9d1f3h5k7m9q",
  "object": "statement",
  "license": "lic_ba2q8v1m3n7p4r6s9t0wxyhk5z",
  "status": "issued",
  "period": { "start": "2026-09-01T00:00:00Z", "end": "2026-10-01T00:00:00Z" },
  "currency": "usd",
  "lines": [
    {
      "description": "Speech in Maya Okafor's voice",
      "units": "seconds",
      "reported": 41200,
      "observed": 41236,
      "billable": 41236,
      "rate": { "amount": 2, "currency": "usd" },
      "amount": 82472
    }
  ],
  "amount_due": 82472,
  "amount_paid": 0,
  "reconciliation": { "result": "under_reported", "detail": "36 seconds observed in generations with no usage record." },
  "due_at": "2026-10-31T00:00:00Z"
}
```

The billable amount uses the observed figure. Northlight pays \$824.72 to Maya Okafor's payee and
records the payment against the statement.

## Related

<Columns cols={2}>
  <Card title="Reporting usage" icon="code" href="/build/reporting-usage">
    Batching, units, reconciliation and corrections.
  </Card>

  <Card title="Licenses" icon="file-signature" href="/concepts/licenses">
    Compensation terms.
  </Card>

  <Card title="Brands and agencies" icon="briefcase" href="/guides/brands-and-agencies">
    Reading statements and recording payments.
  </Card>
</Columns>


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