API reference¶
Experimental
This beta MVP and its documentation are provided as is, without warranty of any kind. The API, the declaration behaviour and the public instances may change in breaking ways before version 1.0.
The store implements the store, fetch, query and service routes of the
C2PA Soft Binding Resolution API
as defined for the upcoming C2PA specification 2.5, whose OpenAPI document still carries version 2.4.0. Every
instance serves an interactive API reference at /docs and the OpenAPI document at /openapi/openapi.yaml (also
as /openapi/openapi.json). Schemas named c2pa.* in that document are copied verbatim from the C2PA OpenAPI
definition (/openapi/c2pa-sbr.json, CC BY 4.0).
Routes¶
| Route | Behaviour |
|---|---|
POST /v1/manifests |
Store a C2PA Manifest Store sent as application/c2pa and declare it |
GET /v1/manifests/{manifestId} |
The stored Manifest Store, byte for byte, as application/c2pa |
DELETE /v1/manifests/{manifestId} |
Delete a manifest and withdraw its declaration; needs the operator token |
GET /v1/matches/byBinding |
Query with alg, value and optional maxResults |
POST /v1/matches/byBinding |
Same query with {"alg": ..., "value": ...} as JSON body |
GET /v1/services/supportedAlgorithms |
io.iscc.v0 and every algorithm the store has indexed, by fingerprint and watermark |
GET /v1/services/capabilities |
info.version of c2pa-sbr.json (2.4.0) and the capability storeManifests |
GET /v1/services/status |
ok, or degraded while the declaration of an upload older than 15 minutes is pending |
GET /.well-known/c2pa-soft-binding-resolution |
The API base, the capabilities and status addresses, and the specification version |
GET /.well-known/did.json |
Extension: the did:web document of the declaration key; 404 when not declaring |
Every GET route also answers HEAD. Not implemented: byContent, byReference, the bindings and receipt
routes.
The discovery document gives absolute addresses:
{
"apiEndpoint": "https://c2pa-store-test.iscc.io/v1",
"c2paSpecificationVersion": "2.4.0",
"capabilitiesEndpoint": "https://c2pa-store-test.iscc.io/v1/services/capabilities",
"statusEndpoint": "https://c2pa-store-test.iscc.io/v1/services/status"
}
Reads and queries are open. Uploads need no token. Each client has two separate rate limits: uploads (10 per minute by default) and fetches and queries together (120 per minute by default), so reading never uses up the upload allowance. A client is its IPv4 address or its IPv6 /64 network. Both limits are counted in fixed windows of the configured period; the service routes are not limited, and neither are the addresses the operator exempts, such as its own resolvers.
Deletes need the operator token as Authorization: Bearer <token>. Public API routes send
Access-Control-Allow-Origin: *, errors and refused bodies included, so web pages of any origin can upload, fetch
and query; DELETE is not allowed cross-origin.
Publish a manifest¶
curl -X POST "https://c2pa-store-test.iscc.io/v1/manifests" \
-H "Content-Type: application/c2pa" \
--data-binary @photo.c2pa
The body is a bare C2PA Manifest Store (a .c2pa sidecar), not the asset. Uploads are capped at 2 MB by default.
{
"manifestId": "urn:c2pa:F9168C5E-CEB2-4FAA-B6BF-329BF39FA1E4",
"manifestUrl": "https://c2pa-store-test.iscc.io/v1/manifests/urn%3Ac2pa%3AF9168C5E-CEB2-4FAA-B6BF-329BF39FA1E4",
"declaration": {
"status": "declared",
"isccId": "ISCC:MAIGKV5FAAXOXYAB",
"hub": "https://staging.iscc.id"
}
}
| Field | Meaning |
|---|---|
manifestId |
Label of the active manifest: the ID under which the store serves the Manifest Store |
manifestUrl |
Extension: the manifest address, also the gateway URL of the declaration |
declaration.status |
Extension: declared, pending, failed, skipped, disabled or withdrawn |
declaration.isccId |
Extension: ISCC-ID of the declaration, once declared |
declaration.hub |
Extension: the ISCC hub of the declaration |
The answer is 200 for a first upload and for the same bytes uploaded again. Manifest IDs are first come, first
served: other bytes under a taken ID, or under the ID of a deleted manifest, get 409 until an operator releases
the ID. The store accepts manifest IDs of at most 255 printable characters; a Manifest Store whose active manifest
has a longer or unprintable label is refused with 400 and the code manifest.idInvalid. A Manifest Store with
more than 64 indexable soft-binding blocks, more than 8 io.iscc.v0 blocks or more than 32 distinct ISCC-UNITs is
refused with 400 and the code manifest.tooManyBindings (Index). Status values are
explained on the Declarations page.
Query parameters¶
| Parameter | Default | Meaning |
|---|---|---|
declare |
deployment setting | Extension: false stores and indexes the manifest without declaring it |
returnReceipt |
false |
Accepted and ignored: the store issues no receipts |
Send declare=false when you declare the content on the ISCC Discovery Protocol yourself, with manifestUrl as
your gateway URL (Declarations). The public instances declare by default.
Fetch a manifest¶
curl -o photo.c2pa \
"https://c2pa-store-test.iscc.io/v1/manifests/urn:c2pa:F9168C5E-CEB2-4FAA-B6BF-329BF39FA1E4"
The answer is the Manifest Store exactly as uploaded, as application/c2pa, with the SHA-256 of the bytes as
ETag and Cache-Control: public, max-age=300. A request whose If-None-Match lists that tag (weak or strong) or
* gets 304. HEAD answers with the same headers and no body.
returnActiveManifest is accepted and ignored; the full Manifest Store is always returned.
Query by binding¶
alg is any algorithm on the
C2PA Soft Binding Algorithm List; value is the base64
binding value, percent-encoded in a GET request. For io.iscc.v0 the value is an ISCC-SEQ as specified in
IEP-0020.
curl "https://c2pa-store-test.iscc.io/v1/matches/byBinding?alg=io.iscc.v0&value=<percent-encoded base64 ISCC-SEQ>"
import base64
from urllib.parse import quote
import iscc_core as ic
units = [
"ISCC:EAD2RASIYU5IKLENP2OFI4CHZGRWYQCSW2WKX3Y6FJGOCXSYNYGLGBI", # Content-Code
"ISCC:GADQLNA7GRZESMRF2J7NZPNWGI3II2ST5YUN5SS6GVQ2ZQGJXPPYDNI", # Data-Code
"ISCC:IAD2KIVPJIWJZP3KQCESJL6SVT5APEZUPOJWM6HVTAXCF7OT3VFA4NY", # Instance-Code
]
value = quote(base64.b64encode(ic.encode_seq(units)).decode("ascii"), safe="")
url = f"https://c2pa-store-test.iscc.io/v1/matches/byBinding?alg=io.iscc.v0&value={value}&maxResults=5"
Response:
{
"matches": [
{
"manifestId": "urn:c2pa:F9168C5E-CEB2-4FAA-B6BF-329BF39FA1E4",
"similarityScore": 100,
"isccId": "ISCC:MAIGKV5FAAXOXYAB"
}
]
}
| Field | Meaning |
|---|---|
manifestId |
Fetch the manifest from GET /v1/manifests/{manifestId} on the same store |
similarityScore |
100 for an exact match or an equal Instance-Code, else the best unit similarity (score) |
isccId |
Extension: ISCC-ID of the store's declaration of this manifest, when it has one |
Matches carry no endpoint, because every match is stored here. An io.iscc.v0 value may hold at most 32
searchable units. Equal scores rank manifests with a trusted signing credential first, then earlier uploads.
maxResults defaults to 10; values above 100 are capped at 100. For values too long for a URL, send
POST /v1/matches/byBinding with {"alg": "io.iscc.v0", "value": "<base64>"} as JSON.
Errors¶
| Status | When |
|---|---|
400 |
Invalid parameter, body or Content-Length; algorithm not on the C2PA list; invalid base64 or ISCC-SEQ; refused Manifest Store |
401 |
DELETE without a valid operator token (WWW-Authenticate: Bearer) |
404 |
Unknown manifest ID (any longer than 255 characters or unprintable too) or API path; /.well-known/did.json when not declaring |
409 |
Upload whose manifest ID is taken by other bytes, or of a deleted manifest |
410 |
Manifest that was deleted |
413 |
Upload larger than the upload limit (2 MB by default), or another request body larger than 64 KB |
415 |
Upload not sent as application/c2pa |
429 |
Rate limit of the client address exceeded; Retry-After says how long to wait |
Error bodies are JSON with a detail member. A refused Manifest Store also carries code: the C2PA validation
status code of the first failure, or manifest.unreadable, manifest.missing, manifest.idInvalid,
manifest.tooManyBindings or claimSignature.missing. Unknown paths under /v1/ and /.well-known/ answer
{"detail": "Not found"}.