Chain of custody
Every attestation carries an append-only timeline of events — who touched it and when. It's returned as chain on the verification result and drives the evidentiary PDF.
Event shape
{
"event_type": "capture | edit | escalation",
"timestamp": "<RFC3339>",
"actor": "device:<id> | tool:<name> | adjuster:<id>",
"summary": "human-readable"
}
Event types
capture— the originating attestation, actordevice:<id>. Written when an asset is attested.edit: a post-capture modification by a tool, actortool:<name>. Every edit is recorded in the chain.escalation— an adjuster escalated the asset to SIU, actoradjuster:<id>. Written byPOST /v1/claims/:claim_id/escalate.
Why it matters
The chain is what turns a risk score into evidence. When a fraudulent claim is escalated, the evidentiary PDF renders this timeline so an investigator can see the whole custody history (capture device, any edits and the escalation) on one page. Chain events on a claim are paginated.
Evidence export
The PDF asserts our verification. For court or SIU use, POST /v1/claims/:id/export (enterprise) produces an independently checkable evidence bundle — a ZIP containing:
- each attestation's canonical bundle JSON (signature + cert chain) and its verification result (risk factors),
- the chain-of-custody events and the media content hashes (not the media bytes — custody of PII stays with you),
- a
manifest.jsonwhosemanifest_hashis the SHA-256 over the canonical list of every file (reusing@provenance/core), - an RFC 3161 timestamp of that manifest hash from a configured TSA (
TSA_URL), proving the evidence existed at a point in time. Without a TSA the export still succeeds and is markedtimestamp: "not_configured", - an embedded
verify-bundle.mjs(node, zero-dependency) that opposing counsel runs to re-check every hash offline:
curl -s -X POST "$API/v1/claims/CLM-…/export" -H "Authorization: Bearer $KEY" # → { export_id, manifest_hash, download_url, … }
curl -s "$API/v1/exports/$ID/download" -o evidence.zip && unzip evidence.zip -d bundle
node bundle/verify-bundle.mjs
# → "BUNDLE VERIFICATION PASSED — every hash matches the manifest." (tamper any file → FAIL, exit 1)
Exporting appends an exported chain event (who/when), meters one evidence_export, and the bundle is stored transiently (24h TTL). The manifest hash is the integrity root — the future anchor point for a transparency log if wanted.