Verify pages and the embed
A verify page lets anyone, without an account or a key, check a file against your records. It reads the file’s C2PA manifest (Content Credentials) and answers one question: was this made with your system? You get one page per account, served in two ways:
- Hosted:
https://originalpictures.com/verify/{slug}/, with your name, logo and colours. - Embedded: the
<op-verify>web component, on any page of your own site.
Both call the same public check API, described below, so you can also build your own interface.
What a check says, and what it does not
Section titled “What a check says, and what it does not”A check states the declared origin of a file and nothing more:
content_credentials.statusisvalid,valid_untrusted,invalidorabsent.valid_untrustedmeans the manifest is intact but its trust was not established: the signer is not on the trust list, or a trust input such as revocation status was unavailable. A file whose manifest was read is never reportedabsent.content_credentials.declared_originquotes what the manifest declares:signed_by,claim_generator,signed_at,ai_generated,digital_source_type,software_agentandactions.made_with.answerisyesorno_record, withmade_with.method(c2pa_manifest,watermarkorfingerprint) when it isyes.statementis a sentence every interface must show with every result:
This describes what the file declares about its origin and whether this provider’s system has a record of it. It is not a judgement of whether the content is real.
A check never says that content is real, fake, authentic or genuine, and it does not detect manipulation. A valid manifest shows who signed it and that it has not changed since; it does not show that the content is true.
made_with.answer is yes when the file’s active manifest was signed by Original Pictures for a
job of your account. If you turn on recover_own_assets, it is also yes when watermark or
fingerprint recovery matches one of your own assets, which can find a copy whose manifest was
removed. Recovery searches only your account’s assets, never anyone else’s. In every other case the
answer is no_record.
Set up your page
Section titled “Set up your page”PUT /v1/verify-pages (scope verify_pages:write)
creates or replaces it. Send expected_version: 0 the first time, then the version you last
read:
curl -sS -X PUT "$OP_API_BASE_URL/v1/verify-pages" \ -H "Authorization: Bearer $OP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "slug": "studio-nova", "display_name": "Studio Nova", "logo_asset_id": null, "colours": { "accent": "#cfa274", "background": "#071520", "text": "#f5f9fc" }, "recover_own_assets": true, "enabled": true, "monthly_check_limit": 2000, "expected_version": 0 }'| Field | Notes |
|---|---|
slug |
3 to 48 lower-case letters, digits and hyphens, not starting or ending with a hyphen. Unique. |
display_name |
Up to 80 characters. Shown as the provider name (“made with Studio Nova’s system”). |
logo_asset_id |
An uploaded PNG, JPEG or WebP asset (ast_…) of your account, at most 1 MB, or null. It becomes publicly readable through the page’s logo URL. |
colours |
accent, background and text as #rrggbb; omitted ones use the defaults. |
recover_own_assets |
true also runs watermark and fingerprint recovery against your own assets. Default false. |
enabled |
false takes the page offline (it answers 404 publicly) and keeps the slug reserved. |
monthly_check_limit |
The most public checks the page answers per calendar month (UTC), 1 to 100,000, or null for the platform default (10,000). Omit it to keep the current value. |
expected_version |
0 to create; the current version to replace. A stale version is 409. |
The response includes hosted_url, the monthly_check_limit you set (null for the default) and
check_usage: this month’s accepted checks against the cap in force, with period_start and
period_end (when the count resets). Read the page back with
GET /v1/verify-pages (verify_pages:read), which
answers 404 VERIFY_PAGE_NOT_FOUND until it is saved. A taken or reserved slug is
409 VERIFY_PAGE_CONFLICT. The console’s Verify pages screen does the same with a preview and
the embed code to copy.
Embed it
Section titled “Embed it”Load the element once, then add it where the check should appear, with your page’s slug:
<script src="https://cdn.jsdelivr.net/npm/@originalpictures/verify@0.1.0/dist/verify.iife.js" defer></script><op-verify page="studio-nova"></op-verify>With a bundler, import "@originalpictures/verify"; defines the element, and
import { verify } from "@originalpictures/verify/core"; gives the same check as a function
(await verify(file, { page: "studio-nova" })). The element also takes api-base and theme
(light or dark). Install it with npm i @originalpictures/verify (0.1.0 on npm since
2026-11-10), or load the script URL above. It has no dependencies, renders in its own shadow DOM, follows
the visitor’s light or dark preference, and is keyboard and screen-reader accessible. A visitor
drops or chooses a file, and the element shows the Content Credentials card, the “made with” answer
and the statement. The file goes only to the public check API.
Inspect media already on your page
Section titled “Inspect media already on your page”To let visitors inspect an image, video or audio file your page already shows, give the element
the asset instead of a drop zone, with for (the id of an <img>, <video> or <audio>,
whose currentSrc it reads) or src (the asset’s URL). An MP4 video is refused in the browser,
before anything is sent, while public checks do not run MP4 (see media types):
<img id="hero" src="https://cdn.example.com/sunrise.jpg" alt="Sunrise" /><op-verify page="studio-nova" for="hero"></op-verify>The element then shows a compact button, “Check Content Credentials”. Pressing it loads the asset in the visitor’s browser, runs the same public check and labels the button “Content Credentials”, “No Content Credentials found” or “Content Credentials that do not validate”. The button expands into the same card, statement included, and collapses on the next press; pressing it again does not run a second check.
| Attribute | Meaning |
|---|---|
src |
The URL of the asset to inspect. |
for |
The id of a media element on the same page (or in the same shadow root). |
autocheck |
Check when the element loads rather than on the first press. Billed every view. |
- Billing: each check is billed to you as one verification, so the element never checks on page
view by default. It checks on the first press, or on load only with
autocheck. - CORS: the browser fetches the asset, so the asset’s server must allow your page’s origin. If it
does not, if the network fails, or if the asset is over 25 MB, nothing is checked: the element
says so and offers the file drop instead. These failures (
op-verify-errorwith aSOURCE_*code) happen before any check request and are not billed. - Function:
inspect(src, { page })from@originalpictures/verify/coredoes the same without the element.
The public check API
Section titled “The public check API”These operations need no key and accept requests from any origin.
| Operation | What it does |
|---|---|
GET /v1/public/verify-pages/{slug} |
The page’s name, logo URL, colours and limits. |
POST /v1/public/verify-pages/{slug}/checks |
Check one file (multipart field file). |
GET /v1/public/verify-pages/{slug}/checks/{check_id} |
Poll a check that is still running. |
curl -sS -X POST "$OP_API_BASE_URL/v1/public/verify-pages/studio-nova/checks" \ -F "file=@photo.jpg"The answer is 200 with a completed check, or 202 with state: "running" and a check_id
(pchk_…) for a file that takes longer, such as a large audio file. Poll at most every 2 seconds;
a check expires 15 minutes after it was created.
- Size: at most 25 MB. A larger file is
413 MEDIA_COMPRESSED_SIZE_LIMIT. - Media types:
GET /v1/public/verify-pages/{slug}lists what a check accepts inaccepted_media_types(JPEG, PNG, WebP, TIFF, AVIF, WAV, M4A, MP3, FLAC and PDF where the deployment admits them). MP4 video is not checked on a public page today: it is refused with415 PUBLIC_CHECK_MEDIA_UNSUPPORTED, whose message lists the accepted types, before anything is staged or billed. A file whose format cannot be read is422 MEDIA_FORMAT_UNDETECTED. To verify a video you signed, usePOST /v1/verificationson the stored object. - Rate limit: 10 checks a minute and 100 a day per network and page, and 120 a minute and 2,000
a day per page across all networks. Over a limit is
429 PUBLIC_CHECK_RATE_LIMITEDwithRetry-After. - Monthly cap: past the page’s
monthly_check_limit(or the default 10,000) in a calendar month, checks are429 PUBLIC_CHECK_MONTHLY_LIMIT_REACHEDwithRetry-Afteruntil the next month starts (UTC). The cap counts accepted, billed checks. - Privacy: the file is held only for the check. It is never stored with the result and never logged, and neither are its file name and the visitor’s IP address (the rate limit uses a keyed hash of it).
- Billing: each public check is billed to you, the page owner, as one verification; see Retention and pricing.
To check many of your own stored outputs at once, use
POST /v1/verifications/batch instead: up to 100
object_ids in one call, each exactly as POST /v1/verifications would accept it.