Skip to main content
An allowed generation returns c2pa: an assertion to add to the output’s C2PA manifest before you deliver it. It carries the signed receipt, so anyone who reads the manifest can verify the license decision without asking you. You stay the C2PA claim signer for your own output. The receipt inside the assertion is signed by Reflections, so it verifies independently of your signature.

Bind the receipt to your signer

A receipt is public, so anyone can copy the assertion into a manifest for other content. To stop that, send c2pa_signer when you create the generation:
thumbprint is sha256: and the lowercase hex SHA-256 of the DER encoding of the end-entity certificate that will sign the output’s C2PA claim. Reflections signs it as tlp.c2pa_signer as given, without checking it against a certificate. A verifier that reads the manifest compares it with the certificate that signed the manifest and can reject a mismatch; a mismatch can also mean a rotated or misdeclared certificate. Without it, the assertion proves a decision was made, not that this file is the one decided (TLP-RCP-16). Rotate the certificate only for outputs submitted after the change: the thumbprint is fixed in each receipt.

The assertion

ai.reflections.license is an entity-specific assertion label, in the reverse-domain form C2PA uses for assertions outside its own c2pa. namespace. Copy c2pa from the generation as it is: put label in the assertion’s label and data in its data. Your C2PA library encodes data as CBOR when it writes the manifest. The generation returns c2pa only when decision is allow: in the create response for images, text and speech up to 60 seconds, and in generation.allowed or generation.released for outputs decided later. It is null otherwise.

Where it sits in the manifest

Add two assertions to the manifest you sign for the output:
  1. A c2pa.actions assertion with a c2pa.created action whose digitalSourceType is the IPTC term for generated media, http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia. This says the output was made by a model. Tools that write C2PA 2.x manifests use the label c2pa.actions.v2; the action and the source type are the same.
  2. The ai.reflections.license assertion, exactly as the generation returned it.
A manifest definition for the line of speech in Maya Okafor’s voice from the receipt example, as Northlight signs it:
Sign the manifest with your own C2PA certificate and embed it in the file. Then show the disclosure text from the disclosure obligation, and deliver.
Embedding the manifest changes the file’s bytes, so the delivered file’s SHA-256 differs from the receipt’s artifact_sha256. Verification finds the receipt by the manifest when it survives. Fingerprint lookup, for a file whose manifest a platform stripped, is planned; until then, keep the receipt ID with the file. A fingerprint never exists for an attested output, because Reflections did not receive its bytes.

Reading it

A verifier that finds ai.reflections.license in a manifest:
  1. Validates the manifest with its C2PA tool, which proves the file is what the claim signer signed.
  2. Verifies data.receipt against the JWKS, as described in Receipt.
  3. Compares tlp.c2pa_signer.thumbprint, when present, with the SHA-256 of the claim signer certificate’s DER, and rejects the assertion when they differ. A receipt without it is unbound.
  4. Reads the live status at GET /v1/receipts/{receipt_id}, or opens data.verify_url.
Or it sends the whole manifest store, base64-encoded, as manifest to POST /v1/verifications. The result reports the claim signer, whether the assertion is present, whether its signature is valid, and the receipt. Manifest verification is planned: neither mode reads a manifest yet, so a verifier checks the signer binding itself.

Embedding provenance

Writing the assertion and the disclosure label with common C2PA tools.

C2PA in the Standard

Manifest and assertion placement, and soft bindings.