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

# Embed provenance

> Write the receipt into the output's C2PA manifest as the ai.reflections.license assertion, and place the disclosure label.

<Note>
  Protocol integration design. The sandbox returns a signed test receipt and a C2PA assertion
  payload; your tool must embed and sign the manifest itself. Hosted manifest verification and
  production conformance certification are not available.
  Start with the [runnable quickstart](/quickstart) and [sandbox support matrix](/sandbox).
</Note>

An allowed generation returns a `c2pa` object: the receipt, ready to add to the C2PA manifest you
sign for the output. You add it next to a `c2pa.actions` entry that declares the output as AI
generated, sign the manifest with your own certificate, and show the disclosure label. Your
platform stays the C2PA claim signer. The receipt inside is signed by Reflections, so it verifies
on its own, whoever signed the manifest.

## What you receive

The generation carries the assertion and the obligations that ask for it:

```json theme={null}
{
  "id": "gen_ma0v5r8f639er6w335xeq9516f",
  "object": "generation",
  "status": "released",
  "decision": "allow",
  "obligations": [
    { "type": "disclosure", "level": "brand", "placement": "both", "text": "Made with AI. Maya Okafor's likeness used under license by Ridgeline." },
    { "type": "c2pa_manifest" }
  ],
  "receipt": "rcpt_ck6nw6q1j9e5wv2r6nw540m4w0",
  "c2pa": {
    "label": "ai.reflections.license",
    "data": {
      "receipt": "eyJhbGciOiJFUzI1NiIsImtpZCI6InJmbC10ZXN0LTIwMjYtMTAtZG9jczMiLCJ0eXAiOiJ0bHAtcmVjZWlwdCtqd3QifQ…",
      "verify_url": "https://app.reflections.ai/r/rcpt_ck6nw6q1j9e5wv2r6nw540m4w0"
    }
  }
}
```

`c2pa` is present once the decision is `allow`: in the generation response, in
[`generation.allowed`](/api-reference/webhooks/generation-allowed) for outputs checked
asynchronously, and in [`generation.released`](/api-reference/webhooks/generation-released) for held
outputs. You can read it again at any time with
[`GET /v1/generations/{generation_id}`](/api-reference/generations/retrieve-a-generation).

## Order of operations

<Steps>
  <Step title="Finish the bytes">
    Resize, re-encode, and draw any visible label into the pixels before you submit the
    generation. The receipt's `artifact_sha256` is the digest of the bytes you submitted.
  </Step>

  <Step title="Submit and wait for allow">
    Create the generation with `c2pa_signer`: the thumbprint of the certificate you will sign the
    manifest with. A held or asynchronous output has no `c2pa` until it is allowed.
  </Step>

  <Step title="Write the manifest">
    Add a `c2pa.actions` assertion and the `c2pa` object, unchanged, to the manifest definition.
  </Step>

  <Step title="Sign with your certificate">
    Sign the manifest as you sign any C2PA claim, and embed it in the file.
  </Step>

  <Step title="Deliver with the label">
    Show the disclosure text where the obligation's `placement` says, then deliver.
  </Step>
</Steps>

Embed the manifest last. Any edit to the file after signing breaks your C2PA signature, and any
change to the content after the generation means the receipt no longer describes what you deliver.

## Write the manifest

This is a manifest definition in the JSON form that `c2patool` reads (see the
[C2PA specifications](https://c2pa.org/specifications/) for the assertions). It declares the output as
created by a trained algorithm, using the IPTC digital source type
`http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia`, and carries the receipt
assertion exactly as the generation returned it.

```json manifest.json theme={null}
{
  "alg": "es256",
  "private_key": "northlight-c2pa.key",
  "sign_cert": "northlight-c2pa-chain.pem",
  "claim_generator_info": [{ "name": "Northlight", "version": "4.2.0" }],
  "title": "ridge-sunrise.png",
  "assertions": [
    {
      "label": "c2pa.actions",
      "data": {
        "actions": [
          {
            "action": "c2pa.created",
            "digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia",
            "description": "Made with AI. Maya Okafor's likeness used under license by Ridgeline."
          }
        ]
      }
    },
    {
      "label": "ai.reflections.license",
      "data": {
        "receipt": "eyJhbGciOiJFUzI1NiIsImtpZCI6InJmbC10ZXN0LTIwMjYtMTAtZG9jczMiLCJ0eXAiOiJ0bHAtcmVjZWlwdCtqd3QifQ…",
        "verify_url": "https://app.reflections.ai/r/rcpt_ck6nw6q1j9e5wv2r6nw540m4w0"
      }
    }
  ]
}
```

* `private_key` and `sign_cert` are your platform's C2PA signing key and certificate chain. Never
  use a Reflections key: the receipt carries the Reflections signature.
* Sign with the certificate whose thumbprint you sent as `c2pa_signer`. A verifier that checks the
  manifest can reject the assertion in a manifest signed by another certificate. Compute the thumbprint from the first
  (end-entity) certificate in `sign_cert`:
  `openssl x509 -in northlight-c2pa-chain.pem -outform DER | sha256sum`, prefixed `sha256:`.
* `description` on the action holds the disclosure text when the `disclosure` obligation's
  `placement` is `metadata` or `both`.
* Copy `c2pa.label` and `c2pa.data` without changes. The receipt is a signed JWS; a single changed
  character makes it fail verification.

Sign and embed with `c2patool`, the open-source C2PA command-line tool:

```bash theme={null}
c2patool ridge-sunrise.png -m manifest.json -o ridge-sunrise-signed.png
```

In code, the open-source C2PA libraries take the same manifest definition: `c2pa-rs` for Rust,
`c2pa-node` for Node.js and `c2pa-python` for Python. Because the generation's `c2pa` object already
has the `label` and `data` shape of a manifest assertion, you can append it as is:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { writeFile } from "node:fs/promises";

  // node manifest.mjs gen_…: the allowed generation the manifest is for.
  const res = await fetch(`https://app.reflections.ai/v1/generations/${process.argv[2]}`, {
    headers: {
      Authorization: `Bearer ${process.env.REFLECTIONS_KEY}`,
      "Reflections-Version": "2026-10-15",
    },
  });
  if (!res.ok) throw new Error(`Read generation failed: ${res.status}`);
  const generation = await res.json();

  const TRAINED = "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia";
  const disclosure = generation.obligations.find((o: { type: string }) => o.type === "disclosure");

  const action: Record<string, string> = { action: "c2pa.created", digitalSourceType: TRAINED };
  if (disclosure && disclosure.placement !== "visible") action.description = disclosure.text;

  const manifest = {
    alg: "es256",
    private_key: "northlight-c2pa.key",
    sign_cert: "northlight-c2pa-chain.pem",
    claim_generator_info: [{ name: "Northlight", version: "4.2.0" }],
    title: "ridge-sunrise.png",
    assertions: [
      { label: "c2pa.actions", data: { actions: [action] } },
      generation.c2pa, // { label: "ai.reflections.license", data: { receipt, verify_url } }
    ],
  };
  await writeFile("manifest.json", JSON.stringify(manifest, null, 2));
  ```

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

  # python manifest.py gen_…: the allowed generation the manifest is for.
  res = requests.get(
      f"https://app.reflections.ai/v1/generations/{sys.argv[1]}",
      headers={
          "Authorization": f"Bearer {os.environ['REFLECTIONS_KEY']}",
          "Reflections-Version": "2026-10-15",
      },
  )
  res.raise_for_status()
  generation = res.json()

  TRAINED = "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia"
  disclosure = next((o for o in generation["obligations"] if o["type"] == "disclosure"), None)

  action = {"action": "c2pa.created", "digitalSourceType": TRAINED}
  if disclosure and disclosure.get("placement") != "visible":
      action["description"] = disclosure["text"]

  manifest = {
      "alg": "es256",
      "private_key": "northlight-c2pa.key",
      "sign_cert": "northlight-c2pa-chain.pem",
      "claim_generator_info": [{"name": "Northlight", "version": "4.2.0"}],
      "title": "ridge-sunrise.png",
      "assertions": [
          {"label": "c2pa.actions", "data": {"actions": [action]}},
          generation["c2pa"],  # {"label": "ai.reflections.license", "data": {...}}
      ],
  }
  with open("manifest.json", "w") as f:
      json.dump(manifest, f, indent=2)
  ```
</CodeGroup>

## Place the disclosure

The `disclosure` obligation gives the exact `text` and where it goes. Show the text as written; do
not shorten or translate it.

| `placement` | Where the label goes |
| - | - |
| `visible` | On or beside the output, wherever your product shows it |
| `metadata` | In the manifest, as the `description` of the `c2pa.created` action |
| `both` | Both places |

For a visible label:

| Output | Placement |
| - | - |
| Image | A caption directly under the image, or a label drawn on it before you submit the generation |
| Video | A caption in the player, or an on-screen label drawn before you submit the generation |
| Speech or audio | In the player or the message that carries the audio |
| Live session | In the call interface for the whole session |

`level` decides the wording: `licensed` says the person's likeness is licensed, `brand` adds who
licensed it, and `scope` adds the use. The text already reflects the level, so you only place it.

Before release, a held output has no receipt and no `c2pa`. If the generation carries
`draft_watermark`, any preview carries the visible draft mark instead.

## Check your output

Read the manifest back with `c2patool`:

```bash theme={null}
c2patool ridge-sunrise-signed.png
```

The report lists both assertions under your active manifest. The following hosted file
verification is a planned protocol example; the sandbox does not accept it. For a runnable
receipt verification, use the [quickstart](/quickstart).

```bash theme={null}
curl https://app.reflections.ai/v1/verifications \
  -H "Authorization: Bearer rk_test_…" \
  -H "Reflections-Version: 2026-10-15" \
  -F file=@ridge-sunrise-signed.png
```

```json theme={null}
{
  "object": "verification",
  "result": "verified",
  "method": "manifest",
  "manifest": { "signer": "Northlight", "assertion_present": true, "signature_valid": true },
  "receipt": { "id": "rcpt_ck6nw6q1j9e5wv2r6nw540m4w0", "decision": "allow", "use_status": "permitted" }
}
```

`manifest.signer` is your platform. Test receipts verify only with a test key.

## What can go wrong

| Problem | Cause and fix |
| - | - |
| `result: "invalid"` | The assertion was altered, or the file was edited after signing. Embed again from the generation's `c2pa`. |
| `assertion_present: false` | The `ai.reflections.license` assertion is missing from the active manifest. |
| A downstream site strips metadata | Fingerprint lookup is planned; until then, verify by the receipt ID you stored with the file. Register your own watermark on the generation with `watermark` to add another way to find it. |
| The receipt describes different bytes | The content changed after the generation. Submit the final bytes and embed again. |

<Tip>
  Verify embedded manifests with your own C2PA tooling and validate test receipts against the
  sandbox key set. Hosted file verification and a hosted runner for the proposed
  [conformance cases](/production/conformance) are not available.
</Tip>

## Related

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

  <Card title="C2PA assertion" icon="code" href="/api-reference/formats/c2pa-assertion">
    The `ai.reflections.license` assertion format.
  </Card>

  <Card title="C2PA interoperability" icon="book" href="/standard/interop/c2pa">
    Manifest and assertion placement in the Standard.
  </Card>

  <Card title="Verify content" icon="shield-check" href="/build/verifying-content">
    Find the receipt behind any file.
  </Card>
</Columns>


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