Every image the API returned here carries a signed C2PA manifest (Content Credentials), in a caBX chunk of each PNG, and OpenAI 86 's help center says API images also get an invisible SynthID 7,023 watermark. The open-source c2patool 0.27.22 (from the contentauth/c2pa-rs releases on GitHub 29 ) reads the manifest of 8.2.3's image, 8.7.3's edit and a copy resized to 512x512 with Pillow 13,858 :
// read-c2pa.mjs: summarize Content Credentials with c2patool (from contentauth/c2pa-rs)
import { execFileSync } from 'node:child_process';
for (const file of process.argv.slice(2)) {
try {
const store = JSON.parse(execFileSync('c2patool', [file], { stdio: 'pipe' }));
const m = store.manifests[store.active_manifest], sig = m.signature_info;
const { actions } = m.assertions.find(a => a.label.startsWith('c2pa.actions')).data;
console.log(`${file}: signed by ${sig.issuer} at ${sig.time.slice(0, 19)}`);
console.log(' ', actions.map(a => a.action).join(', '), '|',
actions[0].digitalSourceType.split('/').pop(), '|', store.validation_state);
} catch (err) { console.log(`${file}: ${String(err.stderr).trim()}`); }
}booknest-window.png: signed by OpenAI OpCo, LLC at 2026-09-26T07:43:58 c2pa.created, c2pa.converted, c2pa.watermarked.unbound | trainedAlgorithmicMedia | Valid nestor-kitchen.png: signed by OpenAI OpCo, LLC at 2026-09-26T08:16:50 c2pa.created, c2pa.converted, c2pa.watermarked.unbound | trainedAlgorithmicMedia | Valid nestor-kitchen-512.png: Error: No claim found
OpenAI signs each manifest, marks the image trainedAlgorithmicMedia (the IPTC term for AI-generated) and records the watermark; the edit is labeled created too. Valid means signature and hashes check out (the certificate counts as trusted only once you install the C2PA trust list). One resize erased the manifest, which is why the watermark exists and why openai.com/verify checks both: provenance is evidence, not proof.
<!doctype html>
<style>
body { margin: 0; padding: 8px; background: #fafaf7; font: 12px system-ui, sans-serif; color: #263238; }
svg { width: 100%; max-width: 600px; display: block; }
</style>
<script src="https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js"></script>
<p><button id="resize">Resize to 512×512 with Pillow</button> <button id="reset">Original</button></p>
<svg viewBox="0 0 600 270" font-size="11"></svg>
<p id="out"></p>
<script>
const svg = d3.select('svg');
// The file: PNG chunks, one of them the caBX chunk holding the signed manifest
const chunks = ['IHDR', 'caBX', 'IDAT', 'IDAT', 'IDAT', 'IEND'];
const file = svg.append('g').attr('transform', 'translate(10,20)');
file.append('text').attr('font-weight', 'bold').text('booknest-window.png');
const chunk = file.selectAll('g.ch').data(chunks).join('g').attr('class', 'ch').attr('transform', (c, i) => `translate(0,${14 + i * 30})`);
chunk.append('rect').attr('width', 120).attr('height', 24).attr('rx', 4).attr('fill', c => c === 'caBX' ? '#e09a10' : '#b0bec5');
chunk.append('text').attr('x', 8).attr('y', 16).attr('fill', '#fff').attr('font-weight', 'bold').text(c => c);
// The watermark lives in the pixels themselves (IDAT), shown as a faint pattern
file.append('text').attr('y', 214).attr('fill', '#5b3f99').text('SynthID watermark: in the pixels');
const manifest = svg.append('g').attr('transform', 'translate(190,20)');
manifest.append('rect').attr('width', 400).attr('height', 180).attr('rx', 8).attr('fill', '#fff').attr('stroke', '#e09a10').attr('stroke-width', 2);
const lines = ['C2PA manifest (Content Credentials)', 'signed by: OpenAI (certificate issuer)', 'time: 2026-09-26T14:02:11',
'c2pa.actions: created', 'digitalSourceType: trainedAlgorithmicMedia', 'assertion: watermark recorded', 'validation_state: Valid'];
manifest.selectAll('text').data(lines).join('text').attr('x', 12).attr('y', (l, i) => 24 + i * 22)
.attr('font-weight', (l, i) => i === 0 ? 'bold' : null).text(l => l);
const link = svg.append('path').attr('d', 'M130,60 C160,60 160,60 190,60').attr('stroke', '#e09a10').attr('stroke-width', 2).attr('fill', 'none');
function show(resized) {
chunk.filter(c => c === 'caBX').transition().duration(500).attr('opacity', resized ? 0.1 : 1);
manifest.transition().duration(500).attr('opacity', resized ? 0.15 : 1);
link.transition().duration(500).attr('opacity', resized ? 0 : 1);
svg.selectAll('text.gone').data(resized ? ['no manifest found'] : []).join('text').attr('class', 'gone')
.attr('x', 300).attr('y', 120).attr('fill', '#b5452f').attr('font-size', 16).attr('font-weight', 'bold').text(t => t);
d3.select('#out').text(resized
? 'c2patool: no C2PA manifest. The resize rewrote the file without the caBX chunk; the pixel watermark survives, which is why openai.com/verify checks both.'
: 'c2patool reads the manifest: provenance is evidence, not proof (install the C2PA trust list to treat the certificate as trusted).');
}
d3.select('#resize').on('click', () => show(true));
d3.select('#reset').on('click', () => show(false));
show(false);
</script>