API reference

The same detection pipeline that powers the analyst console, exposed over a small JSON API. Base URL: https://api.verifai.example. All responses are JSON. All requests must be HTTPS.

Authentication

Every request needs an Authorization: Bearer sk_live_... header. Create keys under Settings → API keys in the console. The full key is shown once at creation - only its SHA-256 hash is stored server-side.

Keys are scoped to a single organization. Revoking a key takes effect on the next request.

Submit a scan

POST/api/public/v1/scans

Pre-sample the video on your side into 1-12 evenly spaced JPEG frames (recommended 6-8) and submit them as base64 alongside a millisecond timestamp. Frames are analyzed synchronously; the response contains the final verdict, per-signal scores, and rate-limit headers.

Request body

filenamestring (optional)≤ 300 charsOriginal filename. Surfaced in the analyst console for triage.
external_refstring (optional)≤ 200 charsYour reference (KYC session id, case number). Idempotency is NOT enforced - resubmitting the same ref creates a second scan.
framesarray (required)1-12 itemsOrdered JPEG frames.
frames[].ts_msinteger (required)≥ 0Frame timestamp within the source video, in milliseconds.
frames[].image_base64string (required)100 - 3.5M charsBase64-encoded JPEG. No data: prefix. Recommended 512-1024 px longest side.

Example request

curl -X POST https://api.verifai.example/api/public/v1/scans \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "kyc-selfie-8123.mp4",
    "external_ref": "kyc-session-8123",
    "frames": [
      { "ts_ms": 0,    "image_base64": "/9j/4AAQSkZJRgAB..." },
      { "ts_ms": 500,  "image_base64": "/9j/4AAQSkZJRgAB..." },
      { "ts_ms": 1000, "image_base64": "/9j/4AAQSkZJRgAB..." },
      { "ts_ms": 1500, "image_base64": "/9j/4AAQSkZJRgAB..." },
      { "ts_ms": 2000, "image_base64": "/9j/4AAQSkZJRgAB..." },
      { "ts_ms": 2500, "image_base64": "/9j/4AAQSkZJRgAB..." }
    ]
  }'

Response - 200 OK

HTTP/1.1 200 OK
content-type: application/json
x-ratelimit-limit-minute: 30
x-ratelimit-remaining-minute: 29
x-ratelimit-limit-month: 10000
x-ratelimit-remaining-month: 9873

{
  "scan_id": "b41a9c8f-2d1e-4c3b-9a77-1e0f2f4d0e01",
  "verdict": "likely_synthetic",
  "score": 0.82,
  "confidence": 0.91,
  "signals": [
    { "signal": "frame_artifacts",       "score": 0.88, "confidence": 0.92, "weight": 0.22 },
    { "signal": "temporal_consistency",  "score": 0.74, "confidence": 0.80, "weight": 0.14 },
    { "signal": "cardiac_liveness",      "score": 0.90, "confidence": 0.78, "weight": 0.17 },
    { "signal": "physics_consistency",   "score": 0.71, "confidence": 0.75, "weight": 0.17 },
    { "signal": "provenance",            "score": 0.95, "confidence": 0.99, "weight": 0.10 }
  ]
}

Response fields

scan_iduuid-Stable id. Use for GET, PDF report, and signed receipt.
verdictenum"authentic" | "suspicious" | "likely_synthetic" | "inconclusive"Final calibrated verdict. See below.
scorenumber0 - 1Weighted ensemble suspicion score.
confidencenumber0 - 1Aggregate confidence across signals. Below 0.35 forces verdict to inconclusive.
signals[]array-Per-signal breakdown used to compute the verdict.
signals[].signalstring-Signal id. See Signal glossary.
signals[].scorenumber0 - 1Suspicion contribution of this signal (higher = more likely synthetic).
signals[].confidencenumber0 - 1How much weight this signal deserves for this specific clip.
signals[].weightnumber0 - 1Ensemble weight for this signal.

Special case - known-synthetic short-circuit

If the video's perceptual fingerprint matches an entry in the cross-org threat-intel registry, the full pipeline is skipped and a single known_synthetic signal is returned. The verdict is locked at likely_synthetic.

HTTP/1.1 200 OK

{
  "scan_id": "b41a9c8f-...",
  "verdict": "likely_synthetic",
  "score": 1,
  "confidence": 0.99,
  "signals": [
    {
      "signal": "known_synthetic",
      "score": 1,
      "confidence": 0.99,
      "weight": 1
    }
  ]
}

Fetch an existing scan

GET/api/public/v1/scans/:id

Read a previously submitted scan, including any analyst override applied in the console.

curl https://api.verifai.example/api/public/v1/scans/b41a9c8f-... \
  -H "Authorization: Bearer sk_live_..."

Returns the same shape as the POST response, plus a reviewed boolean and (if applicable) a reviewed_verdict. When present, reviewed_verdict is authoritative - the automated verdict is preserved for auditability under automated_verdict.

Signed verdict receipt

GET/api/public/v1/scans/:id/receipt

Returns a compact JWS signed with the VerifAI Ed25519 key. Anyone can verify it offline against the public key at /api/public/v1/pubkey - no network call to VerifAI is required to trust a receipt. The payload includes the current head of your organization's audit chain, binding the verdict to the tamper-evident log.

curl https://api.verifai.example/api/public/v1/scans/b41a9c8f-.../receipt \
  -H "Authorization: Bearer sk_live_..."
{
  "receipt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ2ZXJpZmFpIiw...sig",
  "payload": {
    "iss": "verifai",
    "typ": "verdict-receipt",
    "scan_id": "b41a9c8f-...",
    "org_id": "3fa9-...",
    "verdict": "likely_synthetic",
    "automated_verdict": "likely_synthetic",
    "reviewed": false,
    "score": 0.82,
    "confidence": 0.91,
    "completed_at": "2026-07-25T14:03:11.842Z",
    "external_ref": "kyc-session-8123",
    "audit_chain_head": "9f2c4b71a3e8...",
    "audit_chain_entries": 1284,
    "issued_at": "2026-07-25T14:03:12.001Z"
  },
  "verify": "https://api.verifai.example/api/public/v1/pubkey"
}

Verdict values

VerdictTriggerMeaning
authenticscore < 0.35-Cleared. Safe to proceed.
suspicious0.35 - 0.65-Review recommended before high-value action.
likely_syntheticscore ≥ 0.65-Mandatory analyst review before acting.
inconclusiveconfidence < 0.35-Too little signal (very short clip, extreme compression, no face). Resubmit with better frames.

Signal glossary

SignalWhat it measures
frame_artifactsPer-frame GAN/diffusion artifact classifier.
temporal_consistencyFrame-to-frame identity, lighting, and structure drift.
lip_syncAudio-visual phoneme alignment (when audio available).
audio_synthesisProsody and spectral fingerprints of synthetic voice.
provenanceC2PA manifest presence, integrity, and issuer trust.
compression_forensicsDouble-compression and re-encode markers.
cardiac_livenessrPPG - skin-chroma oscillations in the 0.7-3 Hz cardiac band.
physics_consistencyCatchlight geometry, shadow direction agreement, saccade micro-timing.
known_syntheticPerceptual-hash match against the cross-org threat-intel registry.

Errors

All errors are JSON with an error string and, where useful, a details field.

StatusWhen
400Body failed schema validation. details contains the field path.
401Missing, malformed, or revoked API key.
404Scan id not found or not owned by your org.
409Scan has no verdict yet (receipt endpoint only).
429Per-minute or per-month limit exceeded. See Rate limits.
500Detection backend failure. scan_id is returned when available so you can retry the same case.
HTTP/1.1 401 Unauthorized
{ "error": "Invalid or revoked API key" }
HTTP/1.1 400 Bad Request
{ "error": "Invalid body", "details": "frames: Array must contain at most 12 element(s)" }

Rate limits

Each API key is capped at 30 requests per minute and 10,000 requests per month. Every response includes:

HeaderMeaning
x-ratelimit-limit-minutePer-minute cap (30).
x-ratelimit-remaining-minuteRequests left in the current UTC minute.
x-ratelimit-limit-monthPer-month cap (10000).
x-ratelimit-remaining-monthRequests left in the current UTC month.
retry-afterSeconds to wait before retrying (429 only).

Every 429 is written to the tamper-evident audit log. Contact us for higher limits or streaming ingestion.

HTTP/1.1 429 Too Many Requests
retry-after: 42
x-ratelimit-remaining-minute: 0

{ "error": "Rate limit exceeded (per minute)", "retry_after_seconds": 42 }

Evidence report (PDF)

Every scan can be downloaded as a signed PDF from the analyst console (PDF report on the scan detail page). The PDF includes the verdict, per-signal scores and confidences, frame timestamps, analyst notes, override history, and the current head hash of your organization's audit chain - so the report itself is verifiable against the append-only log.