For decision makers: TamperCheck plugs into your existing systems via a simple API - typically one sprint to integrate, no proprietary format, and no mandatory dashboard for compliance teams. The reference below is for engineers implementing the integration.
API Documentation
Upload documents via REST; get a verdict, risk score, and findings. Default mode=async or use instant for a single blocking response.
Authentication
Send your TamperCheck API key as a Bearer token: Authorization: Bearer dt_.... Configure AI providers in the dashboard; the default verified provider is used for analysis.
Analyze endpoint
Request (multipart)
Upload the file as document (PDF, JPG, JFIF, PNG, WEBP, BMP, TIFF; max 40 MB). Optional fields: document_identifier (your correlation id, max 512 chars), mode (async or instant), webhook_id (UUID from Dashboard → Developers → Webhooks; see Webhooks), and suppress_categories (JSON array of finding category keys to exclude; see Suppress categories).
Async & instant modes
Omit mode or send mode=async for the default: the API enqueues analysis and responds immediately. Send mode=instant when the client can hold the connection open until analysis finishes (typically about a minute).
| Mode | HTTP | What to do next |
|---|---|---|
async (default) | 202 Accepted | Poll GET /api/v1/documents/<uuid>/status/ until status is completed or failed, then load full results with GET /api/v1/documents/<uuid>/. |
instant | 200 OK | Response already includes verdict, risk score, findings, and summaries. |
Async - upload (default)
curl -sS -X POST "https://api.tampercheck.ai/api/v1/documents/" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "document=@./document.pdf" \ -F "document_identifier=loan-app-12345" \ -F "mode=async"
Async - 202 response
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing",
"original_filename": "statement.pdf",
"document_identifier": "loan-app-12345",
"page_count": 3,
"documents_billed": 3
}Async - poll status
curl -sS "https://api.tampercheck.ai/api/v1/documents/<job-uuid>/status/" \ -H "Authorization: Bearer YOUR_API_KEY"
Use exponential backoff when polling (e.g. start at 1s, cap around 5–10s). Stop when status is completed or failed.
Instant - upload
curl -sS -X POST "https://api.tampercheck.ai/api/v1/documents/" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "document=@./document.pdf" \ -F "mode=instant"
Suppress finding categories
If you process poor-quality scan copies where certain forensic signals are unreliable, or digital documents with known editing patterns you want to ignore, pass suppress_categories to exclude specific finding categories from the final verdict and risk score.
Suppressed findings are removed after QC validation and before verdict/score calculation. The response findings[] array will not include them, and the risk_score and verdict reflect only the surviving findings.
Example - suppress signature and JPEG ghost findings
curl -sS -X POST "https://api.tampercheck.ai/api/v1/documents/" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "document=@./bank_statement.pdf" \ -F "mode=async" \ -F 'suppress_categories=["signature_forgery", "jpeg_ghost_splicing"]'
Common categories to suppress
| Category key | When to suppress |
|---|---|
signature_forgery | Documents where signatures are expected to look pasted (digital onboarding, e-signed forms). |
jpeg_ghost_splicing | Low-quality scans or photos where JPEG re-compression artifacts are inherent. |
ela_anomaly | Heavily compressed or re-saved images where ELA signals are unreliable. |
font_size_inconsistency | Multi-section documents or forms with intentionally varied fonts. |
metadata_image_editor | Documents known to be cropped or rotated before submission. |
Webhooks
Register HTTPS endpoints under Dashboard → Developers → Webhooks. When a job reaches completed or failed, TamperCheck POSTs the result to your URL—use this instead of polling in async workflows.
Pass webhook_id on upload
Include the webhook UUID (copied from the Developers dashboard) as an optional multipart field on POST /api/v1/documents/:
curl -sS -X POST "https://api.tampercheck.ai/api/v1/documents/" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "document=@./document.pdf" \ -F "document_identifier=loan-app-12345" \ -F "webhook_id=7c9e6679-7425-40de-944b-e07fc1f90ae7"
- With
webhook_id: only that webhook receives the completion notification. - Without
webhook_id: every active webhook on the account is notified (broadcast). - 400 with
error: "invalid_webhook_id"if the UUID is unknown, inactive, or not owned by your account.
Incoming delivery
TamperCheck POSTs JSON to your registered URL. The body matches GET /api/v1/documents/<uuid>/ for the finished job. Two headers identify and authenticate the request:
| Header | Description |
|---|---|
X-Webhook-ID | UUID of the webhook endpoint that was called—the same ID you registered and optionally passed as webhook_id on upload. Use it to route the payload when one URL serves multiple integrations. |
X-Webhook-Signature | sha256=<hex> HMAC-SHA256 of the raw JSON request body, keyed with the webhook secret shown when you created the endpoint. |
Example delivery
POST https://your-app.example/webhooks/tampercheck
Content-Type: application/json
X-Webhook-ID: 7c9e6679-7425-40de-944b-e07fc1f90ae7
X-Webhook-Signature: sha256=a1b2c3d4e5f6...
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"original_filename": "statement.pdf",
"document_identifier": "loan-app-12345",
"verdict": "tampered",
"risk_score": 42,
"confidence": 0.78,
"findings": [],
"created_at": "2026-04-02T12:00:00Z",
"completed_at": "2026-04-02T12:00:03Z"
}Verify signatures (Python)
import hmac
import hashlib
def verify_webhook(body: bytes, signature_header: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
provided = signature_header.removeprefix("sha256=")
return hmac.compare_digest(expected, provided)Respond with any 2xx status to acknowledge receipt. Always verify X-Webhook-Signature before processing the payload.
Response
Instant mode and GET job detail return 200 with verdict (authentic or tampered), risk_score (0–100), findings, calibration, explanation, and timestamps when analysis is done.
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"original_filename": "statement.pdf",
"document_identifier": "loan-app-12345",
"document_type": "bank_statement",
"document_subtype": "Indian Savings Account Statement",
"verdict": "tampered",
"risk_score": 42,
"confidence": 0.78,
"findings": [
{
"category": "font_inconsistency",
"severity": "medium",
"description": "Mixed font metrics in the totals row.",
"confidence": 0.71
}
],
"cv_metrics": {},
"metadata_analysis": null,
"processing_time_s": 3.2,
"calibration": {
"score_breakdown": {},
"suppressed_count": 0,
"calibration_notes": [],
"calibrated_findings": []
},
"explanation": "Plain-language summary…",
"human_summary": "…",
"created_at": "2026-04-02T12:00:00Z",
"completed_at": "2026-04-02T12:00:03Z"
}Error responses
All error responses return a JSON body with error (machine-readable code) and detail (human-readable message). Some errors also include the job id and status.
| Status | Error code | Meaning | Action |
|---|---|---|---|
400 | validation_error | Unsupported file type, file too large (>40 MB), or empty/unreadable file. | Check file format and size. Accepted: PDF, JPG, JFIF, PNG, WEBP, BMP, TIFF. |
401 | authentication_failed | Invalid or missing API key. | Verify your Authorization: Bearer dt_... header. |
402 | insufficient_balance | Wallet balance is too low to process this document. | Add funds at Dashboard → Billing. |
404 | not_found | Document job ID does not exist or belongs to a different API key. | Verify the job UUID and API key. |
422 | processing_failed | Document could not be processed (corrupt image, unrenderable PDF, etc.). No charge applied. | Verify the file is valid and try again. |
503 | ai_service_unavailable | The AI analysis service is temporarily down. No charge applied. | Retry after a short delay (e.g. exponential backoff). |
Example error responses
402 - Insufficient balance
{
"detail": "Insufficient wallet balance. Please top up at /dashboard/billing."
}503 - AI service unavailable
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "ai_service_unavailable",
"detail": "The AI analysis service is temporarily unavailable. Your document was not charged. Please retry in a few moments."
}422 - Processing failed
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "processing_failed",
"detail": "Could not decode image bytes"
}Related endpoints
Same Authorization header on all routes below.
GET /api/v1/documents/list/- list jobs for this API keyGET /api/v1/documents/<uuid>/status/- poll async jobs (lightweight)GET /api/v1/documents/<uuid>/- full results whenstatusiscompletedGET /api/v1/usage/?since=&until=- usage summary
AI providers
Configure AI provider keys under Dashboard → Developers. The public API does not accept per-request provider headers.
MCP for Claude Code and Cursor
The tampercheck-mcp server exposes the same REST API to coding agents (stdio, local). Set TAMPERCHECK_API_KEY in the server environment.
pip install tampercheck-mcp claude mcp add --transport stdio --env TAMPERCHECK_API_KEY="dt_..." tampercheck -- tampercheck-mcp
API keys: Dashboard → Developers. Cursor: add the same command in mcp.json. See the tampercheck-mcp README for full setup.
Claude Code and agent skills
Download the TamperCheck API skill as a Markdown file (YAML frontmatter + integration guide). Add it to Cursor, Claude Code, or any workflow that loads project skills so assistants help you integrate without guessing request shapes.
Download skill.md