DEV Community

EU AI Act Article 50(2): marking AI-generated images with C2PA before the 2 December deadline

If your product generates images, audio, video or text for people in the EU, Article 50(2) of the EU AI Act is the duty that lands on the engineering team, not the design team. It's not about a banner. It's about what travels inside the file.

This post walks through what the text says, the one transition date that matters, and a working pipeline that attaches C2PA Content Credentials to AI-generated images. It's readiness support for developers, not legal advice, and it was AI-assisted and then edited.

What Article 50(2) says

From Regulation (EU) 2024/1689, Article 50, paraphrased closely:

  • Who: providers of AI systems, including general-purpose AI systems, that generate synthetic audio, image, video or text content.
  • What: the outputs must be marked in a machine-readable format and detectable as artificially generated or manipulated.
  • How good: the technical solutions must be "effective, interoperable, robust and reliable as far as this is technically feasible", taking into account the type of content, implementation costs and the generally acknowledged state of the art, "as may be reflected in relevant technical standards".
  • Exceptions in the text: systems that perform an assistive function for standard editing, systems that don't substantially alter the input data provided by the deployer or its semantics, and uses authorised by law for criminal-offence detection and investigation.

Three things developers often get wrong:

  • It's a provider duty. If you ship the model or the generation feature, it's on you. Deployers have a separate, human-facing labelling duty for deepfakes and certain public-interest text under Article 50(4).
  • A visible label is not a machine-readable mark. The European Commission's Article 50 FAQ also makes the reverse point for deepfakes: a provider's machine-readable mark doesn't satisfy the deployer's visible disclosure duty. Plan for both layers.
  • Text is in scope. Article 50(2) lists text alongside audio, image and video, so "we only do LLM chat" doesn't automatically end the conversation. The FAQ discusses exclusions such as short sequences, source code and machine-to-machine output, so read it before you scope text in or out.

The dates

  • 2 August 2026: Article 50 applies (Art. 113 of Regulation 2024/1689).
  • 2 December 2026: a four-month transition for the Article 50(2) marking duty, only for providers whose systems were already placed on the market before 2 August 2026. It was added by the Digital Omnibus on AI, Regulation (EU) 2026/1744 (recital 38 describes it). New systems launched after 2 August 2026 get no transition.

If your image or audio feature launched before August, you have roughly five weeks from today. That's the reason for this post.

The Commission has also backed a voluntary Code of Practice on Transparency of AI-generated Content, with a section for providers on marking and detection. It's worth reading alongside the Commission's Article 50 guidelines, because the regulation itself doesn't name a specific technology.

Where C2PA fits

C2PA (Coalition for Content Provenance and Authenticity) is an open standard for signed provenance manifests, often called Content Credentials, embedded in or attached to media files. A manifest can record that an asset was created by a generative model, using the IPTC digital source type trainedAlgorithmicMedia from the IPTC NewsCodes scheme.

It's a strong candidate for the "machine-readable" and "interoperable" parts of 50(2), but be honest about its limits:

  • Metadata can be stripped by re-encoding, screenshots, or platforms that drop it on upload.
  • So "robust" usually means layering: signed metadata plus something that survives stripping (an invisible watermark, a fingerprint or hash you can look up, or both).
  • C2PA doesn't apply neatly to plain text. Text marking is a separate and less mature problem.

Treat C2PA as one layer of your design, not the whole answer.

A working pipeline with c2patool

c2patool is the open-source CLI from the Content Authenticity Initiative. Install a prebuilt binary from the c2pa-rs releases or with brew install c2patool on macOS.

  1. A manifest definition for AI-generated output

Save as ai-generated.json:

{
  "claim_generator_info": [
    {
      "name": "acme-image-service",
      "version": "2.3.0"
    }
  ],
  "title": "generated.jpg",
  "assertions": [
    {
      "label": "c2pa.actions",
      "data": {
        "actions": [
          {
            "action": "c2pa.created",
            "digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia"
          }
        ]
      }
    }
  ]
}

Use your own service name and version. The key line is digitalSourceType with the trainedAlgorithmicMedia code.

  1. Sign and embed
c2patool generated.jpg -m ai-generated.json -o generated.signed.jpg

The output extension has to match the input; the tool won't convert formats. Without a key and certificate configured, c2patool uses a built-in development certificate and prints a warning. That's fine for a spike and not fine for production. The project's docs say to keep private keys out of manifest files in production and use a subprocess or remote signer instead (see signing docs).

  1. Verify in CI
c2patool generated.signed.jpg > report.json
jq -e '.. | objects | select(.digitalSourceType? | tostring | test("trainedAlgorithmicMedia"))' report.json \
  && echo "AI-source marker present" \
  || { echo "missing AI-source marker"; exit 1; }

Run this as a test on a sample output from every generation path you ship (image endpoint, batch export, editor "AI fill", and so on). The most common real-world gap isn't a missing library. It's one forgotten code path that writes files without the manifest.

  1. Wire it into the service

A thin Node.js wrapper around the CLI:

import { execFile } from "node:child_process";
import { promisify } from "node:util";
const run = promisify(execFile);

export async function markAiImage(inPath, outPath) {
  await run("c2patool", [inPath, "-m", "ai-generated.json", "-o", outPath, "-f"]);
  const { stdout } = await run("c2patool", [outPath]);
  if (!stdout.includes("trainedAlgorithmicMedia")) {
    throw new Error(`C2PA marker missing on ${outPath}`);
  }
  return outPath;
}

-f lets the tool overwrite an existing output file. If you're in Rust, Python or JavaScript and don't want to shell out, the C2PA SDKs expose the same builder.

Keep evidence

Like any transparency duty, you'll want to show what you did, not just say it. A minimal record per release:

# compliance/art50-2-marking.yml
release: 2026-10-27
surfaces:
  - name: image-generation-api
    content: image/jpeg, image/png
    marking: c2pa manifest, digitalSourceType=trainedAlgorithmicMedia
    signer: remote (KMS-backed)
    ci_check: tests/c2pa_marker.sh
  - name: tts-export
    content: audio/mpeg
    marking: TODO (target before 2026-12-02)
known_gaps:
  - marker stripped when users screenshot output (evaluating invisible watermark)

It costs ten minutes and turns a vague "we're working on it" into a dated, reviewable plan.

Checklist

  • List every surface that emits synthetic image, audio, video or text to EU users.
  • Decide per surface whether 50(2) applies, using the text and the Commission FAQ for exclusions.
  • Note whether each system was on the market before 2 August 2026; only those get until 2 December 2026.
  • Add a signed C2PA manifest for media, with production signing keys.
  • Add a second layer for robustness where stripping is likely.
  • Add a CI check per code path.
  • Keep a dated evidence file.

Limits

This isn't legal advice or a compliance certification. Whether C2PA alone is "effective, interoperable, robust and reliable" enough for your product is a judgment for you and your counsel, informed by the Commission's guidelines and the Code of Practice.

If you're still working out which Article 50 paragraphs even apply to your product, RuleReddy has a free, no-signup Article 50 check that walks through the scoping questions.

Sources

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.