Agent Receipt Verifier
Pricing
from $1.50 / 1,000 claim verifieds
Agent Receipt Verifier
Check what an AI agent says it did. Give file claims (expected sha256) or URL claims (expected HTTP status). Each claim gets PROVEN, REJECTED or UNVERIFIABLE with evidence hashes, plus a sha256 run receipt.
Pricing
from $1.50 / 1,000 claim verifieds
Rating
0.0
(0)
Developer
Honest Agent Kit
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a month ago
Last modified
Categories
Security
Limited permissionsShare
Check what an AI agent says it did, against real bytes and real HTTP answers.
Your agent reports "I wrote report.md, its sha256 is 3f7c…" or "the page is live, it returns 200". This Actor checks each claim and gives it one of three verdicts, with the evidence and its hash. At the end it writes a run receipt: the sha256 of all results in canonical JSON, so anyone can recompute it later.
Verdicts
| Verdict | Meaning |
|---|---|
| PROVEN | The actual bytes were hashed, or the actual HTTP status was read, and they match the claim. |
| REJECTED | The actual bytes or status were checked, and they do not match. A real mismatch, never a guess. |
| UNVERIFIABLE | The evidence could not be obtained, or the answer does not settle the claim. Timeouts, DNS failures, oversize files, blocked addresses, unfollowed redirects, missing or bad input, and local paths all land here, never in REJECTED. |
A claim is never PROVEN unless the bytes or the status were actually checked in this run.
What you can check
File claims (expected sha256)
A cloud run cannot see your laptop's disk, so give the bytes in one of three ways:
| Field | How the bytes are obtained |
|---|---|
fileUrl | One GET to an http(s) URL you supply. The body is streamed into sha256, up to Max file size (default 10 MB). Accept-Encoding: identity is requested; a compressed answer is UNVERIFIABLE because its hash would not be the file's. |
contentBase64 | The file bytes inline, as standard base64. |
kvKey | A record in an Apify key-value store you choose in File store. The record's raw bytes are hashed. |
path | Accepted so agent output can be passed straight in, but never read. Always UNVERIFIABLE with the reason "local paths cannot be read from the Apify cloud". |
URL claims (expected HTTP status)
One request to the URL you supply (GET by default, or HEAD). Only the status line and headers are read; the connection is closed before the body. Redirects are not followed:
- If the claim expects a redirect (for example
301) and gets it, that is PROVEN. - If the claim expects
200and the server answers302, the claim is not settled: UNVERIFIABLE. Give the final URL instead. 404claimed and404answered is PROVEN, which is how you check "I deleted that page".401,403,405,407,408,425,429and5xxanswers that differ from the claim are UNVERIFIABLE: the server answered but would not confirm (it may need a login, block automated requests, rate limit, or be down).- Any other different status is REJECTED.
Input example
{"claims": [{ "id": "homepage-live", "type": "url", "url": "https://example.com/", "expectedStatus": 200 },{ "id": "old-page-removed", "type": "url", "url": "https://example.com/old-page", "expectedStatus": 404 },{ "id": "release-notes", "type": "file", "fileUrl": "https://example.com/files/notes.txt", "expectedSha256": "5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03" },{ "id": "inline-hello", "type": "file", "contentBase64": "aGVsbG8K", "expectedSha256": "5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03" },{ "id": "stored-output", "type": "file", "kvKey": "agent-output", "expectedSha256": "5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03" },{ "id": "laptop-file", "type": "file", "path": "C:/agent/out/report.md", "expectedSha256": "5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03" }],"fileStore": "<your key-value store ID>","maxClaims": 100,"timeoutSecs": 15,"maxFileBytes": 10485760}
| Field | Default | Notes |
|---|---|---|
claims | required | 1 to 500 claims. Each needs a unique id and a type (file or url). |
fileStore | this run's default store | Key-value store that holds kvKey records: pick it in the input form, or pass its ID (or username~store-name) through the API. Read-only access is requested. |
maxClaims | 100 | Claims past this are returned UNVERIFIABLE ("not checked") and are not charged. Max 500. |
timeoutSecs | 15 | Per request, including the download. 1 to 60. |
maxFileBytes | 10 MB | Larger files are UNVERIFIABLE (not hashed). Max 50 MB. |
maxConcurrency | 4 | Claims checked at the same time. 1 to 10. |
Output
One dataset item per claim, in input order (index). Example (illustrative values):
{"index": 0,"claimId": "homepage-live","claimType": "url","target": "https://example.com/","verdict": "PROVEN","code": "MATCH","reason": "server answered 200, as claimed","expected": 200,"actual": 200,"checkedAt": "2026-10-04T01:54:54.640Z","evidence": { "kind": "http-status", "method": "GET", "url": "https://example.com/", "httpStatus": 200, "remoteAddress": "93.184.215.14" },"evidenceSha256": "9c1d…","billable": true}
expected/actual: the sha256 (file) or the HTTP status (url).actualisnullwhen nothing was observed.evidence: what was observed (status, address, byte count, content sha256, error code).nullwhen nothing was fetched or read.evidenceSha256: sha256 of the canonical JSON ofevidence.code: machine-readable reason:MATCH,SHA256_MISMATCH,STATUS_MISMATCH,TIMEOUT,DNS_FAILURE,CONNECTION_FAILED,TLS_ERROR,OVERSIZE,ENCODED_BODY,REDIRECT_NOT_FOLLOWED,HTTP_NOT_OK,SERVER_WOULD_NOT_CONFIRM,BLOCKED_ADDRESS,LOCAL_PATH,MISSING_INPUT,BAD_INPUT,KV_RECORD_NOT_FOUND,KV_READ_FAILED,OVER_CLAIM_CAP,BUDGET_REACHED.billable:truewhen the claim was actually checked and counted as oneclaim-verifiedevent.
The default key-value store also gets:
| Key | Content |
|---|---|
SUMMARY | Counts per verdict, checked / not checked, charged events, limits used, start and finish times, and the receipt hash. |
RECEIPT | resultsSha256, the algorithm, the canonicalization rule, and the verdict and evidence hash of every claim. |
RESULTS_CANONICAL | The exact bytes that were hashed for the receipt. |
Recompute the receipt
The receipt is sha256 of the results, ordered by index, as JSON with object keys sorted and no whitespace (UTF-8). Download RESULTS_CANONICAL and hash it, or rebuild it from the dataset:
import hashlib, jsonitems = sorted(json.load(open("dataset.json")), key=lambda x: x["index"])canon = json.dumps(items, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode()print(hashlib.sha256(canon).hexdigest()) # equals RECEIPT.resultsSha256
How it fetches (and what it never does)
- It requests only the URLs you supply, one request per claim, with a polite
User-Agent(AgentReceiptVerifier/1.0 …), a timeout and a size cap. - It does not crawl, follow links, follow redirects, retry, parse pages, run scripts or use proxies.
- Address guard: before connecting it resolves the host and refuses private, loopback, link-local, carrier-grade NAT, multicast, reserved and cloud-metadata addresses (for example
10.0.0.0/8,127.0.0.0/8,169.254.169.254,fd00::/8), including IPv4-mapped IPv6 forms and names that resolve there. The checked address is the one connected to. Blocked claims are UNVERIFIABLE and not charged. - URLs with embedded user names or passwords, and schemes other than http and https, are refused.
Honest limits
- It checks only what it can fetch. A PROVEN file claim means the bytes at that URL, in that inline string or in that record hashed to the expected value at
checkedAt. It does not prove the file on the agent's own disk is the same, or who made it. - It does not check that the content is correct, only that the hash matches.
- A PROVEN URL claim means the server gave that status to this one request. Some sites answer
200for any path, and some answer differently to datacenter IPs, so a200does not prove the page says what the agent claimed. - Many sites block or challenge automated requests from cloud IPs. Those answers come back UNVERIFIABLE, not REJECTED.
- No time-window check: unlike a local disk checker, a cloud run cannot see when a file was last modified, so "created at 14:02" is not checked.
- Results describe one moment. Run it again for a fresh check.
Pricing
Pay per event:
- $0.0015 per claim verified (
claim-verified, $1.50 per 1,000 claims). Charged once for each claim that was actually checked: bytes downloaded, decoded or read, or one HTTP request made, whatever the verdict. A timeout or DNS failure counts, because the request was made. - Not charged: claims with missing or bad input, local paths, blocked addresses, claims past
maxClaims, and claims skipped because your spending limit was reached. - A small Actor start fee set by Apify (
apify-actor-start, $0.00005 per start at up to 1 GB memory).
Each dataset item says whether it was charged (billable), and SUMMARY.chargedEvents gives the total. If you set a maximum cost per run, the Actor stops checking when it is reached and returns the rest as UNVERIFIABLE ("not checked").
Use from an agent or pipeline
Call it with the Apify API, the Apify MCP server, or an integration, pass the claims your agent produced, then read the dataset or the RECEIPT record. Gate the next step on verdict === "PROVEN", and treat UNVERIFIABLE as "a person should look".
Changelog
See CHANGELOG.md.
Author and disclosure
Built by a retired military veteran, with AI coding assistance. Tested by the seller with unit tests and local runs against throwaway test servers, as listed in the changelog. MIT license.