Marking checks
A marking check looks at outputs you have already signed and reports, requirement by requirement, whether each carries the machine-readable marking a jurisdiction preset expects: a valid C2PA manifest, an AI declaration, a recoverable watermark or fingerprint.
Start a check
Section titled “Start a check”POST /v1/marking-checks (scope verify:write, with
an Idempotency-Key header) takes a preset and 1 to 25 items, each an asset_id or an
object_id:
curl -sS -X POST "$OP_API_BASE_URL/v1/marking-checks" \ -H "Authorization: Bearer $OP_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"preset": "eu_ai_act_art50", "items": [{"asset_id": "ast_…"}, {"object_id": "aob_…"}]}'The answer is 202 with a marking_check_id (mchk_…). Poll
GET /v1/marking-checks/{marking_check_id} (scope
verifications:read) until state is completed or failed, with the same backoff as a
verification. An unknown preset, too many items, or an asset or object of another account is
422 MARKING_CHECK_INVALID; an unknown id is 404 MARKING_CHECK_NOT_FOUND. Replaying the same
Idempotency-Key with the same body returns the same check, even when the two requests arrive at
once; a different body under the same key is 422 MARKING_CHECK_INVALID.
Each item runs one verification, recorded as one verification charge in your usage ledger (see Billing: charges are not yet reported to the payment provider and are invoiced from a later date).
Requirements
Section titled “Requirements”| Requirement | Passes when |
|---|---|
c2pa_manifest |
A C2PA manifest is present and its signature and hard binding validate. |
ai_declaration |
The manifest declares a generative digitalSourceType. |
recoverable_watermark |
Our watermark was recovered from the file and resolves to one of your assets, or a watermark was recorded at publication for this exact output. |
recoverable_fingerprint |
A stored fingerprint of one of your assets was matched against the file, or a fingerprint was recorded at publication for this exact output. |
machine_readable_ai_marking |
ai_declaration passes, or our watermark was recovered from the file, or a watermark was recorded at publication for this output and c2pa_manifest passes (the hard binding shows the bytes are unchanged). |
Recovered or recorded
Section titled “Recovered or recorded”The names recoverable_watermark and recoverable_fingerprint overstate what is usually checked.
Recovery runs only when the manifest does not validate. For an unaltered output whose manifest
validates, these requirements pass on what Original Pictures recorded when it published that
output, not on a watermark decoded from, or a fingerprint matched against, the file. Each passing
requirement says which in evidence:
"recovered": the signal was decoded or matched from the checked file."recorded": the requirement rests on a binding recorded for this exact output at publication.null: the requirement was read from the manifest itself, or it failed.
The detail sentence says the same in words. machine_readable_ai_marking accepts a recorded
watermark only together with a validating manifest: the manifest’s hard binding then shows that the
file is byte-for-byte the one that was watermarked and signed. Without it, a recorded watermark
proves nothing about the file and does not count. Checks stored before evidence existed report it
as null.
Presets
Section titled “Presets”An item passes when every required requirement passes. Recommended requirements are reported, but do not fail the item.
| Preset | Covers | Required | Recommended |
|---|---|---|---|
c2pa_baseline |
A C2PA manifest only | c2pa_manifest |
none |
eu_ai_act_art50 |
EU AI Act Art. 50(2) machine-readable marking | c2pa_manifest, machine_readable_ai_marking |
ai_declaration, recoverable_watermark, recoverable_fingerprint |
us_ca_sb942 |
California SB 942 / AB 853 latent disclosure | as eu_ai_act_art50 |
as eu_ai_act_art50 |
cn_labeling_measures |
China AI-generated content labelling: implicit label | c2pa_manifest, ai_declaration, machine_readable_ai_marking |
recoverable_watermark, recoverable_fingerprint |
kr_ai_framework_act |
Korea AI Framework Act Art. 31 marking | as eu_ai_act_art50 |
as eu_ai_act_art50 |
in_synthetic_information |
India synthetically generated information: metadata identifier | as eu_ai_act_art50 |
as eu_ai_act_art50 |
Because an AI declaration is accepted only on a sign-only call
(Declare AI generation), there are two ways to pass the
AI-marking presets: sign with an ai_declaration and "watermark": false (fingerprints allowed),
or sign with "watermark": true. The China preset requires the declaration itself.
The result
Section titled “The result”{ "marking_check_id": "mchk_…", "preset": "eu_ai_act_art50", "state": "completed", "summary": { "passed": 1, "failed": 0, "pending": 0, "errored": 0 }, "items": [ { "index": 0, "asset_id": "ast_…", "object_id": "aob_…", "verification_id": "vjob_…", "outcome": "pass", "failure_reason": null, "requirements": [ { "requirement": "c2pa_manifest", "level": "required", "outcome": "pass", "detail": "A C2PA manifest is present and validates.", "evidence": null }, { "requirement": "recoverable_fingerprint", "level": "recommended", "outcome": "pass", "detail": "A fingerprint was recorded for this output at publication; it was not matched against the file.", "evidence": "recorded" } ] } ], "statement": "A technical check of the machine-readable marking these files carry. It is not a legal determination and does not cover visible labels shown around the files."}An item’s outcome is pass, fail, pending or error (with a failure_reason); each
requirement’s is pass, fail, pending or not_checked.
Gate publication on a check
Section titled “Gate publication on a check”A hosting platform can upload a file, sign it, and refuse to publish it until the marking check passes. The check reads only files already stored in your account, so the order is: upload, sign, then check the signed output.
# 1. Upload and sign as usual (see the quickstart); keep the signed output's asset id.ASSET_ID="ast_…"
# 2. Start the check.CHECK_ID=$(curl -sS -X POST "$OP_API_BASE_URL/v1/marking-checks" \ -H "Authorization: Bearer $OP_API_KEY" \ -H "Idempotency-Key: publish-$ASSET_ID" \ -H "Content-Type: application/json" \ -d "{\"preset\": \"eu_ai_act_art50\", \"items\": [{\"asset_id\": \"$ASSET_ID\"}]}" \ | jq -r .marking_check_id)
# 3. Poll until it is terminal (back off between polls).while :; do RESULT=$(curl -sS "$OP_API_BASE_URL/v1/marking-checks/$CHECK_ID" \ -H "Authorization: Bearer $OP_API_KEY") STATE=$(echo "$RESULT" | jq -r .state) [ "$STATE" = completed ] || [ "$STATE" = failed ] && break sleep 2done
# 4. Gate on the item outcome.if [ "$(echo "$RESULT" | jq -r '.items[0].outcome')" = pass ]; then echo "publish"else echo "$RESULT" | jq -r '.items[0].requirements[] | select(.outcome != "pass") | "\(.level) \(.requirement): \(.detail)"' exit 1fiUse the same Idempotency-Key for a retry of the same publication so a retry never runs (or bills)
a second check. Treat an error outcome as “not checked”, not as a pass. A pass means only that the
file carries the machine-readable marking the preset lists; it says nothing about a visible label
your page shows, and it is not a legal determination.