How to Add C2PA Metadata to AI Ad Creatives (Step-by-Step)
C2PA metadata is a cryptographically signed provenance framework used to embed verifiable history directly into digital assets. Under the IAB AI Transparency and Disclosure Framework, advertisers must embed a C2PA manifest containing two specific custom assertions—com.iab.threshold and com.iab.disclosure—into every AI-involved ad creative prior to distribution. For tools that do not automatically add manifests (e.g., Midjourney, Runway), engineers must use SDKs like c2pa-node to create and sign the manifest from scratch. For tools that do (e.g., Adobe Firefly), the manifest must be updated to append the IAB assertions while preserving the parent ingredient chain.
The IAB AI Transparency and Disclosure Framework requires that every AI-involved ad asset carry machine-readable provenance metadata before it goes into distribution. That metadata lives in a C2PA manifest — a cryptographically signed record embedded directly in the file.
This guide is for engineers and technical creative teams who need to implement that requirement. It covers the two scenarios that actually exist in production pipelines: creatives from tools that already write C2PA (like Adobe Firefly or Photoshop), and creatives from tools that do not (Midjourney, Runway, Stable Diffusion, ElevenLabs). Both paths converge on the exact same mandatory IAB custom assertions.
If you are still deciding whether a creative needs disclosure at all, utilize the IAB AI Materiality Checker process first, then come back here to implement.
What the IAB Framework Actually Requires in Metadata
Before touching any code, it is critical to understand what the framework demands. The IAB AI Transparency and Disclosure Framework specifies two IAB custom C2PA assertions that must be embedded in every AI-involved creative before distribution:
| Assertion | Key | Allowed Values | Who Determines It |
|---|---|---|---|
| Disclosure threshold | com.iab.threshold | "met" or "not met" | Human (compliance lead or materiality checker) |
| Consumer disclosure applied | com.iab.disclosure | "yes" or "no" | Human (creative/ops team) |
Both of these assertions are mandatory regardless of whether a visible consumer-facing label appears on the ad. The metadata acts as the B2B audit layer — traveling with the asset to agencies, publishers, platforms, and regulators. If the asset has no manifest, or the manifest is missing these specific assertions, the creative is fundamentally non-compliant under the IAB framework.
Beyond the IAB assertions, your manifest should also definitively include:
IPTC digitalSourceType— the standardized vocabulary identifying what kind of AI generation occurred.- AI involvement type — a human-readable description (e.g.,
"synthetic voiceover","background replaced"). - Tool identification — the precise name and version of the generative AI tool(s) used.
- Timestamps and signer entity — verifiable records often written automatically by compliant tools.
- Mandatory Assertions: Every AI-involved creative must include
com.iab.thresholdandcom.iab.disclosureC2PA assertions. - No Manifest Default: Tools like Midjourney and Runway do not generate C2PA metadata; you must build and sign manifests from scratch.
- Existing Manifest Updates: Tools like Adobe Firefly generate a base C2PA manifest, but you must append the IAB assertions programmatically while preserving the ingredient chain.
- Verification is Critical: Always verify the signed manifest before distribution; image optimization steps often strip XMP metadata unintentionally.
Step 0: Determine Your IPTC digitalSourceType Value
The IPTC (International Press Telecommunications Council) digitalSourceType vocabulary is the standard C2PA uses to classify how content was created. You need to identify the correct value before writing any code.
The most commonly applicable values for advertising creatives include:
digitalSourceType Value | When to Use It |
|---|---|
trainedAlgorithmicMedia | Fully AI-generated image, video, or audio from a generative model (e.g., Midjourney, Runway Gen-3, Sora). |
compositeWithTrainedAlgorithmicMedia | Human-originated base asset with AI-generated elements composited in (e.g., a real product photo with an AI-replaced background). |
algorithmicMedia | AI-processed but not generative — traditional ML processing, upscaling, noise reduction. |
digitalCapture | Real photograph or live-action video with absolutely no AI generation involved. |
For the vast majority of AI ad creatives, you will use trainedAlgorithmicMedia or compositeWithTrainedAlgorithmicMedia. For instance, if you composite a Midjourney-generated background behind a photographed product prominently, that requires compositeWithTrainedAlgorithmicMedia.
Step 1: Check Whether Your Creative Tool Already Wrote a Manifest
This determines your entire implementation workflow. Run a quick audit before writing scripts.
Tools that write C2PA manifests automatically
Adobe Firefly (in Photoshop, Illustrator, Express) and Adobe Stock AI-generated assets embed Content Credentials by default for any AI-generated or edited element. The manifest includes generation method, tool name, and timestamp. However, it does not include the IAB custom assertions (com.iab.threshold and com.iab.disclosure).
Tools that write no manifest at all
- Midjourney (all versions)
- Runway (Gen-2, Gen-3)
- Stable Diffusion (ComfyUI, AUTOMATIC1111)
- ElevenLabs
- OpenAI DALL·E (via API)
If your pipeline relies on these tools, you are starting from scratch and must create a full manifest.
How to Check Programmatically
You can easily inspect an asset using the c2pa-cli:
# Install the C2PA CLI tool
npm install -g c2pa-cli
# Inspect any asset
c2patool inspect ./your-creative.jpg --output json
If the output shows "manifests": {}, you are in the create-from-scratch path. If it returns a populated manifest object, you must update the existing manifest.
Step 2A: If No Manifest Exists — Create One from Scratch
First, install c2pa-node, the official Node.js binding for the C2PA Rust SDK.
npm install c2pa-node
c2pa-node requires a signing certificate. For development, you can generate a self-signed certificate. For production, you must use a certificate from your PKI or a trusted CA so the signer entity is legally identifiable.
# Generate a self-signed cert for development ONLY
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
-days 365 -nodes -subj "/CN=YourAgencyName"
Full Manifest Creation Code Example
import { createC2pa, ManifestBuilder } from 'c2pa-node';
import { readFile, writeFile } from 'fs/promises';
const c2pa = await createC2pa({
signer: {
privateKey: await readFile('./key.pem'),
certificate: await readFile('./cert.pem'),
algorithm: 'ps256',
tsaUrl: 'http://timestamp.digicert.com',
},
});
const manifest = new ManifestBuilder({
claim_generator: 'YourAgency/CompliancePipeline/1.0',
format: 'image/jpeg',
title: 'hero-banner-q2-campaign.jpg',
assertions: [
// IPTC source type
{
label: 'stds.iptc.photo-metadata',
data: {
'Iptc4xmpExt:DigitalSourceType': 'http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia',
},
},
// AI involvement description
{
label: 'c2pa.ai_info',
data: {
description: 'Hero banner image fully generated by Midjourney v6.',
aiTool: 'Midjourney v6',
aiInvolvementType: 'text-to-image generation',
},
},
// IAB custom assertions
{
label: 'com.iab.threshold',
data: {
value: 'met',
rationale: 'Photorealistic AI-generated human in primary role',
assessedBy: 'compliance-lead@youragency.com',
assessedAt: new Date().toISOString(),
},
},
{
label: 'com.iab.disclosure',
data: {
value: 'yes',
labelText: 'AI-generated image',
labelPlacement: 'overlay-bottom-left',
appliedAt: new Date().toISOString(),
},
},
],
});
const inputBuffer = await readFile('./hero-banner.jpg');
const { signedAsset } = await c2pa.sign({
asset: { mimeType: 'image/jpeg', buffer: inputBuffer },
manifest,
});
await writeFile('./hero-banner-c2pa.jpg', signedAsset.buffer);
console.log('C2PA manifest embedded successfully.');
The com.iab.threshold and com.iab.disclosure values are explicit editorial decisions made by a human. Extract them from your compliance workflow database before executing the script.
Step 2B: If a Manifest Already Exists — Update It
When dealing with Adobe Firefly assets, you cannot simply overwrite the manifest. Each update must be appended as a new ingredient that references the original, creating an auditable provenance chain.
import { createC2pa, ManifestBuilder } from 'c2pa-node';
import { readFile, writeFile } from 'fs/promises';
const c2pa = await createC2pa({ /* signer config */ });
const inputBuffer = await readFile('./firefly-export.jpg');
const updateManifest = new ManifestBuilder({
claim_generator: 'YourAgency/CompliancePipeline/1.0',
format: 'image/jpeg',
title: 'firefly-hero-banner-iab-compliant.jpg',
ingredients: [
{
title: 'firefly-export.jpg',
relationship: 'parentOf',
asset: { mimeType: 'image/jpeg', buffer: inputBuffer },
},
],
assertions: [
{
label: 'com.iab.threshold',
data: {
value: 'met',
rationale: 'AI-generated photorealistic background scene',
assessedBy: 'compliance-lead@youragency.com',
assessedAt: new Date().toISOString(),
},
},
{
label: 'com.iab.disclosure',
data: {
value: 'yes',
labelText: 'AI-generated image',
labelPlacement: 'overlay-bottom-left',
appliedAt: new Date().toISOString(),
},
},
],
});
const { signedAsset } = await c2pa.sign({
asset: { mimeType: 'image/jpeg', buffer: inputBuffer },
manifest: updateManifest,
});
await writeFile('./firefly-hero-banner-iab-compliant.jpg', signedAsset.buffer);
console.log('IAB assertions appended successfully.');
The ingredients array preserves the chain of custody. Verifiers and platforms can traverse the chain backwards to see the exact generative origins while simultaneously verifying your agency's compliance stamp.
Step 3: Handle Video and Audio Creatives
The exact same implementation pattern applies to video (MP4, WebM) and audio (MP3, WAV) assets. You must provide format-specific MIME types and tailor your AI descriptions accordingly.
Remember, for a generic synthetic voice that does not mimic a real person, your compliance check might determine com.iab.threshold is "not met". However, the assertion itself must still be fundamentally embedded into the file.
Step 4: Verify the Manifest Before Distribution
Never ship an asset from your signing pipeline directly to trafficking without verification. Image optimization workflows frequently strip XMP metadata accidentally, destroying the carefully constructed manifest.
c2patool inspect ./hero-banner-c2pa.jpg --output json | jq '.manifests | .[] | .assertions | .[] | select(.label | startswith("com.iab"))'
If either the threshold or disclosure assertion is missing from the output, the asset is strictly non-compliant and must be rejected before trafficking. Integrating automated verification steps into your CI/CD pipeline guarantees flawless compliance continuously.
Frequently Asked Questions
Related Reading
What Is C2PA and Why Does It Matter for AI Advertising?
A foundational overview of the C2PA specification and its deep integration into the IAB transparency guidelines.
IAB AI Transparency & Disclosure Framework: Complete Guide 2026
A comprehensive breakdown of the entire IAB AI Transparency and Disclosure Framework.
Does My AI Ad Need a Disclosure Label?
Detailed guidance on materiality assessments and correctly deciding your threshold classifications.