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 (receivedworking heartbeats → 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 detail message — 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.

ParameterTypeDescription
file | text | bytes_b64 | urlbodyThe document: multipart file, raw prose, inline base64, or a public URL.
deepquery, boolAlso fetch each cited source and judge support/contradiction (slower, ~a minute).
streamquery, boolReturn an SSE lifecycle stream instead of a single response.
bash
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.

bash
curl https://www.stipple.sh/v1/fact-checks/fc_6d22fb4033cbac40

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

ParameterTypeDescription
file | text | bytes_b64 | urlbodyThe document or prose to analyse.
json
{
  "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.

ParameterTypeDescription
file | bytes_b64 | urlbodyThe document: multipart PDF/image, inline base64, or a direct public document URL (fetched raw, SSRF-guarded).
json
{
  "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.

ParameterTypeDescription
file | bytes_b64bodyThe document (PDF or image). URL intake is not available on this endpoint.
streamquery, boolSSE lifecycle stream — recommended for images (deep visual analysis is slower).
freshquery, boolBypass 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

Anonymous calls work everywhere (20 documents/day per IP). A free API key gives you your own 50/day quota and usage metering — send it as Authorization: Bearer stp_… on any endpoint. An invalid key is a hard 401, never a silent fallback.