API reference
Plain REST over HTTPS with JSON responses. No authentication while the service is free — limits apply per IP. All endpoints live under https://www.stipple.sh.
Conventions
- Document inputs are accepted three ways: multipart
file, JSON{"bytes_b64": "...", "filename": "..."}, or JSON{"url": "https://..."}(public hosts only; fetched server-side with strict SSRF protections; analysis endpoints only). - Long-running calls support
?stream=1— a Server-Sent Events stream (received→workingheartbeats →result) so proxies never time out mid-analysis. - Identical bytes are cached by content hash — re-submitting the same document is free and instant.
- Errors use standard status codes with a JSON
detailmessage — see Errors & limits.
Fact-check a document
POST /v1/verify-references
Verifies a document's citations (live / archived / dead, scholarly papers matched against their claimed titles), recomputes its internal arithmetic, and — in deep mode — fetches each cited source to judge whether it supports the claim. Results persist under a content-hash id and include a public shareable permalink.
| Parameter | Type | Description |
|---|---|---|
| file | text | bytes_b64 | url | body | The document: multipart file, raw prose, inline base64, or a public URL. |
| deep | query, bool | Also fetch each cited source and judge support/contradiction (slower, ~a minute). |
| stream | query, bool | Return an SSE lifecycle stream instead of a single response. |
curl -X POST "https://www.stipple.sh/v1/verify-references?deep=1&stream=1" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/report.pdf"}'Key response fields: summary (totals + headline), references[], claims[], internal_consistency[], limitations, check_id, permalink.
Retrieve a fact-check
GET /v1/fact-checks/{check_id}
Fetch a stored fact-check result by its content-hash id (fc_ + 16 hex). This is the data behind the public /fc/<slug>-<hex> permalink pages.
curl https://www.stipple.sh/v1/fact-checks/fc_6d22fb4033cbac40Detect AI-written text
POST /v1/detect-ai-text
Estimates the probability that a document's prose was AI-written, with the specific linguistic tells. Abstains (applicable: false) on forms, tables, and scans, where style detection is unreliable. Stateless — nothing is stored.
| Parameter | Type | Description |
|---|---|---|
| file | text | bytes_b64 | url | body | The document or prose to analyse. |
{
"applicable": true,
"probability": 0.92,
"lean": "ai",
"tells": ["uniform paragraph rhythm", "hedged superlatives", ...],
"reasoning": "...",
"limitations": "Probability is a confidence, not a calibrated truth. ..."
}Classify a financial document
POST /v1/classify
Identify a document's type — we specialise in financial documents: payslip, tax_invoice, bank_statement, salary_certificate, payg_summary, receipt — with issuing country and confidence. Anything non-financial returns other; below the confidence threshold it abstains as unknown rather than guessing. Stateless; type only, never an authenticity judgment.
| Parameter | Type | Description |
|---|---|---|
| file | bytes_b64 | url | body | The document: multipart PDF/image, inline base64, or a direct public document URL (fetched raw, SSRF-guarded). |
{
"document_type": "payslip",
"country_code": "AU",
"confidence": 0.93,
"is_financial_document": true,
"evidence": ["Gross Pay", "PAYG withheld", "Superannuation"],
"supported_types": ["bank_statement", "payg_summary", "payslip", ...],
"limitations": "Type classification only — NOT an authenticity or fraud judgment. ..."
}Inspect a document (forensics)
POST /v1/warrants
Runs the full forensic inspection — tampering signals, structure and metadata analysis, arithmetic reconciliation — and returns a warrant: the document's verification record. The warrant is a signal for review, not a fraud verdict.
| Parameter | Type | Description |
|---|---|---|
| file | bytes_b64 | body | The document (PDF or image). URL intake is not available on this endpoint. |
| stream | query, bool | SSE lifecycle stream — recommended for images (deep visual analysis is slower). |
| fresh | query, bool | Bypass the content-hash cache and re-inspect. |
Key response fields: warrant_id, risk_band (low / medium / high / insufficient / error), inspection_quality, recommended_action, summary, signals[].
Cache check
GET /v1/warrants/check?sha256={hex}
Has this exact document already been inspected? Hash the file yourself (SHA-256, lowercase hex) and check before submitting — identical bytes return the existing warrant for free.
Retrieve a warrant
GET /v1/warrants/{warrant_id}
Fetch a stored warrant by id (warrant_ + 16 hex). Add ?format=md for a human-readable Markdown report.
PDF report
GET /v1/warrants/{warrant_id}/report.pdf
An audit-ready, branded PDF scorecard of the warrant — suitable for case files.
Warrant feedback
POST /v1/warrants/{warrant_id}/feedback
Record a reviewer's verdict on a warrant: {"verdict": "up" | "down", "note": "..."}. Feedback improves the engine's precision over time.
Tool feedback
POST /v1/feedback
Thumbs up/down on a fact-check or AI-text result: {"tool": "detect_ai_text" | "verify_references", "verdict": "up" | "down"}, with optional ref and note.
Health
GET /health
Liveness and version: {"status": "ok", "engine_version": "...", ...}
Create an API key
POST /v1/keys
Self-serve, free, instant: {"email": "you@company.com"} → the key (returned once, stored only as a hash), its key_id, and your daily limit. Up to 5 keys per email. See Authentication.
Metered usage
GET /v1/usage
Requires Authorization: Bearer stp_…. Returns today's used/remaining plus the last 7 days — read from the same counters that enforce the quota.
Authentication
Authorization: Bearer stp_… on any endpoint. An invalid key is a hard 401, never a silent fallback.