---
name: tampercheck-api
description: >-
  Help developers integrate TamperCheck.ai’s public document-fraud API (HTTP, auth,
  multipart upload, responses, billing). Use when writing clients, SDKs, tests, or
  docs for TamperCheck from outside the product codebase.
---

# TamperCheck.ai - API integration (for Claude Code)

## What this skill is for

Use this when the user wants to **call TamperCheck from their own application** (scripts, backends, mobile apps, CI, etc.). Focus on the **public HTTPS API** only. Do **not** assume access to TamperCheck’s internal systems, repositories, or admin tools.

## Product (one sentence)

TamperCheck analyzes uploaded files-**PDFs, camera photos, and scans** (JPG, PNG, WEBP, BMP, TIFF, PDF)-for tampering and fraud signals, returning a **verdict**, **risk score**, and structured **findings**. Processing supports both **async** (default, returns immediately) and **instant** (blocks until completion) modes.

## Base URL

- **Production API:** `https://api.tampercheck.ai`

All paths below are relative to that host (e.g. full URL: `https://api.tampercheck.ai/api/v1/documents/`).

## Getting credentials

1. The user signs up at **tampercheck.ai** and opens the **Dashboard**.
2. They create an **API key** under **Developers** (or equivalent). Keys look like `dt_...`.
3. They may need to add a **payment method** and **wallet funds** before requests succeed if trial credits are exhausted.

Third-party integrations never send LLM provider keys (OpenAI, Anthropic, etc.) to TamperCheck’s API body or headers for document analysis-those are configured **in the dashboard** by the account owner.

## Authentication

Every request:

```http
Authorization: Bearer <TAMPERCHECK_API_KEY>
```

Without a valid key, the API responds with **401**.

## Strategies

| Strategy | How to call | Result shape |
|----------|-------------|--------------|
| `tampering_detection` | `POST /api/v1/documents/` (default) | `authentic` / `tampered`, findings, risk score |
| `deepfake_detection` | `POST /api/v1/documents/` with `strategy=deepfake_detection` | `authentic` / `ai_generated` / `face_swap` / `uncertain`, `artifact_breakdown` |
| `document_extraction` | `POST /api/v1/extract-documents/` only | `pages[].images[]` crops (no verdict) |

## Endpoints

| Method | Path | Role |
|--------|------|------|
| `POST` | `/api/v1/documents/` | Analyze (`strategy`, `mode`, `webhook_id`, `suppress_categories`). |
| `POST` | `/api/v1/extract-documents/` | Split a multi-document scan into page-scoped cropped images. |
| `GET` | `/api/v1/documents/list/` | List analysis jobs for this API key. |
| `GET` | `/api/v1/documents/<uuid>/` | Fetch complete job details by id (must belong to this key). |
| `GET` | `/api/v1/documents/<uuid>/status/` | Lightweight status check (for polling async jobs). |
| `POST` | `/api/v1/documents/<uuid>/report-link/` | Mint a **30-minute** expiring HTTPS URL to download the forensic **PDF** report (completed jobs only). |
| `GET` | `/api/v1/reports/download/?token=...` | Download the PDF using the token from `report-link` (no API key on this GET). |
| `GET` | `/api/v1/usage/?since=...&until=...` | Usage summary for billing/observability. |
| `GET` | `/api/v1/wallet/usage/?since=...&until=...` | Paginated wallet credits/debits for the account (free). |

Date query params for usage and wallet usage should be calendar dates in `YYYY-MM-DD` (inclusive UTC day range). `from`/`to` are accepted as aliases for `since`/`until` on wallet usage.

## PDF forensic report (expiring download link)

For **server-side** or **automation** workflows you should not scrape the dashboard. Instead:

1. **`POST /api/v1/documents/<job_uuid>/report-link/`** with the same `Authorization: Bearer ...` as other v1 calls.
2. Response JSON includes **`download_url`**, **`expires_at`** (ISO timestamp), and **`expires_in_seconds`** (always **1800** = 30 minutes from mint time).
3. **`GET`** the `download_url` (or pass the `token` query param to `/api/v1/reports/download/?token=...`) **before it expires**. This GET does **not** send your API key.

The link is **signed**, **bound** to the job and owning API key, and **single-use** - only the first `GET` returns the PDF; subsequent requests respond with **410 Gone**. After **30 minutes** the token also expires automatically.

> **Dashboard users:** The dashboard downloads reports via `GET /api/dashboard/jobs/<uuid>/report/` using session authentication. No token URL is involved - the PDF streams directly and requires an active session.

```bash
curl -sS -X POST "https://api.tampercheck.ai/api/v1/documents/<job_uuid>/report-link/" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

# Then (within 30 minutes, single-use, no Authorization header):
curl -sS -L "PASTE_download_url_HERE" -o report.pdf
```

## `POST /api/v1/documents/` - request

### Two modes of analysis

- **`async`** (default): API returns immediately with job ID and `status: "processing"`. You poll `/api/v1/documents/<id>/status/` for results. Best for high-volume, non-blocking workflows.
- **`instant`**: API blocks until analysis completes, returning full results. Useful for synchronous integrations or dashboard preview.

### Request fields

- **Content-Type:** `multipart/form-data`
- **Fields:**
  - **`document`** (required): file - PDF, JPG, PNG, WEBP, BMP, or TIFF; **max 40 MB**.
  - **`document_identifier`** (optional): string (e.g. your loan id or ticket id), up to **512** characters, returned in the response for correlation.
  - **`mode`** (optional): string, either `"async"` (default) or `"instant"`. Controls whether the API returns immediately or waits for analysis.
  - **`strategy`** (optional): `"tampering_detection"` (default) or `"deepfake_detection"`. See Strategies above. Extraction is a separate endpoint, not this field.
  - **`webhook_id`** (optional): UUID of a webhook registered under **Dashboard → Developers → Webhooks**. When set, only that webhook receives the completion notification. When omitted, all active webhooks on the account are notified. Returns **400** with `error: "invalid_webhook_id"` if the UUID is unknown, inactive, or not owned by the account.
  - **`suppress_categories`** (optional): JSON array of finding category keys to exclude from the verdict and risk score (e.g. `["signature_forgery", "jpeg_ghost_splicing"]`). Useful when processing poor-quality scans where certain forensic signals are unreliable, or digital documents with known editing patterns. Suppressed findings are removed after QC validation; the final `risk_score` and `verdict` reflect only surviving findings. Unknown keys are silently ignored.
  - **`pdf_password`** (optional): string, up to **512** characters. Password for encrypted/password-protected PDF files. Omit or leave blank for unprotected files. If a password-protected PDF is uploaded without this field, the API returns **400** with `error: "pdf_password_required"`. If the password is wrong, returns **400** with `error: "pdf_password_incorrect"`. The password is encrypted at rest and never returned in any API response.

### Example: Async mode (default, returns immediately)

```bash
curl -sS -X POST "https://api.tampercheck.ai/api/v1/documents/" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "document=@./file.pdf" \
  -F "document_identifier=case-2026-001" \
  -F "mode=async"
```

Response: **HTTP 202 Accepted** with `{"id": "...", "status": "processing"}`

Then poll for results:

```bash
curl -sS "https://api.tampercheck.ai/api/v1/documents/<id>/status/" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Example: Instant mode (blocks until analysis completes)

```bash
curl -sS -X POST "https://api.tampercheck.ai/api/v1/documents/" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "document=@./file.pdf" \
  -F "document_identifier=case-2026-001" \
  -F "mode=instant"
```

Response: **HTTP 200 OK** with complete analysis results.

### Example: Suppress finding categories

If you have poor-quality scan copies or digital documents with known editing patterns, suppress specific finding categories so they don't affect the verdict:

```bash
curl -sS -X POST "https://api.tampercheck.ai/api/v1/documents/" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "document=@./scanned_statement.pdf" \
  -F "mode=async" \
  -F 'suppress_categories=["signature_forgery", "jpeg_ghost_splicing"]'
```

Common categories: `signature_forgery`, `jpeg_ghost_splicing`, `ela_anomaly`, `font_size_inconsistency`, `metadata_image_editor`, `double_jpeg_compression`.

### Example notes for generated code

- Use the platform’s multipart upload (e.g. `FormData` in browsers, `multipart.File` in Python, etc.).
- Do not base64 the whole request unless the user explicitly wants a non-multipart flow-**the documented API is multipart**.
- For **async mode**: start with a job ID poll loop using exponential backoff (start at 1s, back off to 5-10s max). Stop when `status` is `completed` or `failed`.

## `POST /api/v1/extract-documents/`

Split a multi-document scan/photo into individual PNG crops, grouped by page.

Multipart fields: `document` (required), `mode` (`async` default | `instant`), optional `document_identifier`, optional `webhook_id`.

Billing: separate extraction SKU; billable units = `ceil(page_count / 6)` (same metering as analysis).

Poll with `GET /api/v1/documents/<uuid>/status/` then `GET /api/v1/documents/<uuid>/`. Extraction jobs return `strategy: "document_extraction"` and a `pages` array (each page has `images[]` with base64 `data` and `bbox`).

```bash
curl -sS -X POST "https://api.tampercheck.ai/api/v1/extract-documents/" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "document=@./scan.pdf" \
  -F "mode=async"
```

## Webhooks

Register HTTPS endpoints under **Dashboard → Developers → Webhooks**. When a job reaches `completed` or `failed`, TamperCheck POSTs the result to your URL.

### Target a webhook on upload

Pass the webhook UUID as `webhook_id` in the multipart upload:

```bash
curl -sS -X POST "https://api.tampercheck.ai/api/v1/documents/" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "document=@./file.pdf" \
  -F "webhook_id=7c9e6679-7425-40de-944b-e07fc1f90ae7"
```

- **With `webhook_id`:** only that webhook receives the notification.
- **Without `webhook_id`:** every active webhook on the account is notified (broadcast).

### Incoming delivery headers

TamperCheck POSTs JSON to your registered URL. The body matches `GET /api/v1/documents/<uuid>/` for the finished job (analysis shape or page-scoped extraction shape depending on strategy).

| Header | Description |
|--------|-------------|
| `X-Webhook-ID` | UUID of the webhook endpoint that was called (same ID you registered and optionally passed as `webhook_id` on upload). |
| `X-Webhook-Signature` | `sha256=<hex>` HMAC-SHA256 of the **raw** JSON request body, keyed with the webhook secret. |

Verify signatures before trusting the payload:

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

## Success response (HTTP 200)

On success, expect JSON including (names may appear in this spirit; always handle unknown fields defensively):

- **`id`**, **`status`** (e.g. completed when done)
- **`original_filename`**, **`document_identifier`**
- **`document_type`** (detected category)
- **`document_subtype`** (optional — finer format-match classification, e.g. `Digital National ID (ePhilID)`)
- **`verdict`** - see below
- **`risk_score`** - numeric risk (commonly **0–100**, higher = riskier)
- **`confidence`**
- **`findings`** - array of objects with fields such as **`category`**, **`severity`**, **`description`**, **`confidence`** (and sometimes region/evidence)
- **`ai_analysis`**, **`cv_metrics`**, **`metadata_analysis`** (as applicable)
- **`processing_time_s`**
- **`calibration`** - scoring breakdown and calibrated findings (structure is nested JSON)
- **`explanation`**, **`human_summary`**
- **`created_at`**, **`completed_at`**

Do **not** invent legacy fields such as **`tamper_score`** or a nested **`analysis.points_checked`** unless the live API documentation the user pastes explicitly includes them.

### Verdict values

`verdict` is one of:

- `authentic`
- `tampered`

## Errors

| Status | Meaning | What to build |
|--------|---------|----------------|
| **401** | Bad or missing API key | Prompt user to fix the key or login to dashboard. |
| **402** | Insufficient wallet balance | Tell the user to **add funds** (billing) before retrying. |
| **400** | Bad file (empty, wrong type, too large) | Validate file type and size client-side when possible. |
| **422** | Processing failed | Read **`error`** in the body; optionally surface **`id`** for support. |

## Billing and activation (product-level)

- Usage is typically **pay-as-you-go** from a **wallet** (e.g. a per-document price such as **$0.50** - always tell the user to confirm current pricing on **tampercheck.ai/pricing**).
- New accounts must **add a payment method** before the API will run. The first top-up (**$5** minimum) is matched with **$5** in free credits.
- A wallet with no card and no credit returns **402** with `"error": "payment_method_required"`; a funded account that runs its balance negative returns **402** with `"error": "wallet_suspended"`. Both are cleared by funding the wallet.

## How Claude Code should behave

1. **Prefer official docs** the user provides or links (`https://tampercheck.ai/docs` if available) over guessing fields.
2. **Generate integration code** (HTTP client, error handling for 401/402/422, optional retries only where safe).
3. **Never fabricate** response fields; if uncertain, say what is stable (multipart + Bearer auth + verdict/risk/findings) and suggest a test call.
4. **Keep secrets out of logs** (API keys in env vars, not committed files).
5. **Do not reference** internal implementation details of TamperCheck’s own apps or repositories-this skill describes the **public API contract** only.

## Quick integration checklist

- [ ] API key in environment variable
- [ ] `POST` multipart with `document`
- [ ] Handle **402** with a clear “add funds” path
- [ ] Parse **`verdict`**, **`risk_score`**, **`findings`**
- [ ] Optional: store **`document_identifier`** and **`id`** for support and idempotency discussions with the user
- [ ] Optional: register webhooks and pass **`webhook_id`** on upload; verify **`X-Webhook-Signature`** on delivery
