I Investigated How X Detects AI Generated Images. It’s Not SynthID.
I ran SynthID detection and pulled C2PA manifests from real ChatGPT and Gemini images to understand how X’s “Made with AI” label actually…
I Investigated How X Detects AI Generated Images. It’s Not SynthID.
I ran SynthID detection and pulled C2PA manifests from real ChatGPT and Gemini images to understand how X’s “Made with AI” label actually works.

Not a Medium member? Read by clicking **here**.
Ever posted an AI-generated image on X? Before you even hit Post, it almost instantly says, “Made with AI.” I always assumed it was reading some kind of watermark embedded in the image. Turns out I was completely wrong.
Every image you generate with ChatGPT or Gemini contains a secret. It’s not in the filename or the metadata you can read, it’s baked into the pixels themselves. This is SynthID, Google DeepMind’s invisible watermarking system.
But here’s the twist: X can’t read it. And when you see the “AI-generated” label on a post, it probably didn’t come from SynthID at all. It’s based on C2PA (Content Provenance and Authenticity).

X immediately detects AI generated content
Let me break down what’s actually going on.
What Is SynthID?
Before we get to how X detects AI media content, we need to understand SynthID, because it’s the technology everyone assumes X is using, and the reason most people have the wrong mental model of how AI image detection works.
SynthID embeds a pseudorandom pattern directly into image pixels at generation time. The perturbation is so small — at most ±4 intensity units per channel out of 0–255 — that it’s completely invisible to the human eye.
Google DeepMind introduced SynthID initially for images, and later extended it to text. The text version is open-sourced on Hugging Face. The image version remains proprietary.

How SynthID works
How SynthID Works
Detection works by computing a statistical correlation between the image and the expected pattern for a known secret key. If the score crosses a threshold (~0.03 Pearson correlation), the watermark is confirmed.
Here’s a stripped-down implementation of the core idea:
def detect(img, key):
# Regenerate the expected pattern from the secret key
pattern = np.random.RandomState(key).choice([-1.0, 1.0], size=img.shape)
# Pearson-style correlation: high score = watermark present
img_f = img.astype(float)
score = np.mean((img_f - img_f.mean()) * pattern) / (img_f.std() + 1e-8)
return score > 0.03 # threshold
Running this against different test cases:

The third result is the key insight: the watermark is statistically undetectable without the matching secret key. Even if you know the image is watermarked, you can’t confirm it without the key. SynthID can only answer one very specific question:
“Was this image generated by OUR model using key K?”
It cannot tell you whether an image from Midjourney, Stable Diffusion, or any other model is AI-generated. Those models don’t have the key, so they can’t embed the signal, and SynthID can’t detect what it didn’t embed.
Think of it like a wax seal on a letter. You can verify your own seal. But you can’t verify someone else’s, and you certainly can’t prove there was never a seal just because you don’t see yours.
Enter C2PA: The Other Provenance System
Alongside SynthID, there’s a second, very different approach: C2PA Content Credentials, an open standard backed by Adobe, Google, Microsoft, and OpenAI.

How C2PA works
Where SynthID hides a signal in pixels, C2PA attaches a cryptographically signed manifest to the image file itself. Think of it as a tamper-evident receipt: it records who created the image, which tool was used, and what actions were taken, all signed by the provider’s certificate.
The catch: it lives in the file’s metadata, not the pixels. Re-save the image and the manifest is gone.
You can read the manifest from any image with the c2pa Python library:
import c2pa
import json
reader = c2pa.Reader("image.png")
manifest = json.loads(reader.json())
active = manifest["manifests"][manifest["active_manifest"]]
issuer = active["signature_info"]["issuer"]
actions = [a["action"] for a in active.get("assertions", [])
if a.get("label") == "c2pa.actions"]
source_type = next(
(a["data"].get("digitalSourceType") for a in active.get("assertions", [])
if a.get("label") == "stds.schema-org.CreativeWork"),
None
)
print(f"Issuer: {issuer}")
print(f"Actions: {actions}")
print(f"Source type: {source_type}")
What’s Actually Inside ChatGPT and Gemini Images?
I tested this against real images generated by ChatGPT and Gemini.

AI generated image of an apple using a macbook
Here’s what I found.
ChatGPT (GPT-image-2)
Running the SynthID detector: score = +0.00048 — no match, as expected. I don't have OpenAI's key.
But inspecting the PNG metadata revealed something more interesting: a 29KB signed provenance record embedded in the caBX PNG chunk.

PNG metadata in a GPT-image-2 generated image
Here’s the full manifest:
{
"active_manifest": "urn:c2pa:1e58f39f-c798-46b4-8661-390de9684129",
"title": "image.png",
"claim_generator_info": [{ "name": "OpenAI Media Service API", "specVersion": "2.2.0" }],
"assertions": [{
"label": "c2pa.actions.v2",
"data": {
"actions": [
{
"action": "c2pa.created",
"when": "2026-06-01T00:00:00Z",
"softwareAgent": { "name": "gpt-image", "version": "2.0" },
"digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia"
},
{ "action": "c2pa.converted", "when": "2026-06-01T00:00:00Z" },
{ "action": "c2pa.watermarked.unbound", "when": "2026-06-01T00:00:00Z" }
]
}
}],
"signature_info": {
"alg": "Ps256",
"issuer": "OpenAI OpCo, LLC",
"common_name": "OpenAI Media Service",
"time": "2026-06-01T02:00:15Z"
},
"validation_status": [{ "code": "signingCredential.untrusted" }]
}
That c2pa.watermarked.unbound action is OpenAI essentially leaving a signed note saying "yes, we applied SynthID to this image." They log it explicitly. OpenAI also announced this publicly in May 2025, adding C2PA Content Credentials alongside SynthID for all generated images.
[embed]
Gemini
Gemini images also include a C2PA manifest signed by Google LLC — but compare what’s inside:

Gemini image metadata
Here’s the full Gemini manifest:
{
"active_manifest": "urn:c2pa:46ae9bed-a95b-76a2-ce32-4fe1a88f0941",
"title": null,
"claim_generator_info": [{
"name": "Google C2PA Core Generator Library",
"version": "922065892:922065892"
}],
"assertions": [{
"label": "c2pa.actions.v2",
"data": {
"actions": [
{
"action": "c2pa.opened",
"parameters": {
"ingredients": [{ "hash": "lJTT3DwXQs3vKcUK8zvSyTs0Kfy8NF78MDfnFJSpnRI=" }]
}
},
{
"action": "c2pa.edited",
"digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/composite",
"description": "Added visible watermark"
},
{ "action": "c2pa.converted", "description": "Converted to .png" }
]
}
}],
"signature_info": {
"alg": "Es256",
"issuer": "Google LLC",
"common_name": "Google Media Processing Services",
"time": "2026-06-01T02:16:36Z"
}
}
Notice that Google silently embeds SynthID without recording it in the manifest. And the composite source type is a weaker signal than OpenAI's trainedAlgorithmicMedia for automated labeling systems.
Older Gemini images have no C2PA manifest at all, no metadata, no record, nothing. I confirmed this by testing against images I generated a few months back.
So How Does X Actually Label AI Images?
This is the most surprising part. X does not have access to OpenAI’s or Google’s SynthID keys. It cannot verify the pixel watermark.

How X detects AI images
Here’s what actually happens:
1. At upload: X reads the caBX chunk from the PNG, parses the C2PA manifest, and checks the digitalSourceType field. If it's trainedAlgorithmicMedia → it adds the "AI-generated" label.
2. After storage: X re-encodes every uploaded image (JPEG compression, resize). This destroys the caBX chunk entirely. Images you download from X have zero C2PA metadata.
3. Fallback: For images without C2PA metadata, older Gemini outputs, manually stripped metadata, Midjourney, Stable Diffusion, X likely runs an internal ML classifier. During my testing, when I uploaded AI-generated images without C2PA metadata, X wasn’t able to detect them.
In other words: X’s AI label is powered by C2PA metadata, not SynthID. The pixel watermark is reserved for the model provider’s own verification portal.
What SynthID Is Actually For
SynthID isn’t designed to let platforms or users verify images. It’s designed to let the model provider verify their own outputs, for legal attribution, abuse investigation, or compliance audits.
- OpenAI: openai.com/research/verify/
- Google: Vertex AI API / SynthID portal
The secret key never leaves the provider. That’s by design, if it leaked, anyone could forge watermarks on arbitrary images. The watermark’s entire security model depends on key secrecy.
Side note: @pleometric ran some fascinating experiments to extract the GPT-image-2 watermark — worth checking out if you want to go deeper on the pixel side.
[embed]
Conclusion
We now have two parallel systems for AI image provenance:
SynthID: cryptographically strong, invisible, survives re-encoding. But requires the secret key. Only the model provider can verify. Third parties are completely blind to it.
C2PA: human-readable, verifiable by anyone, open standard. But strippable: re-saving the image removes it entirely. What platforms read at upload time is gone after re-encoding.
Neither system is foolproof in isolation. SynthID is unverifiable by third parties; C2PA is trivially strippable. The real provenance story, “was this generated by AI?”, lives at the intersection of both, and even then, only for images from providers who implement both standards.
The next time you see an “AI-generated” label on X, now you know: it came from metadata that has since been stripped from the image.
메타데이터
- post_id
- acb2a4bfab44
- slug
- i-investigated-how-x-detects-ai-generated-images-its-not-synthid-acb2a4bfab44
- url
- https://medium.com/agileinsider/i-investigated-how-x-detects-ai-generated-images-its-not-synthid-acb2a4bfab44
- canonical_url
- https://medium.com/agileinsider/i-investigated-how-x-detects-ai-generated-images-its-not-synthid-acb2a4bfab44
- author_url
- https://medium.com/@utkarshabakshi
- status
- ok
- fetched_at
- 2026-06-09 15:37:30