A signed media QC receipt is a tamper-evident record of a quality-check verdict — PASS, WATCH, or BLOCK — bound to the exact file it checked via that file's sha256 hash and the named profile it was checked against. Because the whole record is signed, a CI pipeline or an AI agent can gate its publish step on media quality the same way a signed status check gates a code merge: no asset ships without a green receipt.
The problem: media has no "required check"
Code has this solved. A pull request can't merge until the tests pass, the linter is green, and the required status checks report success. The gate is enforced — not "here's some advice a human reads," but "this blocks the merge."
Media pipelines usually have no equivalent. An AI-video pipeline generates a clip, an editor exports a cut, an ad tool spins up a hundred variants — and the only quality gate is someone eyeballing it, if that. So a caption clipped by the platform UI, audio that cuts out at 0:08, a frozen frame at a stitch seam, or a morphing hand ships to a paying audience. The failure is caught after publish, in a refund or a re-render, instead of before.
A QC report is a step up, but it's still just data a human interprets. What turns quality into a required check is a verdict a pipeline can act on automatically and prove afterward. That's the receipt.
What's in a receipt
Run UploadCheck on a finished export against a named profile, then fetch the receipt for the completed job. It's a small, signed JSON object:
{
"schema": "uploadcheck-receipt/1",
"verdict": "PASS",
"sha256": "<the checked file's hash>",
"profile": "youtube_shorts",
"gates": ["av_sync", "canvas_fill", "loudness", "shorts_format"],
"blocked": [],
"jobId": "job_...",
"issuedAt": "2026-07-20T00:00:00.000Z",
"issuer": "uploadcheck.app",
"signature": "sha256=..."
}
Four things make it a gate rather than a report:
- Bound to the exact file. The
sha256is part of the signed payload. Swap the file and the signature no longer matches — a receipt can never be reused for different content. - Bound to a named target.
profilerecords the delivery spec the file was checked against, so a green receipt reads "PASS foryoutube_shorts," not an abstract list of checks. - Tamper-evident. The whole record is HMAC-SHA256 signed. Any change to the verdict, hash, profile, or gate list is caught on verification.
- Verifiable by anyone. A third party — a CI system, an auditor, a client — confirms authenticity with one no-auth call, without access to your workspace.
How to gate a pipeline on it
The UploadCheck CLI and GitHub Action already exit non-zero on a BLOCK verdict, which fails the build. Add the receipt so the run also produces a verifiable artifact:
# CLI — fails the job on BLOCK, and writes the signed receipt
uploadcheck check final.mp4 \
--profile youtube_shorts \
--fail-on block \
--receipt uploadcheck-receipt.json
In GitHub Actions, the same thing as a step:
- uses: uploadcheck/action@v1
with:
file: dist/final.mp4
profile: youtube_shorts
fail-on: block
receipt-path: uploadcheck-receipt.json
api-key: ${{ secrets.UPLOADCHECK_API_KEY }}
The job fails if the verdict is BLOCK; on PASS it continues and leaves uploadcheck-receipt.json for a later step to upload as an artifact or hand to a downstream deploy. An AI agent does the same through the qc_get_receipt MCP tool and gates its own publish action on receipt.verdict === "PASS".
Verifying a receipt later
The point of a signature is that trust doesn't depend on the transport. Anyone handed a receipt can confirm it's authentic and unaltered:
curl -X POST https://api.uploadcheck.app/v1/public/receipts/verify \
-H "content-type: application/json" \
--data @uploadcheck-receipt.json
The endpoint — no authentication, no account — recomputes the signature and reports whether the receipt is genuine. Then confirm the receipt's sha256 matches the file you're about to publish, and the chain is complete: this verdict, for this exact file, against this named spec, provably issued by UploadCheck.
Named delivery profiles
Check against a target your pipeline recognizes so the receipt names a spec, not a checklist. Available profiles include youtube_shorts, youtube_longform, meta_reels_ad, tiktok_post, broadcast_r128, podcast_delivery, and ai_render_gate. See the receipts documentation for the full list and the fields each one checks.
Why this is the unlock
A report tells you what's wrong. A signed, file-bound, named-target receipt lets a pipeline run unattended — generate, check, gate, publish — and prove afterward that every asset passed a specific spec. That's the leap linters and CI status checks made for code: from advice to this blocks the deploy. For anyone shipping media at scale — AI-video pipelines, ad-variant factories, localization houses — it's the difference between hoping the output is clean and requiring it.