Skip to content

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

  1. Signed or stored. Either you upload an asset to /v1/sign and we sign it, or you upload an externally signed asset to /v1/manifests and 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.
  2. Bindings computed. Hard binding (SHA-256), DCT-pHash, dHash, ISCC, and chromaprint (for audio) are computed and indexed.
  3. 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.
  4. Webhooks fire. Subscribers to manifest.signed, manifest.stored, and anchor.completed get an HMAC-signed POST. See Webhooks.
  5. Resolvable. Anyone (no API key) can call /v1/verify with 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.