Skip to content

Marking checks

llms.txtAsk agent

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.

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:

Terminal window
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).

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).

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.

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.

{
"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.

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.

Terminal window
# 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 2
done
# 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 1
fi

Use 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.