CONCEPTS
Manifests
Every signed or stored asset on Hashproof produces a manifest: a JSON-shaped object holding cryptographic claims, bindings, and identifiers. This page covers the shape, the lifecycle, and what gets stored on your behalf.
Shape
A representative response from POST /v1/sign:
{
"manifestId": "ce29...c4f1",
"manifest": {
"id": "ce29...c4f1",
"title": "Field photo, Reykjavik 2026",
"format": "image/jpeg",
"instanceId": "urn:uuid:9b4c...",
"claimGenerator": "Hashproof/1.0",
"signatureInfo": {
"issuer": "Hashproof Managed Signing CA",
"subject": "u_123 / managed",
"algorithm": "ES256+ML-DSA-65",
"signedAt": "2026-04-29T10:14:32.001Z",
"certChain": [ … ]
},
"assertions": [
{ "label": "c2pa.hash.data", "data": { "algorithm": "SHA-256", "hash": "..." } },
{ "label": "c2pa.actions", "data": { "actions": [ { "action": "c2pa.created" } ] } }
],
"ingredients": [],
"hardBinding": { "algorithm": "SHA-256", "hash": "..." },
"validationStatus":"valid",
"rawSize": 1812,
"createdAt": "2026-04-29T10:14:32.001Z"
},
"softBindings": [
{ "algorithm": "phash-dct-64", "value": "..." },
{ "algorithm": "dhash-64", "value": "..." }
]
}Identifiers
- manifestId: server- assigned UUID. Stable across the manifest's lifetime; this is what you store in your application.
- instanceId: C2PA URN identifying the specific signing event. Useful when the same asset is re-signed in different contexts.
- cid (optional): the IPFS Content Identifier for the raw manifest bytes. Computed locally on every store. Pinning to an IPFS daemon is opt-in.
- hardBinding.hash: SHA-256 of the asset bytes. The hard-binding lookup at
GET /v1/manifests?hash=uses this.
Lifecycle
- Signed or stored. Either you upload an asset to
/v1/signand we sign it, or you upload an externally signed asset to/v1/manifestsand we extract its embedded C2PA manifest. Either way, the manifest lands in your account scoped to the API key (or managed signing key) that uploaded it. - Bindings computed. Hard binding (SHA-256), DCT-pHash, dHash, ISCC, and chromaprint (for audio) are computed and indexed.
- Anchored. Within the hour, the Merkle root of new manifests is anchored to Base L2. Once the on-chain transaction confirms, the manifest gets a verifiable timestamp via
GET /v1/manifests/:id/proof. - Webhooks fire. Subscribers to
manifest.signed,manifest.stored, andanchor.completedget an HMAC-signed POST. See Webhooks. - Resolvable. Anyone (no API key) can call
/v1/verifywith the asset and recover the manifest via embedded → hard binding → soft binding cascade.
What we store
For each manifest we persist:
- The raw manifest bytes (CBOR), in Cloudflare R2.
- Indexed metadata (signer, format, hard binding hash, IPFS CID, validation status, claim generator, assertions) in Postgres.
- Soft bindings keyed by manifest ID for the resolver.
- Ingredient edges (parent / component / input relationships) for lineage queries.
We do not store the original asset bytes. The hard binding hash is what we keep; the asset itself stays on your infrastructure.
Validation status
Each manifest carries a validationStatus: one of well_formed, valid, trusted, or unknown. Trusted means valid AND the signer is on the C2PA trust list. Valid means the cryptographic checks passed but the signer is not on the public trust list. Well_formed means the manifest parses but signature checks failed.