Agent Receipt Verifier avatar

Agent Receipt Verifier

Pricing

from $1.50 / 1,000 claim verifieds

Go to Apify Store
Agent Receipt Verifier

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

Honest Agent Kit

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a month ago

Last modified

Categories

Security

Limited permissions

Share

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

VerdictMeaning
PROVENThe actual bytes were hashed, or the actual HTTP status was read, and they match the claim.
REJECTEDThe actual bytes or status were checked, and they do not match. A real mismatch, never a guess.
UNVERIFIABLEThe 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:

FieldHow the bytes are obtained
fileUrlOne 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.
contentBase64The file bytes inline, as standard base64.
kvKeyA record in an Apify key-value store you choose in File store. The record's raw bytes are hashed.
pathAccepted 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 200 and the server answers 302, the claim is not settled: UNVERIFIABLE. Give the final URL instead.
  • 404 claimed and 404 answered is PROVEN, which is how you check "I deleted that page".
  • 401, 403, 405, 407, 408, 425, 429 and 5xx answers 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
}
FieldDefaultNotes
claimsrequired1 to 500 claims. Each needs a unique id and a type (file or url).
fileStorethis run's default storeKey-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.
maxClaims100Claims past this are returned UNVERIFIABLE ("not checked") and are not charged. Max 500.
timeoutSecs15Per request, including the download. 1 to 60.
maxFileBytes10 MBLarger files are UNVERIFIABLE (not hashed). Max 50 MB.
maxConcurrency4Claims 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). actual is null when nothing was observed.
  • evidence: what was observed (status, address, byte count, content sha256, error code). null when nothing was fetched or read.
  • evidenceSha256: sha256 of the canonical JSON of evidence.
  • 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: true when the claim was actually checked and counted as one claim-verified event.

The default key-value store also gets:

KeyContent
SUMMARYCounts per verdict, checked / not checked, charged events, limits used, start and finish times, and the receipt hash.
RECEIPTresultsSha256, the algorithm, the canonicalization rule, and the verdict and evidence hash of every claim.
RESULTS_CANONICALThe 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, json
items = 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 200 for any path, and some answer differently to datacenter IPs, so a 200 does 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.