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":           "CN=Hashproof Local Signing CA",
      "subject":          "CN=Hashproof Local Signer",
      "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"
  },
  "signedAssetUrl": null,
  "message": "Asset signed and manifest stored successfully"
}

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, chromaprint (for audio), and optionally ISCC are computed and indexed.
  3. Anchored. When an anchoring batch runs, the Merkle root over new manifests is recorded and each included manifest gets an inclusion proof 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 S3-compatible object storage.
  • 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.

The uploaded bytes are stored as the manifest record blob; when the byte-scan fallback extracts an embedded manifest, only the extracted manifest bytes are stored. The hard binding hash links the record to the asset.

Validation status

Each manifest carries a validationStatus: one of well_formed, valid, trusted, or unknown. Cryptographic checks run at parse time for embedded manifests; stored and resolved records report the status recorded at that point. Trusted means the manifest validated at parse time and the signer is on the C2PA trust list. Valid is the recorded status for managed-sign and synthetic records, and for parsed manifests whose checks passed without a trust-list match. Well_formed means the manifest parses but checks did not pass.