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.

Get API keyskill.md

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

POST https://api.tampercheck.ai/api/v1/documents/

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).

ModeHTTPWhat to do next
async (default)202 AcceptedPoll GET /api/v1/documents/<uuid>/status/ until status is completed or failed, then load full results with GET /api/v1/documents/<uuid>/.
instant200 OKResponse 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 keyWhen to suppress
signature_forgeryDocuments where signatures are expected to look pasted (digital onboarding, e-signed forms).
jpeg_ghost_splicingLow-quality scans or photos where JPEG re-compression artifacts are inherent.
ela_anomalyHeavily compressed or re-saved images where ELA signals are unreliable.
font_size_inconsistencyMulti-section documents or forms with intentionally varied fonts.
metadata_image_editorDocuments 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:

HeaderDescription
X-Webhook-IDUUID 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-Signaturesha256=<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.

StatusError codeMeaningAction
400validation_errorUnsupported file type, file too large (>40 MB), or empty/unreadable file.Check file format and size. Accepted: PDF, JPG, JFIF, PNG, WEBP, BMP, TIFF.
401authentication_failedInvalid or missing API key.Verify your Authorization: Bearer dt_... header.
402insufficient_balanceWallet balance is too low to process this document.Add funds at Dashboard → Billing.
404not_foundDocument job ID does not exist or belongs to a different API key.Verify the job UUID and API key.
422processing_failedDocument could not be processed (corrupt image, unrenderable PDF, etc.). No charge applied.Verify the file is valid and try again.
503ai_service_unavailableThe 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"
}

Same Authorization header on all routes below.

  • GET /api/v1/documents/list/ - list jobs for this API key
  • GET /api/v1/documents/<uuid>/status/ - poll async jobs (lightweight)
  • GET /api/v1/documents/<uuid>/ - full results when status is completed
  • GET /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