{"openapi":"3.1.0","info":{"title":"Hashproof API","description":"Content Provenance Infrastructure — C2PA Manifest Repository, Resolution, Verification, and Managed Signing API.","version":"0.1.0","contact":{"name":"Hashproof","url":"https://hashproof.com","email":"api@hashproof.com"},"license":{"name":"MIT","url":"https://opensource.org/licenses/MIT"}},"servers":[{"url":"https://api.hashproof.ai","description":"Production"},{"url":"http://localhost:3001","description":"Local development"}],"security":[{"BearerAuth":[]}],"tags":[{"name":"Manifests","description":"C2PA manifest storage and retrieval"},{"name":"Resolve","description":"Soft binding resolution via perceptual hashing"},{"name":"Verify","description":"One-call content provenance verification"},{"name":"Signing","description":"Managed C2PA signing (\"Let's Encrypt for C2PA\")"},{"name":"Compliance","description":"EU AI Act compliance reporting"},{"name":"Blockchain","description":"Merkle tree anchoring and proof retrieval"},{"name":"API Keys","description":"API key management"},{"name":"Webhooks","description":"Webhook registration and management"},{"name":"Fingerprint","description":"Perceptual fingerprint computation"},{"name":"Billing","description":"Subscription and payment management"},{"name":"Health","description":"API status and health checks"},{"name":"Streaming","description":"CMAF segment recording for live streaming provenance (C2PA v2.3)"},{"name":"Batch","description":"Batch operations — process multiple items in one request"},{"name":"Training Data","description":"Training data lineage — register datasets and check membership (Attestry bridge)"}],"paths":{"/v1/manifests":{"post":{"operationId":"storeManifest","summary":"Store a C2PA manifest","description":"Upload a digital asset to extract and store its embedded C2PA manifest. Perceptual fingerprints (soft bindings) are computed automatically for future resolution.","tags":["Manifests"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"The digital asset file (JPEG, PNG, WebP, TIFF, MP4, or PDF)"},"title":{"type":"string","description":"Human-readable title for the asset"}}}}}},"responses":{"201":{"description":"Manifest stored successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreResult"},"example":{"manifestId":"550e8400-e29b-41d4-a716-446655440000","manifest":{"id":"550e8400-e29b-41d4-a716-446655440000","title":"Press Photo 2024","format":"image/jpeg","instanceId":"xmp:iid:abc123","claimGenerator":"Hashproof/0.1.0","validationStatus":"valid","rawSize":2048,"signatureInfo":{"issuer":"CN=Hashproof Local Signing CA","subject":"CN=Hashproof Local Signer","algorithm":"ES256+ML-DSA-65","certSerialNumber":"","signedAt":"2024-06-01T12:00:00Z","certChain":[]},"assertions":[{"label":"c2pa.hash.data","data":{"algorithm":"SHA-256","hash":"8f3d2a1b..."},"kind":"cbor"}],"ingredients":[],"hardBinding":{"algorithm":"SHA-256","hash":"8f3d2a1b..."},"createdAt":"2024-06-01T12:00:00Z","cid":"bafkreigh2akiscaildc..."},"softBindings":[]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"413":{"description":"File exceeds maximum size (50 MB)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"},"example":{"error":"File exceeds maximum size of 52428800 bytes","code":"FILE_TOO_LARGE","statusCode":413}}}}}},"get":{"operationId":"listManifests","summary":"List manifests or lookup by hash","description":"When `hash` is provided, looks up a single manifest by its hard binding SHA-256 hash. Otherwise, returns a paginated list of manifests for the authenticated API key.","tags":["Manifests"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"parameters":[{"name":"hash","in":"query","description":"SHA-256 hash of the original asset for direct lookup","schema":{"type":"string"}},{"name":"page","in":"query","description":"Page number (1-indexed)","schema":{"type":"integer","default":1,"minimum":1}},{"name":"perPage","in":"query","description":"Results per page","schema":{"type":"integer","default":20,"minimum":1,"maximum":100}},{"name":"search","in":"query","description":"Search term to filter manifests by title","schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated manifest list or single manifest (when hash provided)","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/PaginatedManifests"},{"$ref":"#/components/schemas/ManifestData"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/manifests/{id}":{"get":{"operationId":"getManifest","summary":"Retrieve a manifest by ID","description":"Fetch the full manifest data for a given manifest UUID.","tags":["Manifests"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Manifest UUID","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Manifest data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManifestData"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/manifests/{id}/lineage":{"get":{"operationId":"getManifestLineage","summary":"Get manifest provenance DAG (ancestors + descendants)","description":"Traverses the manifest lineage graph via ingredient hashes. Returns a bounded DAG of nodes (manifests) and edges (relationships). Ancestors are manifests referenced as ingredients by the root; descendants are manifests that reference the root as an ingredient. Traversal is depth-bounded (1-10) and cycle-safe.","tags":["Manifests"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Manifest UUID","schema":{"type":"string","format":"uuid"}},{"name":"direction","in":"query","description":"Traversal direction","schema":{"type":"string","enum":["ancestors","descendants","both"],"default":"both"}},{"name":"maxDepth","in":"query","description":"Maximum traversal depth in each direction (clamped to [0, 10])","schema":{"type":"integer","minimum":0,"maximum":10,"default":5}}],"responses":{"200":{"description":"Lineage DAG with nodes, edges, and truncation flag","content":{"application/json":{"schema":{"type":"object","required":["root","nodes","edges","totalNodes","maxDepthReached"],"properties":{"root":{"type":"string","format":"uuid","description":"The queried manifest ID"},"nodes":{"type":"array","items":{"type":"object","required":["id","title","format","claimGenerator","createdAt","depth"],"properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"format":{"type":"string"},"claimGenerator":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"depth":{"type":"integer","description":"0 = root, negative = ancestors, positive = descendants"}}}},"edges":{"type":"array","items":{"type":"object","required":["from","to","relationship"],"properties":{"from":{"type":"string","format":"uuid"},"to":{"type":"string","format":"uuid"},"relationship":{"type":"string","enum":["parentOf","componentOf","inputTo"]}}}},"totalNodes":{"type":"integer"},"maxDepthReached":{"type":"boolean","description":"True if any traversal branch was truncated at maxDepth"}}},"example":{"root":"550e8400-e29b-41d4-a716-446655440000","nodes":[{"id":"550e8400-e29b-41d4-a716-446655440000","title":"Edited Photo","format":"image/jpeg","claimGenerator":"Adobe/Photoshop","createdAt":"2024-06-01T12:00:00Z","depth":0},{"id":"aaaaaaaa-e29b-41d4-a716-446655440000","title":"Camera Original","format":"image/jpeg","claimGenerator":"Nikon/Z9","createdAt":"2024-05-28T09:15:00Z","depth":-1}],"edges":[{"from":"550e8400-e29b-41d4-a716-446655440000","to":"aaaaaaaa-e29b-41d4-a716-446655440000","relationship":"parentOf"}],"totalNodes":2,"maxDepthReached":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Manifest not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/manifests/{id}/proof":{"get":{"operationId":"getManifestProof","summary":"Get the Merkle inclusion proof","description":"Retrieve the Merkle inclusion proof for an anchored manifest. The proof verifies the manifest against its batch's Merkle root. The anchor's transactionHash and blockNumber are null. Batch roots are not submitted on-chain.","tags":["Blockchain"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Manifest UUID","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Merkle proof with anchor details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerkleProof"},"example":{"manifestId":"550e8400-e29b-41d4-a716-446655440000","root":"a1b2c3d4e5f6...","proof":["hash1","hash2","hash3"],"index":42,"anchor":{"id":"anchor-uuid","merkleRoot":"a1b2c3d4e5f6...","transactionHash":null,"chainId":8453,"blockNumber":null,"batchSize":1000,"anchoredAt":"2024-01-15T12:00:00Z"},"verified":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"No Merkle proof found for this manifest","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"},"example":{"error":"No Merkle proof found for this manifest. It may not have been anchored yet.","code":"NO_PROOF","statusCode":404}}}}}}},"/v1/resolve":{"post":{"operationId":"resolveByFile","summary":"Resolve provenance by file upload or C2PA soft-binding request","description":"Resolves content provenance via soft binding. Accepts either a multipart/form-data file upload (server computes the fingerprint) OR an application/json body with a pre-computed C2PA-aligned soft_binding request. Implements the C2PA Soft Binding Resolution concept (spec v2.1 terminology) with Hashproof extensions for API ergonomics and backward compatibility.","tags":["Resolve"],"security":[{"BearerAuth":[]},{"apiKey":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"The file to resolve"},"algorithm":{"type":"string","enum":["phash-dct-64","dhash-64","chromaprint","iscc","neural-sscd-v1"],"default":"phash-dct-64","description":"Soft binding algorithm"},"maxResults":{"type":"integer","default":10,"description":"Maximum number of matches to return"}}}},"application/json":{"schema":{"type":"object","required":["soft_binding"],"description":"C2PA-aligned soft binding resolution request. See C2PASoftBindingRequest.","properties":{"content_hash":{"type":"object","description":"Optional hard binding hash of the original asset","properties":{"value":{"type":"string","description":"Hex-encoded hash value"},"algorithm":{"type":"string","example":"sha256"}}},"soft_binding":{"type":"object","required":["value","algorithm"],"description":"The perceptual fingerprint to resolve","properties":{"value":{"type":"string","description":"Hex-encoded fingerprint (max 64 chars)"},"algorithm":{"type":"string","enum":["phash-dct-64","dhash-64","chromaprint","iscc","neural-sscd-v1"]}}},"max_results":{"type":"integer","default":10,"description":"Maximum number of matches to return (1-100)"},"threshold":{"type":"integer","default":10,"description":"Maximum Hamming distance for a match (0-64)"}}},"example":{"soft_binding":{"value":"a1b2c3d4e5f60718","algorithm":"phash-dct-64"},"max_results":10,"threshold":10}}}},"responses":{"200":{"description":"Resolution results with matching manifests. Response envelope includes both Hashproof-native fields (matches, query, searchTimeMs) and a C2PA-aligned `c2pa` envelope. The `C2PA-Version` response header indicates the C2PA spec version targeted.","headers":{"C2PA-Version":{"description":"C2PA specification version this endpoint targets","schema":{"type":"string","example":"2.1"}}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ResolveResult"},{"type":"object","required":["c2pa"],"properties":{"c2pa":{"type":"object","required":["version","resolution_results"],"description":"C2PA Soft Binding Resolution envelope","properties":{"version":{"type":"string","example":"2.1"},"resolution_results":{"type":"array","items":{"type":"object","required":["manifest_uri","confidence","binding_type","distance"],"properties":{"manifest_uri":{"type":"string","example":"urn:hashproof:manifest:550e8400-e29b-41d4-a716-446655440000"},"confidence":{"type":"number","format":"float","minimum":0,"maximum":1},"binding_type":{"type":"string","example":"phash-dct-64"},"distance":{"type":"integer","minimum":0}}}}}}}}]},"example":{"matches":[{"manifestId":"550e8400-e29b-41d4-a716-446655440000","distance":2,"similarity":0.96875,"manifest":{"id":"550e8400-e29b-41d4-a716-446655440000","title":"Original Photo","instanceId":"urn:uuid:01931f2e-aaaa-bbbb-cccc-1234567890ab","format":"image/jpeg","signatureInfo":{"issuer":"CN=Hashproof Local Signing CA","subject":"CN=Hashproof Local Signer","algorithm":"ES256+ML-DSA-65","certSerialNumber":"","signedAt":"2024-06-01T12:00:00Z","certChain":[]},"claimGenerator":"Hashproof/1.0","assertions":[{"label":"c2pa.hash.data","data":{"algorithm":"SHA-256","hash":"8f3d2a1b..."},"kind":"cbor"}],"ingredients":[],"hardBinding":{"algorithm":"SHA-256","hash":"8f3d2a1b..."},"validationStatus":"valid","rawSize":1024,"createdAt":"2024-06-01T12:00:00Z","cid":"bafkreigh2akiscaildc..."}}],"query":{"fingerprint":"a1b2c3d4e5f60718","algorithm":"phash-dct-64"},"searchTimeMs":12,"c2pa":{"version":"2.1","resolution_results":[{"manifest_uri":"urn:hashproof:manifest:550e8400-e29b-41d4-a716-446655440000","confidence":0.97,"binding_type":"phash-dct-64","distance":2}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}},"get":{"operationId":"resolveByFingerprint","summary":"Resolve provenance by pre-computed fingerprint","description":"Look up matching manifests using a pre-computed perceptual fingerprint. No file upload required.","tags":["Resolve"],"security":[{"BearerAuth":[]},{"apiKey":[]}],"parameters":[{"name":"fingerprint","in":"query","required":true,"description":"Hex-encoded perceptual fingerprint (minimum 8 characters)","schema":{"type":"string","minLength":8}},{"name":"algorithm","in":"query","description":"Soft binding algorithm used to compute the fingerprint","schema":{"type":"string","enum":["phash-dct-64","dhash-64","chromaprint","iscc","neural-sscd-v1"],"default":"phash-dct-64"}},{"name":"maxResults","in":"query","description":"Maximum number of matches to return","schema":{"type":"integer","default":10}},{"name":"threshold","in":"query","description":"Maximum Hamming distance for a match","schema":{"type":"integer","default":10}}],"responses":{"200":{"description":"Resolution results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveResult"}}}},"400":{"description":"Invalid fingerprint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"},"example":{"error":"Invalid fingerprint. Must be a hex string of at least 8 characters.","code":"INVALID_FINGERPRINT","statusCode":400}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/verify":{"post":{"operationId":"verifyContent","summary":"Verify content provenance","description":"One-call verification endpoint. Checks embedded C2PA manifests, hard binding (SHA-256 hash match), and soft binding (perceptual hash resolution). Returns provenance source, manifest data, cryptographic validation, and trust status. Accepts anonymous requests; per-IP rate limiting applies.","tags":["Verify"],"security":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"The file to verify"}}}}}},"responses":{"200":{"description":"Verification result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyResult"},"example":{"hasProvenance":true,"source":"resolved","resolvedVia":"hard-binding-sha256","manifest":{"id":"01931f2e-aaaa-bbbb-cccc-1234567890ab","title":"Press Photo","format":"image/jpeg","validationStatus":"valid","instanceId":"urn:uuid:01931f2e-aaaa-bbbb-cccc-1234567890ab","signatureInfo":{"issuer":"CN=Hashproof Local Signing CA","subject":"CN=Hashproof Local Signer","algorithm":"ES256","certSerialNumber":"","signedAt":"2024-06-01T12:00:00Z","certChain":[]},"claimGenerator":"Hashproof/1.0","assertions":[{"label":"c2pa.hash.data","data":{"algorithm":"SHA-256","hash":"8f3d2a1b..."},"kind":"cbor"}],"ingredients":[],"hardBinding":{"algorithm":"SHA-256","hash":"8f3d2a1b..."},"rawSize":1024,"createdAt":"2024-06-01T12:00:00Z"},"validation":{"status":"valid","errors":[],"warnings":[],"trustChainValid":true,"signerTrusted":false},"trustStatus":"untrusted"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"413":{"description":"File exceeds the 50 MB upload limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/sign":{"post":{"operationId":"signContent","summary":"Sign a digital asset with C2PA","description":"Managed signing service (\"Let's Encrypt for C2PA\"). Upload a file and Hashproof creates a signed C2PA manifest on your behalf without you needing to manage certificates or PKI. Requires the `sign` scope on your API key. Keys created with default scopes lack the sign scope and receive 403. Dashboard session callers sign with their managed signing key.","tags":["Signing"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"The digital asset to sign"},"title":{"type":"string","description":"Human-readable title for the asset"}}}}}},"responses":{"201":{"description":"Asset signed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignResult"},"example":{"manifestId":"550e8400-e29b-41d4-a716-446655440000","manifest":{"id":"550e8400-e29b-41d4-a716-446655440000","format":"image/jpeg","validationStatus":"valid","instanceId":"urn:uuid:01931f2e-aaaa-bbbb-cccc-1234567890ab","signatureInfo":{"issuer":"CN=Hashproof Local Signing CA","subject":"CN=Hashproof Local Signer","algorithm":"ES256+ML-DSA-65","certSerialNumber":"3f8a1c9e2b7d4a60","signedAt":"2024-06-01T12:00:00Z","certChain":["-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"]},"claimGenerator":"Hashproof/1.0","assertions":[{"label":"c2pa.hash.data","data":{"algorithm":"SHA-256","hash":"8f3d2a1b..."},"kind":"cbor"},{"label":"c2pa.actions","data":{"actions":[{"action":"c2pa.created","parameters":{"description":"Signed via Hashproof managed signing"}}]},"kind":"cbor"},{"label":"hashproof.pq-signature","data":{"signature":"SIMULATED-PQ:3a9f...","publicKey":"SIMULATED-PQ-KEY:...","algorithm":"ML-DSA-65","simulated":true,"generatedAt":"2024-06-01T12:00:00Z"},"kind":"cbor"}],"ingredients":[],"hardBinding":{"algorithm":"SHA-256","hash":"8f3d2a1b..."},"rawSize":1024,"createdAt":"2024-06-01T12:00:00Z","cid":"bafkreigh2akiscaildc...","title":"Field photo, 2026"},"signedAssetUrl":null,"message":"Asset signed and manifest stored successfully"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Missing required `sign` scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"},"example":{"error":"Insufficient permissions. Required scope: sign","code":"FORBIDDEN","statusCode":403}}}}}}},"/v1/compliance/reports":{"post":{"operationId":"generateComplianceReport","summary":"Generate a compliance report","description":"Analyze all manifests owned by the calling user, across all of their API keys, against EU AI Act Article 50 requirements. Checks AI generation disclosure, certificate validation status, and content binding. The report is generated synchronously, persisted, and returned in full.","tags":["Compliance"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reportType":{"type":"string","enum":["eu-ai-act"],"default":"eu-ai-act","description":"Report type. Only eu-ai-act is supported."}}},"example":{"reportType":"eu-ai-act"}}}},"responses":{"201":{"description":"Compliance report generated and persisted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComplianceReport"},"example":{"id":"550e8400-e29b-41d4-a716-446655440000","reportType":"eu-ai-act","generatedAt":"2026-06-10T12:00:00.000Z","summary":{"totalAssets":150,"assetsWithProvenance":147,"compliancePercentage":94,"aiGeneratedCount":30,"aiDisclosedCount":28,"certificateIssues":3},"details":[{"manifestId":"9b2f1c4e-7a31-4f0b-8a6e-2d5c9e8f1a07","assetTitle":"AI Generated Banner","issues":["AI-generated content lacks action disclosure assertion (Article 50 §1)"],"status":"non_compliant"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}},"get":{"operationId":"listComplianceReports","summary":"List compliance reports","description":"List the calling user's persisted compliance reports, newest first. Each row carries the summary counters; the full details array is returned by the per-report detail endpoint.","tags":["Compliance"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"parameters":[{"name":"page","in":"query","schema":{"type":"integer","default":1},"description":"Page number. Numeric values below 1 are clamped to 1."},{"name":"perPage","in":"query","schema":{"type":"integer","default":20,"maximum":100},"description":"Rows per page. Numeric values outside 1 to 100 are clamped."}],"responses":{"200":{"description":"Paginated report list, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ComplianceReportListItem"}},"total":{"type":"integer"},"page":{"type":"integer"},"perPage":{"type":"integer"}},"required":["data","total","page","perPage"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/compliance/reports/{id}":{"get":{"operationId":"getComplianceReport","summary":"Get a compliance report","description":"Fetch one persisted report in full, including the per-manifest details array. Owner only.","tags":["Compliance"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The full report","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComplianceReport"},"example":{"id":"550e8400-e29b-41d4-a716-446655440000","reportType":"eu-ai-act","generatedAt":"2026-06-10T12:00:00.000Z","summary":{"totalAssets":150,"assetsWithProvenance":147,"compliancePercentage":94,"aiGeneratedCount":30,"aiDisclosedCount":28,"certificateIssues":3},"details":[{"manifestId":"9b2f1c4e-7a31-4f0b-8a6e-2d5c9e8f1a07","assetTitle":"AI Generated Banner","issues":["AI-generated content lacks action disclosure assertion (Article 50 §1)"],"status":"non_compliant"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"deleteComplianceReport","summary":"Delete a compliance report","description":"Delete one persisted report. Owner only.","tags":["Compliance"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Report deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]},"example":{"success":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/anchor":{"post":{"operationId":"triggerAnchoring","summary":"Trigger Merkle tree anchoring","description":"Trigger the anchoring process for un-anchored manifests, oldest first, capped per run. Builds a Merkle tree and stores per-manifest inclusion proofs. Available to any authenticated caller (API key or session). Batch roots are not submitted on-chain.","tags":["Blockchain"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"responses":{"200":{"description":"Anchoring result","content":{"application/json":{"schema":{"type":"object","properties":{"anchorId":{"type":"string","description":"The anchor batch UUID"},"merkleRoot":{"type":"string","description":"The computed Merkle root hash"},"batchSize":{"type":"integer","description":"Number of manifests in this batch"},"message":{"type":"string"}}},"example":{"anchorId":"anchor-uuid","merkleRoot":"a1b2c3d4e5f6...","batchSize":500,"message":"Merkle tree computed and inclusion proofs stored. The batch root is not submitted on-chain."}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/api-keys":{"post":{"operationId":"createApiKey","summary":"Create a new API key","description":"Generate a new API key for the authenticated session user. The raw key is returned only once at creation and must be stored securely. Session-JWT only; API keys are not accepted.","tags":["API Keys"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"A human-readable name for the key"},"scopes":{"type":"array","items":{"type":"string","enum":["manifests:read","manifests:write","resolve","verify","sign","reports"]},"description":"Permission scopes. Defaults to [manifests:read, manifests:write, resolve, verify]."},"expiresInDays":{"type":"number","description":"Number of days until the key expires. Omit for no expiration."}}},"example":{"name":"Production Key","scopes":["manifests:read","manifests:write","resolve","verify","sign"],"expiresInDays":365}}}},"responses":{"201":{"description":"API key created. The raw key is only shown once.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"key":{"type":"string","description":"The raw API key. Only returned at creation."},"keyPrefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"expiresAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"warning":{"type":"string"}}},"example":{"id":"key-uuid","name":"Production Key","key":"hpsk_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2","keyPrefix":"hpsk_a1b2c3d","scopes":["manifests:read","manifests:write","resolve","verify","sign"],"expiresAt":"2025-06-01T00:00:00Z","createdAt":"2024-06-01T00:00:00Z","warning":"Store this key securely. It will not be shown again."}}}}},"security":[{"SessionAuth":[]}]},"get":{"operationId":"listApiKeys","summary":"List API keys","description":"List the API keys owned by the authenticated session user. Returns a JSON array. Session-JWT only; API keys are not accepted.","tags":["API Keys"],"responses":{"200":{"description":"List of API keys","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyInfo"}}}}}},"security":[{"SessionAuth":[]}]}},"/v1/api-keys/{id}":{"delete":{"operationId":"revokeApiKey","summary":"Revoke an API key","description":"Permanently deactivate an API key. This action cannot be undone. Session-JWT only; API keys are not accepted.","tags":["API Keys"],"parameters":[{"name":"id","in":"path","required":true,"description":"API key UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Key revoked successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}},"example":{"success":true,"message":"API key revoked"}}}},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"SessionAuth":[]}]}},"/v1/webhooks":{"post":{"operationId":"createWebhook","summary":"Register a webhook endpoint","description":"Register a URL to receive webhook notifications for specified events. A signing secret is generated and returned for verifying webhook payloads.","tags":["Webhooks"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri","description":"The URL to send webhook events to"},"events":{"type":"array","items":{"type":"string"},"description":"Event types to subscribe to (e.g., manifest.stored, manifest.verified)"}}},"example":{"url":"https://example.com/webhooks/hashproof","events":["manifest.stored","manifest.verified","anchor.completed"]}}}},"responses":{"201":{"description":"Webhook registered","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"}},"secret":{"type":"string","description":"HMAC signing secret for verifying payloads. A 64-character hex string, shown only once."},"createdAt":{"type":"string","format":"date-time"},"isActive":{"type":"boolean"},"warning":{"type":"string","description":"Reminder that the secret is shown only once."}}},"example":{"id":"wh-uuid","url":"https://example.com/webhooks/hashproof","events":["manifest.stored","manifest.verified"],"secret":"a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2","isActive":true,"warning":"Store this secret. It will not be shown again.","createdAt":"2024-06-01T12:00:00Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/fingerprint":{"post":{"operationId":"computeFingerprint","summary":"Compute a perceptual fingerprint","description":"Compute a perceptual fingerprint for a file without resolving against the manifest repository. Useful for pre-computing fingerprints for later resolution.","tags":["Fingerprint"],"security":[{"BearerAuth":[]},{"apiKey":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"The file to fingerprint"},"algorithm":{"type":"string","enum":["phash-dct-64","dhash-64","chromaprint","iscc","neural-sscd-v1"],"default":"phash-dct-64","description":"The fingerprinting algorithm to use"}}}}}},"responses":{"200":{"description":"Computed fingerprint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FingerprintResult"},"example":{"fingerprint":"a1b2c3d4e5f60718","algorithm":"phash-dct-64","bitLength":64}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/manifests/{id}/raw":{"get":{"operationId":"getManifestRaw","summary":"Download raw manifest blob","description":"Download the stored manifest record blob. For assets with an embedded C2PA manifest this is the extracted manifest bytes; for managed signing and synthetic records it is the stored record bytes, typically the uploaded asset bytes. Served with an application/cbor content type.","tags":["Manifests"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Raw manifest blob","content":{"application/cbor":{"schema":{"type":"string","format":"binary"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/anchors":{"get":{"operationId":"listAnchors","summary":"List Merkle anchor batches","description":"List the 20 most recent Merkle anchor batches, newest first.","tags":["Blockchain"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"responses":{"200":{"description":"The 20 most recent anchor batches, newest first.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MerkleAnchor"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/webhooks/{id}":{"delete":{"operationId":"deleteWebhook","summary":"Delete a webhook","description":"Deactivate a webhook (soft delete). The webhook stops receiving deliveries; the record is retained.","tags":["Webhooks"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Webhook deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/status":{"get":{"operationId":"getStatus","summary":"API health status","description":"Returns the operational status of the Hashproof API.","tags":["Health"],"responses":{"200":{"description":"Service status","content":{"application/json":{"schema":{"type":"object","required":["status","services"],"properties":{"status":{"type":"string"},"services":{"type":"object","description":"Per-service health probes: api, database, storage, cache, imageAnalysis, trustList, rateLimiter.","additionalProperties":true}}}}}}},"security":[]}},"/v1/billing/subscription":{"get":{"operationId":"getSubscription","summary":"Get current subscription","description":"Returns the current subscription tier, subscription state, and billing period. Tier quotas are reported by GET /v1/usage.","tags":["Billing"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"responses":{"200":{"description":"Subscription details","content":{"application/json":{"schema":{"type":"object","required":["tier","subscription","billingConfigured"],"properties":{"tier":{"type":"string","enum":["free","growth","scale","enterprise"]},"subscription":{"type":"object","required":["status","stripeSubId","currentPeriodStart","currentPeriodEnd","cancelAtPeriodEnd"],"properties":{"status":{"type":"string","description":"Subscription status as recorded from Stripe; 'inactive' when no subscription exists."},"stripeSubId":{"type":["string","null"]},"currentPeriodStart":{"type":["string","null"],"format":"date-time"},"currentPeriodEnd":{"type":["string","null"],"format":"date-time"},"cancelAtPeriodEnd":{"type":"boolean"}}},"billingConfigured":{"type":"boolean","description":"False when Stripe is not configured on the server."}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/billing/checkout-session":{"post":{"operationId":"createCheckoutSession","summary":"Create Stripe checkout session","description":"Create a Stripe Checkout session for a plan upgrade and return its URL. Returns 503 when Stripe is not configured on the server.","tags":["Billing"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tier"],"properties":{"tier":{"type":"string","enum":["growth","scale"]}}}}}},"responses":{"200":{"description":"Checkout session","content":{"application/json":{"schema":{"type":"object","required":["checkoutUrl","sessionId"],"properties":{"checkoutUrl":{"type":["string","null"],"description":"Stripe-hosted Checkout URL to redirect the user to."},"sessionId":{"type":"string","description":"Stripe Checkout session id."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"User not found."},"503":{"description":"Billing is not configured on this server (Stripe key or tier price id missing)."}}}},"/v1/billing/webhook":{"post":{"operationId":"handleBillingWebhook","summary":"Stripe webhook handler","description":"Receives Stripe webhook events for subscription lifecycle management. Authenticated by Stripe webhook signature, not by API credentials.","tags":["Billing"],"responses":{"200":{"description":"Webhook signature verified and the event accepted. Event-handler failures are logged server-side and still return 200 (Stripe is not asked to retry), so 200 does not guarantee the event was applied.","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean"}}}}}},"202":{"description":"Event acknowledged but not processed: Stripe is not configured on this server (no STRIPE_SECRET_KEY or STRIPE_WEBHOOK_SECRET). The event is dropped.","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean"},"processed":{"type":"boolean"}}}}}},"400":{"description":"Missing or invalid stripe-signature header, or the raw request body was unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}},"security":[]}},"/v1/stream/start":{"post":{"operationId":"startStreamSession","summary":"Initialize a streaming provenance session","description":"Creates a root C2PA manifest for a live stream and a StreamSession record. Subsequent CMAF segments get hash-bound per-segment provenance records via POST /v1/stream/sign. The root manifest's asset payload is a deterministic seed derived from session metadata.","tags":["Streaming"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","format"],"properties":{"title":{"type":"string","minLength":1,"maxLength":500,"description":"Human-readable title for the live stream"},"format":{"type":"string","minLength":1,"maxLength":120,"description":"Media format; typically 'video/mp4' for CMAF fragmented MP4"},"metadata":{"type":"object","additionalProperties":true,"description":"Optional arbitrary metadata persisted inside the root manifest's seed payload"}}},"example":{"title":"Live Broadcast 2026-04-14","format":"video/mp4","metadata":{"channel":"news","region":"us-east"}}}}},"responses":{"201":{"description":"Stream session initialized","content":{"application/json":{"schema":{"type":"object","required":["streamId","rootManifestId","status","message"],"properties":{"streamId":{"type":"string","format":"uuid"},"rootManifestId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["active"]},"message":{"type":"string"}}},"example":{"streamId":"550e8400-e29b-41d4-a716-446655440000","rootManifestId":"aaaaaaaa-e29b-41d4-a716-446655440000","status":"active","message":"Stream session initialized. Send segments to POST /v1/stream/sign."}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"description":"Failed to initialize stream session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/stream/sign":{"post":{"operationId":"signStreamSegment","summary":"Record a CMAF segment against an active stream session","description":"Uploads a fragmented MP4 segment and records it as a hash-bound child manifest of the stream's root. The segment manifest includes a 'c2pa.actions' assertion with action 'c2pa.streaming.segment' and an ingredient entry (relationship 'componentOf') referencing the root manifest's hard binding hash.","tags":["Streaming"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file","streamId","sequenceNum"],"properties":{"file":{"type":"string","format":"binary","description":"CMAF segment binary (fragmented MP4)"},"streamId":{"type":"string","format":"uuid","description":"Stream session UUID returned by /v1/stream/start"},"sequenceNum":{"type":"integer","minimum":0,"description":"Ordering sequence number for this segment (must be unique within the session)"},"durationMs":{"type":"integer","minimum":0,"description":"Optional segment duration in milliseconds"},"title":{"type":"string","description":"Optional human-readable title override for the segment manifest"}}}}}},"responses":{"201":{"description":"Segment recorded and persisted","content":{"application/json":{"schema":{"type":"object","required":["segmentManifestId","streamId","sequenceNum","rootManifestId"],"properties":{"segmentManifestId":{"type":"string","format":"uuid"},"streamId":{"type":"string","format":"uuid"},"sequenceNum":{"type":"integer"},"rootManifestId":{"type":"string","format":"uuid"}}}}}},"400":{"description":"Missing file, invalid streamId/sequenceNum, empty segment, or stream is not active","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Stream session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Duplicate sequenceNum within the session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"410":{"description":"Stream root manifest has been deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"413":{"description":"Segment exceeds maximum size","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/stream/end":{"post":{"operationId":"endStreamSession","summary":"Mark a stream session as completed","description":"Sets the stream's status to 'completed'. Idempotent — calling /end on an already-ended stream returns 200 with the current state rather than an error.","tags":["Streaming"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["streamId"],"properties":{"streamId":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"Stream ended (or already ended)","content":{"application/json":{"schema":{"type":"object","required":["streamId","status","totalSegments","rootManifestId"],"properties":{"streamId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["active","completed","failed"]},"totalSegments":{"type":"integer"},"rootManifestId":{"type":"string","format":"uuid"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Stream session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/stream/{streamId}/verify":{"get":{"operationId":"verifyStream","summary":"Verify the integrity of a streaming provenance session","description":"Traverses the session's segments in sequence order, checks for gaps (missing sequence numbers), and reports per-segment verification state. A segment is considered verified when its manifest exists and carries a non-empty hard binding hash.","tags":["Streaming"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"parameters":[{"name":"streamId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Stream verification report","content":{"application/json":{"schema":{"type":"object","required":["streamId","rootManifestId","status","totalSegments","verifiedSegments","failedSegments","gaps","segments"],"properties":{"streamId":{"type":"string","format":"uuid"},"rootManifestId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["active","completed","failed"]},"totalSegments":{"type":"integer"},"verifiedSegments":{"type":"integer"},"failedSegments":{"type":"integer"},"gaps":{"type":"array","items":{"type":"integer"},"description":"Sorted list of missing sequence numbers between min and max observed"},"segments":{"type":"array","items":{"type":"object","required":["sequenceNum","manifestId","verified"],"properties":{"sequenceNum":{"type":"integer"},"manifestId":{"type":"string","format":"uuid"},"verified":{"type":"boolean"},"durationMs":{"type":["integer","null"]}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Stream session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/batch/verify":{"post":{"operationId":"batchVerify","summary":"Batch verify multiple files","description":"Upload multiple files in a single multipart request. Each file is verified independently via Promise.allSettled — a failure on one item does NOT abort the batch. Max 100 items, max 500 MB total payload.","tags":["Batch"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"array","items":{"type":"string","format":"binary"},"description":"One or more files to verify. Field name can repeat or use array notation."}}}}}},"responses":{"200":{"description":"Per-item batch verification results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResult"}}}},"400":{"description":"No files uploaded or batch exceeds item limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"413":{"description":"Batch total size exceeds 500 MB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/batch/store":{"post":{"operationId":"batchStore","summary":"Batch store multiple manifests","description":"Upload multiple files and store each as a manifest. Per-item failures do NOT abort the batch. Max 100 items, max 500 MB total payload.","tags":["Batch"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"array","items":{"type":"string","format":"binary"},"description":"One or more files whose manifests should be stored."}}}}}},"responses":{"200":{"description":"Per-item batch store results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResult"}}}},"400":{"description":"No files uploaded or batch exceeds item limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"413":{"description":"Batch total size exceeds 500 MB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/batch/resolve":{"post":{"operationId":"batchResolve","summary":"Batch resolve multiple fingerprints","description":"Resolve multiple pre-computed fingerprints against the soft binding index. JSON body. Individual invalid fingerprints appear as rejected items in the response without aborting the batch.","tags":["Batch"],"security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["fingerprints"],"properties":{"fingerprints":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","required":["fingerprint"],"properties":{"fingerprint":{"type":"string","description":"Hex string between 8 and 64 characters","pattern":"^[0-9a-fA-F]{8,64}$"},"algorithm":{"type":"string","enum":["phash-dct-64","dhash-64","chromaprint","iscc","neural-sscd-v1"],"default":"phash-dct-64"},"maxResults":{"type":"integer","minimum":1,"maximum":100,"default":10}}}}}}}}},"responses":{"200":{"description":"Per-item batch resolve results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResult"}}}},"400":{"description":"Empty fingerprints array or batch exceeds item limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/v1/training-data/register":{"post":{"tags":["Training Data"],"summary":"Register a new training dataset","security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","organizationId"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"organizationId":{"type":"string"}}}}}},"responses":{"201":{"description":"Dataset created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrainingDataset"}}}},"400":{"description":"Invalid input"},"401":{"description":"Unauthorized"}}}},"/v1/training-data/{datasetId}/ingest":{"post":{"tags":["Training Data"],"summary":"Ingest a batch of fingerprints into a dataset","security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"parameters":[{"name":"datasetId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["fingerprints"],"properties":{"fingerprints":{"type":"array","items":{"type":"object","required":["algorithm","value"],"properties":{"algorithm":{"type":"string"},"value":{"type":"string"},"manifestId":{"type":"string"},"metadata":{"type":"object"}}}}}}}}},"responses":{"200":{"description":"Ingest result","content":{"application/json":{"schema":{"type":"object","properties":{"ingested":{"type":"integer"},"duplicatesSkipped":{"type":"integer"}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Unauthorized"},"404":{"description":"Dataset not found"},"413":{"description":"Batch exceeds max size"}}}},"/v1/training-data/membership":{"post":{"tags":["Training Data"],"summary":"Check whether a fingerprint matches any registered training dataset","description":"Accepts JSON with a precomputed fingerprint or multipart with a file upload.","security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["fingerprint","algorithm"],"properties":{"fingerprint":{"type":"string"},"algorithm":{"type":"string"},"maxResults":{"type":"integer"},"threshold":{"type":"integer"},"datasetIds":{"type":"array","items":{"type":"string"}}}}},"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"},"algorithm":{"type":"string"},"maxResults":{"type":"string"},"threshold":{"type":"string"}}}}}},"responses":{"200":{"description":"Membership result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrainingDataMembershipResult"}}}},"400":{"description":"Invalid query"},"401":{"description":"Unauthorized"},"413":{"description":"File too large"}}}},"/v1/training-data/report":{"get":{"tags":["Training Data"],"summary":"Generate a training data coverage report","security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"parameters":[{"name":"organizationId","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Report","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrainingDataReport"}}}},"400":{"description":"Missing organizationId"},"401":{"description":"Unauthorized"}}}},"/v1/training-data/datasets":{"get":{"tags":["Training Data"],"summary":"List training datasets for an organization","security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"parameters":[{"name":"organizationId","in":"query","required":true,"schema":{"type":"string"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","default":1}},{"name":"perPage","in":"query","required":false,"schema":{"type":"integer","default":20}}],"responses":{"200":{"description":"Paginated dataset list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TrainingDataset"}},"total":{"type":"integer"},"page":{"type":"integer"},"perPage":{"type":"integer"},"hasMore":{"type":"boolean"}}}}}},"400":{"description":"Missing organizationId"},"401":{"description":"Unauthorized"}}}},"/v1/training-data/datasets/{id}":{"get":{"tags":["Training Data"],"summary":"Get a single training dataset by id","security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Dataset","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrainingDataset"}}}},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}}},"/v1/verify/forensic":{"post":{"tags":["Verify"],"summary":"Forensic verification — enhanced analysis for insurance and finance fraud assessment","description":"Runs standard verification plus EXIF extraction, manipulation signal detection, confidence scoring, and actionable recommendations.","security":[{"BearerAuth":[]},{"apiKey":[]},{"SessionAuth":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}}}},"responses":{"200":{"description":"Forensic verification result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForensicVerifyResult"}}}},"400":{"description":"Invalid request / missing file / empty file"},"401":{"description":"Unauthorized"},"413":{"description":"File exceeds maximum size of 50 MB"},"429":{"description":"Rate limit exceeded"},"500":{"description":"Internal forensic verification error"}}}}},"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"Authenticate with an Hashproof API key (hpsk_...) as a Bearer token."},"apiKey":{"type":"apiKey","in":"header","name":"x-api-key","description":"Hashproof API key. Equivalent form: Authorization: Bearer hpsk_<key>."},"SessionAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Supabase dashboard session JWT. Not an API key."}},"schemas":{"BatchResult":{"type":"object","description":"Envelope for batch endpoint responses. Each item carries per-item status, plus aggregate success/failure counts.","properties":{"results":{"type":"array","description":"Per-item results, indexed to the input order","items":{"type":"object","required":["index","status"],"properties":{"index":{"type":"integer","minimum":0},"status":{"type":"string","enum":["fulfilled","rejected"]},"value":{"description":"Present when status == fulfilled"},"error":{"type":"object","description":"Present when status == rejected","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}},"totalItems":{"type":"integer","minimum":0},"successCount":{"type":"integer","minimum":0},"failureCount":{"type":"integer","minimum":0},"processingTimeMs":{"type":"integer","minimum":0}},"required":["results","totalItems","successCount","failureCount","processingTimeMs"]},"ManifestData":{"type":"object","description":"A C2PA manifest with parsed metadata, signature info, assertions, and validation status.","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"format":{"type":"string","description":"MIME type of the original asset"},"instanceId":{"type":"string","description":"XMP instance ID"},"claimGenerator":{"type":"string"},"signatureInfo":{"$ref":"#/components/schemas/SignatureInfo"},"assertions":{"type":"array","items":{"$ref":"#/components/schemas/Assertion"}},"ingredients":{"type":"array","items":{"$ref":"#/components/schemas/Ingredient"}},"hardBinding":{"$ref":"#/components/schemas/HardBinding"},"validationStatus":{"type":"string","enum":["well_formed","valid","trusted","unknown"]},"rawSize":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"cid":{"type":["string","null"],"description":"Content-address CIDv1 computed for the stored record blob; null when computation failed. Absent on embedded-parse manifests that were never stored."}},"required":["id","format","instanceId","claimGenerator","signatureInfo","assertions","ingredients","hardBinding","validationStatus","rawSize","createdAt"]},"SignatureInfo":{"type":"object","properties":{"issuer":{"type":"string"},"subject":{"type":"string"},"algorithm":{"type":"string"},"certSerialNumber":{"type":"string"},"signedAt":{"type":"string","format":"date-time"},"certChain":{"type":"array","items":{"type":"string"}}},"required":["issuer","subject","algorithm","certSerialNumber","signedAt","certChain"]},"Assertion":{"type":"object","properties":{"label":{"type":"string","description":"C2PA assertion label (e.g., c2pa.hash.data, c2pa.actions)"},"data":{"type":"object","additionalProperties":true},"kind":{"type":"string","enum":["cbor","json","binary"]}},"required":["label","data","kind"]},"Ingredient":{"type":"object","properties":{"title":{"type":"string"},"format":{"type":"string"},"instanceId":{"type":"string"},"relationship":{"type":"string","enum":["parentOf","componentOf","inputTo"]},"hash":{"type":"string"}},"required":["title","format","instanceId","relationship","hash"]},"HardBinding":{"type":"object","properties":{"algorithm":{"type":"string","enum":["SHA-256","SHA-384","SHA-512"]},"hash":{"type":"string"}},"required":["algorithm","hash"]},"ValidationResult":{"type":"object","properties":{"status":{"type":"string","enum":["well_formed","valid","trusted","unknown"]},"errors":{"type":"array","items":{"$ref":"#/components/schemas/ValidationError"}},"warnings":{"type":"array","items":{"type":"string"}},"trustChainValid":{"type":"boolean"},"signerTrusted":{"type":"boolean"}},"required":["status","errors","warnings","trustChainValid","signerTrusted"]},"ValidationError":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]},"ResolveResult":{"type":"object","description":"Result of a soft binding resolution query.","properties":{"matches":{"type":"array","items":{"$ref":"#/components/schemas/ResolveMatch"}},"query":{"type":"object","properties":{"fingerprint":{"type":"string"},"algorithm":{"type":"string","enum":["phash-dct-64","dhash-64","chromaprint","iscc","neural-sscd-v1"]}},"required":["fingerprint","algorithm"]},"searchTimeMs":{"type":"number"}},"required":["matches","query","searchTimeMs"]},"ResolveMatch":{"type":"object","properties":{"manifestId":{"type":"string","format":"uuid"},"distance":{"type":"integer","description":"Hamming distance between fingerprints"},"similarity":{"type":"number","minimum":0,"maximum":1,"description":"Normalized similarity score (0.0 to 1.0)"},"manifest":{"$ref":"#/components/schemas/ManifestData"}},"required":["manifestId","distance","similarity","manifest"]},"VerifyResult":{"type":"object","description":"Result of a one-call provenance verification.","properties":{"hasProvenance":{"type":"boolean","description":"Whether any provenance was found"},"source":{"type":"string","enum":["embedded","resolved","none"],"description":"How provenance was discovered"},"manifest":{"oneOf":[{"$ref":"#/components/schemas/ManifestData"},{"type":"null"}],"description":"The full manifest data if found"},"validation":{"oneOf":[{"$ref":"#/components/schemas/ValidationResult"},{"type":"null"}],"description":"Cryptographic validation result"},"trustStatus":{"type":"string","enum":["trusted","untrusted","unknown"]},"resolvedVia":{"type":"string","description":"How the manifest was located. Present only on resolved paths."}},"required":["hasProvenance","source","manifest","validation","trustStatus"]},"StoreResult":{"type":"object","properties":{"manifestId":{"type":"string","format":"uuid"},"manifest":{"$ref":"#/components/schemas/ManifestData"},"softBindings":{"type":"array","description":"Soft bindings computed for the stored asset.","items":{"type":"object","required":["algorithm","value"],"properties":{"algorithm":{"type":"string"},"value":{"type":"string"}}}}},"required":["manifestId","manifest","softBindings"]},"SignResult":{"type":"object","properties":{"manifestId":{"type":"string","format":"uuid"},"manifest":{"$ref":"#/components/schemas/ManifestData"},"signedAssetUrl":{"type":["string","null"],"format":"uri","description":"Currently always null. Reserved for a signed-file download URL."},"message":{"type":"string"}},"required":["manifestId","manifest","signedAssetUrl","message"]},"FingerprintResult":{"type":"object","properties":{"fingerprint":{"type":"string","description":"Hex-encoded perceptual fingerprint"},"algorithm":{"type":"string","enum":["phash-dct-64","dhash-64","chromaprint","iscc","neural-sscd-v1"]},"bitLength":{"type":"integer","description":"Number of bits in the fingerprint"}},"required":["fingerprint","algorithm","bitLength"]},"SoftBinding":{"type":"object","properties":{"id":{"type":"string"},"manifestId":{"type":"string","format":"uuid"},"bindingType":{"type":"string","enum":["phash","dhash","watermark","fingerprint"]},"bindingValue":{"type":"string","description":"Hex-encoded binding value"},"algorithm":{"type":"string","enum":["phash-dct-64","dhash-64","chromaprint","iscc","neural-sscd-v1"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","manifestId","bindingType","bindingValue","algorithm","createdAt"]},"MerkleProof":{"type":"object","description":"Merkle inclusion proof for a manifest, verified against the stored batch root. The anchor transactionHash and blockNumber are null; batch roots are not submitted on-chain.","properties":{"manifestId":{"type":"string","format":"uuid"},"root":{"type":"string","description":"Merkle root hash"},"proof":{"type":"array","items":{"type":"string"},"description":"Array of sibling hashes for proof verification"},"index":{"type":"integer","description":"Leaf index in the Merkle tree"},"anchor":{"$ref":"#/components/schemas/MerkleAnchor"},"verified":{"type":"boolean","description":"Whether the proof was verified against the root"}},"required":["manifestId","root","proof","index","anchor","verified"]},"MerkleAnchor":{"type":"object","description":"Merkle batch record for a set of manifests. transactionHash and blockNumber are null. Batch roots are not submitted on-chain.","properties":{"id":{"type":"string"},"merkleRoot":{"type":"string"},"transactionHash":{"type":["string","null"],"description":"Always null. Batch roots are not submitted on-chain."},"chainId":{"type":"integer","description":"Configured EVM chain id (8453 = Base). Informational: batch roots are not submitted on-chain."},"blockNumber":{"type":["integer","null"],"description":"Always null. Batch roots are not submitted on-chain."},"batchSize":{"type":"integer"},"anchoredAt":{"type":"string","format":"date-time"}},"required":["id","merkleRoot","transactionHash","chainId","blockNumber","batchSize","anchoredAt"]},"ComplianceReport":{"type":"object","description":"EU AI Act Article 50 compliance report. Generated synchronously from all manifests owned by the calling user and persisted for later retrieval.","properties":{"id":{"type":"string"},"reportType":{"type":"string","enum":["eu-ai-act"]},"generatedAt":{"type":"string","format":"date-time"},"summary":{"type":"object","properties":{"totalAssets":{"type":"integer"},"assetsWithProvenance":{"type":"integer"},"compliancePercentage":{"type":"integer"},"aiGeneratedCount":{"type":"integer"},"aiDisclosedCount":{"type":"integer"},"certificateIssues":{"type":"integer"}},"required":["totalAssets","assetsWithProvenance","compliancePercentage","aiGeneratedCount","aiDisclosedCount","certificateIssues"]},"details":{"type":"array","items":{"$ref":"#/components/schemas/ComplianceDetail"}}},"required":["id","reportType","generatedAt","summary","details"]},"ComplianceReportListItem":{"type":"object","description":"Compliance report list row: identity fields plus the summary counters, without the details array.","properties":{"id":{"type":"string"},"reportType":{"type":"string","enum":["eu-ai-act"]},"generatedAt":{"type":"string","format":"date-time"},"totalAssets":{"type":"integer"},"assetsWithProvenance":{"type":"integer"},"compliancePercentage":{"type":"integer"},"aiGeneratedCount":{"type":"integer"},"aiDisclosedCount":{"type":"integer"},"certificateIssues":{"type":"integer"}},"required":["id","reportType","generatedAt","totalAssets","assetsWithProvenance","compliancePercentage","aiGeneratedCount","aiDisclosedCount","certificateIssues"]},"ComplianceDetail":{"type":"object","properties":{"manifestId":{"type":"string"},"assetTitle":{"type":"string"},"issues":{"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["compliant","non_compliant","needs_review"]}},"required":["manifestId","assetTitle","issues","status"]},"ApiKeyInfo":{"type":"object","description":"API key metadata (the raw key is never returned after creation).","properties":{"id":{"type":"string"},"name":{"type":"string"},"keyPrefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"rateLimit":{"type":"integer"},"isActive":{"type":"boolean"},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"expiresAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}},"PaginatedManifests":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ManifestData"}},"total":{"type":"integer"},"page":{"type":"integer"},"perPage":{"type":"integer"},"hasMore":{"type":"boolean"}},"required":["data","total","page","perPage","hasMore"]},"ApiError":{"type":"object","description":"Standard error response.","properties":{"error":{"type":"string","description":"Human-readable error message"},"code":{"type":"string","description":"Machine-readable error code"},"statusCode":{"type":"integer"},"details":{"type":["object","null"],"additionalProperties":true}},"required":["error","code","statusCode"]},"TrainingDataset":{"type":"object","required":["id","name","description","organizationId","fingerprints","algorithms","createdAt","updatedAt"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"organizationId":{"type":"string"},"fingerprints":{"type":"integer","description":"count of registered fingerprints"},"algorithms":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"TrainingDataMatch":{"type":"object","properties":{"datasetId":{"type":"string"},"datasetName":{"type":"string"},"manifestId":{"type":["string","null"],"description":"Manifest ID this fingerprint was registered against. Null when the fingerprint was ingested without a manifest reference."},"distance":{"type":"integer"},"similarity":{"type":"number"},"registeredAt":{"type":"string","format":"date-time"}}},"TrainingDataMembershipResult":{"type":"object","properties":{"queryFingerprint":{"type":"string"},"queryAlgorithm":{"type":"string"},"matches":{"type":"array","items":{"$ref":"#/components/schemas/TrainingDataMatch"}},"searchTimeMs":{"type":"integer"},"datasetsSearched":{"type":"integer"}}},"TrainingDataReport":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string"},"generatedAt":{"type":"string","format":"date-time"},"summary":{"type":"object","properties":{"totalDatasets":{"type":"integer"},"totalFingerprints":{"type":"integer"},"algorithmsUsed":{"type":"array","items":{"type":"string"}},"coverageByDataset":{"type":"array","items":{"type":"object","properties":{"datasetId":{"type":"string"},"datasetName":{"type":"string"},"fingerprintCount":{"type":"integer"}}}}}},"recentActivity":{"type":"array","items":{"type":"object","properties":{"action":{"type":"string"},"datasetId":{"type":"string"},"count":{"type":"integer"},"timestamp":{"type":"string","format":"date-time"}}}}}},"ExifMetadata":{"type":"object","properties":{"make":{"type":["string","null"]},"model":{"type":["string","null"]},"software":{"type":["string","null"]},"dateTimeOriginal":{"type":["string","null"]},"gpsLatitude":{"type":["number","null"]},"gpsLongitude":{"type":["number","null"]},"gpsAltitude":{"type":["number","null"]},"imageWidth":{"type":["integer","null"]},"imageHeight":{"type":["integer","null"]},"orientation":{"type":["integer","null"]},"colorSpace":{"type":["string","null"]},"focalLength":{"type":["number","null"]},"exposureTime":{"type":["string","null"]},"fNumber":{"type":["number","null"]},"iso":{"type":["integer","null"]}}},"ManipulationSignal":{"type":"object","properties":{"type":{"type":"string","enum":["metadata_stripped","metadata_inconsistency","editing_software","timestamp_anomaly","gps_anomaly","resolution_anomaly","c2pa_signature_issue"]},"severity":{"type":"string","enum":["info","warning","critical"]},"description":{"type":"string"},"details":{"type":"object","additionalProperties":true}}},"ForensicVerifyResult":{"type":"object","description":"Extends VerifyResult with a forensicAnalysis block for insurance/finance fraud assessment.","properties":{"hasProvenance":{"type":"boolean"},"source":{"type":"string","enum":["embedded","resolved","none"]},"manifest":{"type":["object","null"]},"validation":{"type":["object","null"]},"trustStatus":{"type":"string","enum":["trusted","untrusted","unknown"]},"resolvedVia":{"type":["string","null"]},"forensicAnalysis":{"type":"object","properties":{"exifMetadata":{"$ref":"#/components/schemas/ExifMetadata"},"manipulationSignals":{"type":"array","items":{"$ref":"#/components/schemas/ManipulationSignal"}},"confidenceScore":{"type":"number","minimum":0,"maximum":1},"riskLevel":{"type":"string","enum":["low","medium","high","critical"]},"recommendations":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"BadRequest":{"description":"Bad request — missing or invalid parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"},"example":{"error":"No file uploaded","code":"MISSING_FILE","statusCode":400}}}},"Unauthorized":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"},"example":{"error":"Invalid or missing API key","code":"UNAUTHORIZED","statusCode":401}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"},"example":{"error":"Resource not found","code":"NOT_FOUND","statusCode":404}}}}}}}