Developer API Guide
Submit documents, poll status, and retrieve forensic analysis results. Built with Python + FastAPI + OpenCV. No authentication required — open access for hackathon evaluation.
Quick Facts
- Base URL (prod):
https://forgery.tanuh.ai/api - Base URL (local):
http://127.0.0.1:8000 - Authentication: None (open access)
- Accepted: PDF, JPG, PNG, TIFF, BMP, WEBP
- Max file size: 25 MB
- Preview bytes cached in memory; re-fetch allowed until server restart
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /jobs |
Upload a document and create a processing job |
| GET | /jobs/{job_id} |
Poll job status and progress (0.0 → 1.0) |
| GET | /jobs/{job_id}/results |
Fetch complete forgery analysis results |
| GET | /jobs/{job_id}/files/{file_name} |
Rendered page preview image (cached in memory, deleted later) |
| GET | /health |
Service health check |
Interactive docs: https://forgery.tanuh.ai/api/docs · OpenAPI JSON: https://forgery.tanuh.ai/api/openapi.json
1 — Upload a document
Send as multipart/form-data. Returns a job_id to poll.
curl -X POST "https://forgery.tanuh.ai/api/jobs" \ -F "file=@/path/to/document.pdf" \ -F "ocr_enabled=true"
2 — Poll job status
Poll until status is "complete" or "error".
curl "https://forgery.tanuh.ai/api/jobs/JOB_ID"
# Response:
{
"job_id": "abc123",
"status": "processing",
"progress": 0.42
}
3 — Fetch results
Returns full forgery findings with per-page categories, regions, and summary text.
curl "https://forgery.tanuh.ai/api/jobs/JOB_ID/results"
4 — Preview image
Fetch rendered page image with bounding boxes. Preview bytes are cached in memory.
curl -L \ "https://forgery.tanuh.ai/api/jobs/JOB_ID/files/page_001.png" \ -o preview.png
Preview bytes are cleared when the server restarts.
Python (requests) — End-to-end flow
Upload a file, poll for completion, then fetch results.
# pip install requests
import time
import requests
BASE_URL = "https://forgery.tanuh.ai/api"
FILE_PATH = "/path/to/document.pdf"
with open(FILE_PATH, "rb") as handle:
response = requests.post(
f"{BASE_URL}/jobs",
files={"file": handle},
data={"ocr_enabled": "true"},
timeout=60,
)
response.raise_for_status()
job_id = response.json()["job_id"]
for _ in range(6):
status = requests.get(f"{BASE_URL}/jobs/{job_id}", timeout=30).json()
if status.get("status") in ("complete", "error"):
break
time.sleep(2)
results = requests.get(f"{BASE_URL}/jobs/{job_id}/results", timeout=30)
print(results.json())
Results Response Example
Job status response
{
"job_id": "abc123",
"status": "complete",
"progress": 1.0,
"created_at": "2026-05-14T10:04:12Z"
}
Full results payload
{
"job_id": "abc123",
"file_name": "report.pdf",
"category_summary": {
"C1": 2, "C3": 1
},
"findings_summary": {
"summary_text": "Page 1: Added content ...",
"findings": [{
"page": 1,
"category_id": "C3",
"category_label": "Added content",
"summary": "Page 1: Possible stamp alteration",
"box": {"x":120,"y":80,"w":200,"h":60}
}]
},
"pages": [{
"page_number": 1,
"image_url": "/jobs/abc123/files/page_001.png",
"categories": ["C3"],
"regions": [...]
}]
}
Privacy & Data Handling
Uploaded files are deleted immediately after processing completes. Rendered preview images are cached in-memory and cleared on server restart. No document data is written to disk or stored in any database.