The legal side of this is its own article: from August 2, Article 50(2) of the EU AI Act wants your generative system's output marked in a machine-readable way, and the Commission's Code of Practice points at C2PA Content Credentials as an example that clears the bar. This is the engineering side — how to actually stamp that mark on an image, in about fifteen minutes, with the official tooling.
What you're building#
A Content Credential is a signed manifest embedded in the file. For our purpose it needs to say one thing clearly: this file was created by an algorithm. In C2PA that statement is an assertion — a c2pa.actions block with a c2pa.created action whose digitalSourceType is the IPTC URI for algorithmically generated media. Get that field right and a verifier anywhere can read "AI-generated" off the file.
1. Install c2patool#
c2patool is the official CLI from the Content Authenticity Initiative. Grab a release binary or build it with Cargo:
# option A: prebuilt binary from the releases page (contentauth/c2patool)
# option B: from source
cargo install c2patool
c2patool --version
2. Write the manifest#
Create ai-manifest.json. The one line that matters for Article 50 is the digitalSourceType:
{
"claim_generator": "acme-image-api/1.4",
"title": "AI-generated image",
"assertions": [
{
"label": "c2pa.actions",
"data": {
"actions": [
{
"action": "c2pa.created",
"digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia"
}
]
}
}
]
}
trainedAlgorithmicMedia is the IPTC vocabulary value for "produced by a trained algorithm" — i.e. a model. (If a human meaningfully edited a real photo with AI tooling, that's a different value; for fully generated output, this is the one.)
3. Sign it into the file#
Point the tool at your generated image and the manifest. In development, c2patool signs with built-in test credentials, so this is a single command:
c2patool generated.jpg -m ai-manifest.json -o signed.jpg
signed.jpg now carries the credential. For production, don't ship the test cert — sign with your own key and certificate so the credential is attributable to you:
export C2PA_PRIVATE_KEY=$(cat my_key.pem)
export C2PA_SIGN_CERT=$(cat my_cert.pem)
c2patool generated.jpg -m ai-manifest.json -o signed.jpg
4. Verify before you ship#
Read the manifest straight back out:
c2patool signed.jpg
You want to see the c2pa.created action and the trainedAlgorithmicMedia source type in the output, with a valid signature. As a human sanity check, drop signed.jpg into the public inspector at contentcredentials.org/verify — it renders the same claim your users' tools will read.
Two things that will bite you#
Do it at generation time. The correct place for the c2patool call — or the in-process c2pa-python / Rust binding — is the same function that returns the asset to the user. A nightly batch job that marks yesterday's images is a nightly window of unmarked output, and for a system launched after August 2 there is no grace period.
Metadata is strippable, so add a layer. A screenshot or a careless re-encode can drop a C2PA manifest, which is exactly why the law says "robust and reliable as far as technically feasible." The pattern the big labs converged on — and the one the Code of Practice describes — is provenance metadata plus a pixel-level watermark such as SynthID underneath it. C2PA is the layer you can ship this afternoon; treat the watermark as the next ticket, not a reason to delay this one.



