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

# Receipt

> The signed proof of a decision, its claims, its JWS and COSE forms, and how to verify it.

<Note>
  Sandbox receipts are signed test JWS objects. COSE serialization and embedded-manifest verification
  below are target contract features. Use the [quickstart](/quickstart) for the supported JSON/JWS
  flow; test receipts do not authorize real talent content.
</Note>

Every decided check and generation has a receipt: a signed statement of what Reflections decided,
under which licenses, about which request or bytes. A receipt verifies offline against the
published keys and never expires. Its live status, which a revocation can change, is at
[`GET /v1/receipts/{receipt_id}`](/api-reference/receipts/retrieve-a-receipt).

A receipt comes in two forms with the same claims: a compact JWS (`jws`), and a COSE\_Sign1
structure (`cose`) for tools that work in CBOR.

## Header

```json theme={null}
{
  "alg": "ES256",
  "kid": "rfl-2026-10-docs3",
  "typ": "tlp-receipt+jwt"
}
```

`typ` is always `tlp-receipt+jwt`. A receipt has no `exp` and no `aud`: it is a record anyone may
check, not a credential.

## Claims

| Claim | Type | Value |
| - | - | - |
| `iss` | string | `https://app.reflections.ai` |
| `jti` | string | The receipt ID, `rcpt_…`. |
| `iat` | number | When the decision was made, in Unix seconds. |
| `sub` | string | The check or generation the receipt is for, `chk_…` or `gen_…`. |
| `tlp.ver` | string | The protocol version, `"1.0"`. |
| `tlp.kind` | string | `check` or `generation`. |
| `tlp.decision` | string | `allow` or `deny`. An escalation gets its receipt when it is decided. |
| `tlp.assurance` | string | `observed`: Reflections checked these bytes or this request itself. `attested`: Reflections recorded what the platform stated, such as text sent `withheld`. |
| `tlp.talent` | string array | The `tal_` IDs the decision covers. |
| `tlp.licenses` | object array | The licenses the decision relied on: one entry per talent whose license applied, in the order of `tlp.talent`. Each entry has `talent`, `license`, `revision` (the `lrv_` ID of the signed revision) and `terms_digest` (that revision's `terms_digest`). Empty when no license applied, as for a check whose inputs invoke no talent or a `deny` with `no_license`. |
| `tlp.request_digest` | string | The request digest of the check, or for a generation, of the check it was made under. |
| `tlp.artifact_sha256` | string | Generations only. The SHA-256, in hex, of the bytes submitted to the generation. |
| `tlp.use` | object | The use the decision covers: `modality`, `kind`, `purpose`, `commercial`, `channels`, `territories`. |
| `tlp.obligations` | object array | The obligations the decision carried, as full objects with their fields, such as a disclosure's `text`. An authorization token lists only their types. |
| `tlp.platform` | string | The `acct_` ID of the platform that made the request. |
| `tlp.licensee` | string or null | The `acct_` ID of the licensee whose license applied. `null` when no license applied. |
| `tlp.c2pa_signer` | object | Generations only, when the platform sent [`c2pa_signer`](/api-reference/generations/create-a-generation): `{ "thumbprint": "sha256:<hex>" }`, the SHA-256 of the DER of the certificate that signs the output's C2PA claim. Absent otherwise; such a receipt is unbound to any file. |

## Example payload

The receipt for a line of speech in Maya Okafor's voice, generated by Northlight under its own voice
license for branded content on audio and the web in the US, and allowed after its generation check:

```json theme={null}
{
  "iss": "https://app.reflections.ai",
  "jti": "rcpt_hk5zba2q8v1m3n7p4r6s9t0wxy",
  "iat": 1792260363,
  "sub": "gen_2q8v1m3n7p4r6s9t0wxyhk5zba",
  "tlp": {
    "ver": "1.0",
    "kind": "generation",
    "decision": "allow",
    "assurance": "observed",
    "talent": ["tal_9t0wxyhk5zba2q8v1m3n7p4r6s"],
    "licenses": [
      {
        "talent": "tal_9t0wxyhk5zba2q8v1m3n7p4r6s",
        "license": "lic_ba2q8v1m3n7p4r6s9t0wxyhk5z",
        "revision": "lrv_7p4r6s9t0wxyhk5zba2q8v1m3n",
        "terms_digest": "sha256:8e0a2c4e6b8d0f2a4c6e8b0d2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a"
      }
    ],
    "request_digest": "sha256:d4f6b8a0c2e4d6f8b0a2c4e6d8f0b2a4c6e8d0f2b4a6c8e0d2f4b6a8c0e2d4f6",
    "artifact_sha256": "3b5d7f9a1c3e5b7d9f1a3c5e7b9d1f3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b5d",
    "use": {
      "modality": "speech",
      "kind": "impersonation",
      "purpose": "branded_content",
      "commercial": true,
      "channels": ["audio", "web"],
      "territories": ["US"]
    },
    "obligations": [
      { "type": "disclosure", "level": "licensed", "placement": "metadata", "text": "AI voice of Maya Okafor, licensed." },
      { "type": "c2pa_manifest" },
      { "type": "usage_report", "units": "seconds", "within": "P1D" }
    ],
    "platform": "acct_7p4r6s9t0wxyhk5zba2q8v1m3n",
    "licensee": "acct_7p4r6s9t0wxyhk5zba2q8v1m3n"
  }
}
```

Northlight holds this license itself, so `platform` and `licensee` are the same account.

A check receipt has `kind: "check"`, a `chk_` subject and no `artifact_sha256`. A `deny` has a
receipt too, so a platform can prove it refused.

This example is unbound. Had Northlight declared its C2PA claim-signing certificate, `tlp` would
also carry, and the receipt object would repeat as `c2pa_signer`:

```json theme={null}
{ "c2pa_signer": { "thumbprint": "sha256:5d2a8c0e4b6f1a3c5e7d9b0f2a4c6e8d0b2f4a6c8e0d2b4f6a8c0e2d4b6f8a1c" } }
```

## COSE form

`cose` on the [receipt object](/api-reference/receipts/retrieve-a-receipt) is a base64
COSE\_Sign1 structure (RFC 9052, CBOR tag 18) carrying the same claims:

| Part | Content |
| - | - |
| Protected header | `alg` (label `1`): `-7`, ES256. `content type` (label `3`): `"application/json"`. `kid` (label `4`): the key ID as bytes, such as `rfl-2026-10-docs3`. |
| Unprotected header | Empty. |
| Payload | The claims above, as UTF-8 JSON. |
| Signature | 64 bytes: ECDSA P-256 over SHA-256, `r` then `s`. |

In CBOR diagnostic notation:

```text theme={null}
18([
  / protected /   << { 1: -7, 3: "application/json", 4: h'72666c2d323032362d3130' } >>,
  / unprotected / {},
  / payload /     h'7b22696174223a313739323236303336332c22697373223a2268747470733a2f2f6170692e7265666c656374696f6e732e6169222c…',
  / signature /   h'3a1a5d0bff92f0c3…'
])
```

The payload is the same bytes as the JWS payload, which for this receipt begin
`{"iat":1792260363,"iss":"https://app.reflections.ai",`.

To verify it, build the `Sig_structure` `["Signature1", protected, h'', payload]`, encode it as
CBOR, and verify the signature over those bytes with the JWKS key whose `kid` matches. The payload
then parses as the same JSON claims as the JWS.

## Verify a receipt

<Steps>
  <Step title="Check the header">
    `alg` is `ES256` and `typ` is `tlp-receipt+jwt`. A receipt with a `kid` that starts with
    `rfl-test-` was made in test mode and proves nothing about live content.
  </Step>

  <Step title="Check the signature">
    Verify it against the key with the same `kid` in the
    [JWKS](/api-reference/formats/discovery#jwks). Retired keys stay published, so an old receipt
    keeps verifying. Do not reject a receipt for its age.
  </Step>

  <Step title="Check the claims">
    `iss` is `https://app.reflections.ai`. Read `tlp.decision`, `tlp.assurance`, `tlp.talent` and
    `tlp.use` to see what was decided and on what evidence. Each entry in `tlp.licenses` names the
    signed revision behind one talent's license. A party that can read the license, such as the
    licensee, compares each entry's `terms_digest` with the `terms_digest` of the revision it names,
    from [`GET /v1/licenses/{license_id}/revisions/{revision_id}`](/api-reference/licenses/retrieve-a-license-revision).
  </Step>

  <Step title="Match it to the content">
    `tlp.artifact_sha256` is the digest of the bytes the platform submitted, before it embedded the
    C2PA manifest, so a delivered file has a different digest. To match a file to its receipt, send
    the file to [`POST /v1/verifications`](/api-reference/verifications/verify-content), which
    reads an embedded manifest first, then matches by exact digest, then by fingerprint and
    watermark. For a receipt read from a manifest, compare `tlp.c2pa_signer.thumbprint` with the
    SHA-256 of the manifest's claim signer certificate (DER). A receipt without `tlp.c2pa_signer`
    proves the decision, not that this file carries it
    ([TLP-RCP-16](/standard/receipts#binding-to-a-c2pa-claim-signer)).
  </Step>

  <Step title="Read the live status">
    The signature proves a past decision. Whether the output may still be used is in `use_status`
    from [`GET /v1/receipts/{receipt_id}`](/api-reference/receipts/retrieve-a-receipt).
  </Step>
</Steps>

## Live status

```json theme={null}
{
  "id": "rcpt_hk5zba2q8v1m3n7p4r6s9t0wxy",
  "object": "receipt",
  "livemode": true,
  "kind": "generation",
  "subject": "gen_2q8v1m3n7p4r6s9t0wxyhk5zba",
  "decision": "allow",
  "assurance": "observed",
  "talent": ["tal_9t0wxyhk5zba2q8v1m3n7p4r6s"],
  "licenses": [
    {
      "talent": "tal_9t0wxyhk5zba2q8v1m3n7p4r6s",
      "license": "lic_ba2q8v1m3n7p4r6s9t0wxyhk5z",
      "license_revision": "lrv_7p4r6s9t0wxyhk5zba2q8v1m3n",
      "terms_digest": "sha256:8e0a2c4e6b8d0f2a4c6e8b0d2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a"
    }
  ],
  "use_status": "permitted",
  "use_status_changed_at": null,
  "jws": "eyJhbGciOiJFUzI1NiIsImtpZCI6InJmbC0yMDI2LTEwLWRvY3MzIiwidHlwIjoidGxwLXJlY2VpcHQrand0In0…",
  "cose": "0oRYIqMBJgNwYXBwbGljYXRpb24vanNvbgRLcmZsLTIwMjYtMTCg…",
  "page_url": "https://app.reflections.ai/r/rcpt_hk5zba2q8v1m3n7p4r6s9t0wxy",
  "issued_at": "2026-10-17T18:06:03Z"
}
```

The receipt object lists the same licenses as the signed `tlp.licenses`, with one difference in
spelling: each entry names its revision `license_revision`, where the claim names it `revision`.

| `use_status` | Meaning |
| - | - |
| `permitted` | Use within the receipt's scope is permitted. |
| `ceased` | After a revocation: no new placements. |
| `withdrawn` | After a revocation for cause: take the output down. |
| `expired` | The use window ended. |
| `null` | A `deny` receipt. It permits no use. |

Anyone can read a receipt, with any key or none, and see it as a page at
`https://app.reflections.ai/r/{receipt_id}`. Discovery publishes that template as `receipt_page`;
`receipt_endpoint` remains the JSON route at `/v1/receipts/{receipt_id}`. A test page is explicitly
fictional and grants no production rights. An unavailable human page contains no receipt facts;
protocol clients use the JSON endpoint's HTTP 503 response.

## Related

<Columns cols={2}>
  <Card title="C2PA assertion" icon="receipt" href="/api-reference/formats/c2pa-assertion">
    Where the receipt goes in the output's manifest.
  </Card>

  <Card title="Verifying content" icon="shield-check" href="/build/verifying-content">
    Verification by receipt, manifest or file.
  </Card>

  <Card title="Provenance" icon="receipt" href="/concepts/provenance">
    Receipts, manifests, fingerprints and verification.
  </Card>

  <Card title="Receipts in the Standard" icon="book" href="/standard/receipts">
    Receipt format, signing keys and key rotation.
  </Card>
</Columns>


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