Within /v1, changes are additive only — existing fields never change meaning or disappear; clients must ignore fields they do not recognize. The machine-readable contract is served at /v1/openapi.yaml; the entries below record when each addition shipped.
c2patool validation is CI-enforced
The registry test suite now runs the real c2patool (pinned, checksum-verified) against every full-stamp format on every commit — the citation behind "validates in c2patool".
Bring-your-own certificates are recorded as post-launch; managed signing is the launch path.
spec 1.4.0
API keys are minted only by the CLI device flow
zerobyte login is the only self-serve way to mint a key: the CLI starts a pairing, you approve it in the browser, the key lands in your terminal once.
Signup no longer returns an API key; POST /v1/keys and the dashboard create endpoint are removed. GET /v1/keys and DELETE /v1/keys/{id} (list and revoke) are unchanged.
Keys are named per pairing ("CLI login <code>"), so the dashboard list is distinguishable.
spec 1.3.0
Fingerprint v2
The perceptual hash is a real 2-D pHash; every proof records fingerprint_version, and only the current version takes part in matching.
Content with no perceptual detail is refused at stamp with 422 CONTENT_TOO_UNIFORM; on verify the registry signal reports skipped with the reason instead of risking a false match.
RegistryEvidence.status gains "skipped". Latency re-measured with the new hash: stamp p50 41 ms / p99 53 ms (loopback, excluding network).
spec 1.2.0
Request ids and rate-limit headers
Every response carries X-Request-Id (yours, echoed, when header-safe); every error body repeats it as request_id.
Responses for rate-limited tiers carry X-RateLimit-Limit / -Remaining / -Reset; every 429 carries Retry-After. The per-IP limiter answers in the standard JSON shape (IP_RATE_LIMITED).
Malformed JSON and unknown routes answer in the standard error shape too (INVALID_JSON, NOT_FOUND).
spec 1.1.0
Idempotent stamping and the format policy
Idempotency-Key on POST /v1/stamp: a retry with the same key and request returns the original proof (replayed: true, Idempotency-Replayed header) instead of minting a duplicate into the append-only log.
Unsupported or mislabelled content types answer 415 (UNSUPPORTED_MEDIA_TYPE / CONTENT_TYPE_MISMATCH) with the supported list; declared types are validated against magic bytes.
Every error carries docs_url pointing at the section that explains it.
The classifier estimate is REST-only
analyze() is removed from both SDKs before their first publish. The opt-in statistical estimate remains reachable as the deprecated /v1/analyze endpoint — always separate from the verdict.
Untrusted signers never verify origin
A cryptographically valid manifest from a signer not on the C2PA trust list yields the distinct verdict provenance_untrusted — a self-claim is disclosed, never promoted to verified_origin.
spec 1.0.0
Evidence-first verification and C2PA stamping
POST /v1/verify returns a verdict built only from evidence (verdict, origin_sources, per-signal evidence report); legacy matched/confidence fields kept, additively.
POST /v1/stamp signs a C2PA manifest (when the deployment has a certificate) and always anchors the fingerprint — the binding field discloses which you got, never a silent downgrade. Optional creator attribution names your brand as the claim generator.