{"openapi":"3.1.0","info":{"title":"Proofcan API","version":"1.1.0","description":"Mark images with a C2PA manifest (AI-generated declaration) and an invisible TrustMark watermark, and verify marked images. Video and audio marking is in BETA (background jobs, /v1/media/jobs; accounts enabled by Proofcan). Documentation: https://www.proofcan.com/docs/api"},"servers":[{"url":"https://engine.proofcan.com"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer"}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}},"Record":{"type":"object","properties":{"lookup_id":{"type":"string","example":"ABCDEFGH"},"registered_at":{"type":"string","format":"date-time"},"sha256":{"type":"string","description":"SHA-256 of the marked file."},"claim_generator":{"type":"string","example":"Proofcan/0.2.0"},"digital_source_type":{"type":"string"},"watermark":{"type":"string","example":"TrustMark Q/BCH_4"},"record_url":{"type":"string","format":"uri"}}},"MediaJob":{"type":"object","properties":{"job_id":{"type":"string","example":"mj_0123456789abcdef0123456789abcdef"},"kind":{"type":"string","enum":["video","audio"]},"status":{"type":"string","enum":["queued","running","done","failed"]},"progress":{"type":"number","minimum":0,"maximum":1},"queue_position":{"type":"integer","description":"Only while queued (1 = next)."},"duration_seconds":{"type":"number"},"created_at":{"type":"string","format":"date-time"},"started_at":{"type":["string","null"],"format":"date-time"},"finished_at":{"type":["string","null"],"format":"date-time"},"lookup_id":{"type":["string","null"]},"record_url":{"type":["string","null"],"format":"uri"},"sha256":{"type":["string","null"]},"result_url":{"type":["string","null"],"format":"uri","description":"Download link while the marked file is kept (24 hours)."},"result_expires_at":{"type":["string","null"],"format":"date-time"},"result_available":{"type":"boolean"},"error":{"type":["object","null"],"properties":{"code":{"type":"string","description":"timeout, interrupted, decode_failed, encode_failed, too_short, watermark_failed, signing_failed, internal_error"},"message":{"type":"string"}}},"webhook":{"type":["object","null"],"properties":{"state":{"type":"string","enum":["waiting","pending","delivered","failed"]},"attempts":{"type":"integer"}}}}}}},"paths":{"/v1/mark":{"post":{"summary":"Mark an image","description":"Signs the image with a C2PA manifest declaring it AI-generated and embeds an invisible watermark carrying the lookup ID. Uses one mark from the account's monthly quota, only on success.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"JPEG, PNG or WebP; at most 50 MB and 50 megapixels."},"ai_system_name":{"type":"string","maxLength":100},"ai_system_version":{"type":"string","maxLength":100},"provider_name":{"type":"string","maxLength":100}}}}}},"responses":{"200":{"description":"The marked file, same format as the upload.","headers":{"Proofcan-Lookup-Id":{"schema":{"type":"string"}},"Proofcan-Record-Url":{"schema":{"type":"string","format":"uri"}},"Proofcan-Quota-Remaining":{"schema":{"type":"integer"}},"Proofcan-Sha256":{"schema":{"type":"string"}}},"content":{"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/png":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Malformed request, empty or corrupt image, or invalid field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"File larger than 50 MB or image larger than 50 megapixels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Not a JPEG, PNG or WebP image (checked by file signature).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Monthly quota used up.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}}},"503":{"description":"Temporarily unavailable or busy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}}}}}},"/v1/verify":{"post":{"summary":"Verify an image, a video or an audio file","description":"Checks the C2PA manifest, the watermark and the registry. Does not use quota. Images: JPEG, PNG, WebP. Video (BETA): MP4, MOV up to 100 MB; audio (BETA): WAV, MP3, M4A, FLAC up to 50 MB — the response then also has `media` {type, duration_seconds}.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"200":{"description":"Verification result.","content":{"application/json":{"schema":{"type":"object","properties":{"c2pa":{"type":"object","properties":{"present":{"type":"boolean"},"valid":{"type":"boolean"},"state":{"type":["string","null"]},"ai_declared":{"type":"boolean"}}},"watermark":{"type":"object","properties":{"found":{"type":"boolean"},"lookup_id":{"type":["string","null"]}}},"lookup_id":{"type":["string","null"]},"record":{"oneOf":[{"$ref":"#/components/schemas/Record"},{"type":"null"}]},"sha256_match":{"type":"boolean"}}}}}},"400":{"description":"Malformed request, empty or corrupt image, or invalid field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"File larger than 50 MB or image larger than 50 megapixels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Not a JPEG, PNG or WebP image (checked by file signature).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}}},"503":{"description":"Temporarily unavailable or busy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}}}}}},"/v1/records/{lookup_id}":{"get":{"summary":"Look up a registry record","parameters":[{"name":"lookup_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Z2-7]{8}$"}}],"responses":{"200":{"description":"Public record (no personal data).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Record"}}}},"400":{"description":"Malformed lookup ID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No record with this lookup ID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}}},"503":{"description":"Temporarily unavailable or busy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}}}}}},"/v1/usage":{"get":{"summary":"Quota usage","responses":{"200":{"description":"Plan and usage for the current period.","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string"},"used":{"type":"integer"},"limit":{"type":"integer"},"remaining":{"type":"integer"},"period_start":{"type":"string","format":"date-time"},"renews_at":{"type":["string","null"],"format":"date-time"}}}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}}},"503":{"description":"Temporarily unavailable or busy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}}}}}},"/v1/media/jobs":{"post":{"summary":"Mark a video or an audio file (BETA, background job)","description":"Queues a marking job and returns 202 at once. The job embeds an invisible watermark (video: Video Seal, audio: AudioSeal) carrying the record lookup ID with error correction, signs a C2PA manifest declaring the file AI-generated (the producer's own manifest is kept as the parent ingredient) and writes the registry record. Only for accounts in the media beta (others: 403 media_beta_required). Uses the beta allowance (video seconds / audio minutes), only on success.\n\nVideo: MP4 or MOV, at most 100 MB, 120 s and 1920×1080, 60 fps. Audio: WAV, MP3, M4A or FLAC, at most 50 MB, 10 s to 10 min. Every marked audio file passes a self-check before delivery: the lookup ID must decode again after MP3 at 128 kbit/s. Otherwise the job fails with `too_short` and the allowance is not used (music usually needs at least 30 s; speech works from 10 s).\n\nWebhook: when the job ends, a POST is sent to `webhook_url` (https, port 443, public host only, redirects are not followed) with header `Proofcan-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.\" + body)>`. The secret is HKDF-SHA256 of your API key (salt: 32 zero bytes, info: \"proofcan/webhook/v1\", 32 bytes). Test vector: key `pc_live_` + 43 × `A` → secret `18ba7b0e88b766b742e40eb71507b8fe4d622b4949cfcddd31668d4e870b83c2`. The first delivery is followed by up to 3 retries (30 s, 2 min, 10 min); any 2xx counts as delivered.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"},"webhook_url":{"type":"string","format":"uri","maxLength":2048},"ai_system_name":{"type":"string","maxLength":100},"ai_system_version":{"type":"string","maxLength":100},"provider_name":{"type":"string","maxLength":100}}}}}},"responses":{"202":{"description":"Queued.","headers":{"Location":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaJob"}}}},"400":{"description":"Unreadable file, duration or resolution out of range, invalid webhook_url or field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Beta allowance for this period used up.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The account is not in the media beta (media_beta_required).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"File too large.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Not MP4/MOV video or WAV/MP3/M4A/FLAC audio (checked by file signature).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}}},"503":{"description":"Temporarily unavailable or busy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}}}}}},"/v1/media/jobs/{job_id}":{"get":{"summary":"Media job status (BETA)","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Status, progress and result link.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaJob"}}}},"403":{"description":"The account is not in the media beta.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No media job with this ID for this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}}},"503":{"description":"Temporarily unavailable or busy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}}}}}},"/v1/media/jobs/{job_id}/result":{"get":{"summary":"Download the marked file (BETA)","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The marked file, same format as the upload.","headers":{"Proofcan-Lookup-Id":{"schema":{"type":"string"}},"Proofcan-Sha256":{"schema":{"type":"string"}},"Proofcan-Record-Url":{"schema":{"type":"string","format":"uri"}}},"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"No media job with this ID for this account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The job has not finished.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"The marked file was deleted after 24 hours; the record stays verifiable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}}},"503":{"description":"Temporarily unavailable or busy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}}}}}},"/v1/openapi.json":{"get":{"summary":"This document","security":[],"responses":{"200":{"description":"OpenAPI 3.1 document."}}}}}}