Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Verify whether a PDF document (bank statement, payslip, invoice, tax return, ID, contract) is authentic or has been forged, AI-generated or edited. Use whenever the user asks to "verify", "check", "validate" or "detect fraud in" a PDF, or asks whether a document is real, fake, tampered or trustworthy. Submits the file to the VerifyPDF forensic API and returns a structured trust_score (0-100), a fraud_risk label and a list of fraud warnings.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | 82% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 108% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 11% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 86% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 17% | 0% |
Verify a PDF with VerifyPDF's forensic API. One submit, poll for the verdict, read back a trust_score, a fraud_risk label and the fraud warnings.
VerifyPDF detects forged and manipulated PDFs: AI-generated bank statements and payslips, documents edited after creation, template forgeries and metadata anomalies. It works on any HTTP client, no SDK required.
Every call needs your VerifyPDF API key in the API-KEY request header.
key_live_ and consume your plan allowance.key_test_, are free, and return a deterministicverdict from the filename (see "Testing" below). Use a test key to confirm the integration works before spending live quota.
Get a key from the Developers section of https://secure.verifypdf.com. API access is on the Professional and Corporate plans.
This skill resolves the key in this order:
VERIFYPDF_API_KEY environment variable (recommended for production andCI — keep the secret out of files on disk).
.verifypdf-key file in this skill's own directory, containing nothingbut the key on a single line. Copy .verifypdf-key.example to .verifypdf-key and paste your key in. The file is git-ignored.
If neither is set, the helper scripts stop and tell the user how to add one. Never paste the key into a chat prompt or echo it into logs.
The two helper scripts in this directory do all of this for you. The flow they implement, against base URL https://api.verifypdf.com:
POST /v2/document as multipart/form-data with the fieldfile (the PDF) and an optional custom_id. Returns 202 Accepted with a document_id. The bytes go to the API directly; never base64-encode the PDF into a tool argument — models truncate long base64 and produce a meaningless verdict.
GET /v2/document?document_id=<id> every few seconds untilstatus is no longer processing (typically 10-30 seconds).
To avoid polling entirely, set a webhook URL on your API key in the Developers section; VerifyPDF then POSTs the same JSON to your endpoint when analysis finishes, signed with an X-Webhook-Signature (HMAC-SHA256 of the raw body).
Bash (needs curl and jq):
bash./verify.sh ./statement.pdf # submit + poll + print verdict ./verify.sh ./statement.pdf invoice-4821 # with a custom_id ./verify.sh get doc_abc123 # fetch an existing document ./verify.sh delete doc_abc123 # delete a document ./verify.sh report doc_abc123 ./report.pdf # download the PDF report
Python (standard library only, no pip install):
bashpython3 verify.py ./statement.pdf python3 verify.py get doc_abc123 python3 verify.py delete doc_abc123 python3 verify.py report doc_abc123 ./report.pdf
Both print the full verdict JSON to stdout and a one-line human summary to stderr, and exit non-zero on an API error or a high fraud_risk so a CI step or an agent can branch on the exit code.
A completed GET /v2/document returns JSON shaped like:
json{ "status": "analyzed", "document_id": "doc_abc123", "trust_score": 42, "fraud_risk": "high", "warnings": [ { "indicator_id": "font_anachronism", "description": "..." }, { "indicator_id": "incremental_update", "description": "..." } ], "original_filename": "statement.pdf", "text_changes": [ { "page": 2, "original_text": "1,200.00", "modified_text": "9,200.00", "bounding_box": { "x": 0, "y": 0, "width": 0, "height": 0 } } ], "change_summary": { "pages_changed": 1, "total_changes": 1 }, "metadata": { }, "tags": [ ], "test_mode": false, "recommendation": { "action": "reject", "text": "Do not accept as-is (trust 42/100). Strong forgery/tampering signals: ... Request the original from the issuer, or reject." } }
Fields that matter:
status — processing (poll again), analyzed (verdict ready),password-protected or corrupted (no verdict possible). trust_score and fraud_risk are null unless status is analyzed.
trust_score (0-100) — continuous score. 100 = no fraud signals; 0 =maximum cumulative penalty. Use this for your own threshold (e.g. flag anything under 70 for manual review).
fraud_risk — trusted | low | medium | high, a coarse label derivedfrom trust_score for when you want a single word instead of a number. The API value stays medium; the VerifyPDF dashboard shows that band as Needs Attention.
warnings — detected fraud signals, heaviest first. Each has anindicator_id and a human description.
text_changes / change_summary — structured per-page diff when thedocument was edited after it was created (what changed, on which page).
recommendation — added by this skill (not the API): an actionablenext step derived from the verdict. recommendation.action is a stable token your agent or CI step can branch on — accept, review, reject or unverifiable — and recommendation.text is the one-line human explanation (it names the heaviest warnings and flags edited documents).
Lead with recommendation.text, then back it with the evidence. The recommendation.action token tells you what to do without re-deriving it:
| action | When | What the agent should do | |----------|------|--------------------------| | accept | trusted / low | Document looks authentic; state the trust_score and proceed. | | review | medium (Needs Attention) | Surface the top 2-3 warnings and ask for a human look before accepting. | | reject | high | Lead with "likely forged or manipulated", list the warnings and any text_changes. Do not approve automatically. | | unverifiable | password-protected / corrupted | Ask the user for an unlocked, intact PDF and re-submit. |
Quote the verdict and the recommendation; never invent indicators that are not in warnings.
With a test key (key_test_…), the verdict comes from the filename, not the file contents, so you can prove the whole submit/poll/report flow end to end:
| Filename | Result | |---------------------|----------------------------------------------| | test-trusted.pdf | fraud_risk: trusted, trust_score: 100 | | test-low.pdf | fraud_risk: low, trust_score: 95 | | test-medium.pdf | fraud_risk: medium + synthetic warnings | | test-high.pdf | fraud_risk: high + synthetic warnings | | test-error.pdf | 500 — exercises your error-handling path |
Matching is case-insensitive and anything before .pdf after the marker is ignored (test-high_001.pdf works). Any other filename returns 400 with a test key. Test traffic is capped at 500 requests/org/month and never touches live quota, billing or your document data.
verification is enabled on your account.
90 days. submitted_url / thumbnail_url in the response are tokenized links valid for 60 minutes.
429 means a rate limit (back off and retry) or, on a test key, the monthlytest cap. 401 = bad/missing key. 403 = account suspended or no permission.
the contract in sync with the published v2 reference if it changes.
Other measured skills in the registry, with their headline benchmark lift.