Response format
One envelope, identical across every gate.
{
"ok": true,
"verdict": "clean" | "suspect" | "block",
"findings": [
{
"code": "email.typo_domain",
"severity": "low" | "medium" | "high" | "critical",
"message": "Domain \"gmial.com\" looks like a typo of \"gmail.com\"",
"location": "gmial.com"
}
],
"data": { "normalised": "bob@gmail.com" },
"checks_run": ["email.syntax", "email.role_account", "..."],
"checks_skipped": [
{ "code": "email.mx", "reason": "error" }
],
"meta": { "gate": "emails", "ms": 41, "ruleset": "2026.08.19" }
}Verdict is derived, not scored#
There's no numeric score. Every finding carries a fixed severity, and verdict is the highest severity present: no findings is clean, low/medium is suspect, high/critical is block.
A finding always moves the verdict — there's no informational tier. Emitting a finding at all is already saying the input isn't clean. Neutral observations that aren't a risk signal go in data instead — for example, a normalised email address is a fact about the input, not a finding.
Findings#
Each finding has a stable, namespaced code (documented on its own page — see each gate's page for the full list), a severity, a human-readable message, and an optional location pointing at the specific part of the input that triggered it.
data is gate-specific output, not findings#
data holds values a gate resolved while checking your input — a normalised address, a resolved redirect chain, a domain's registration age — typed per gate and documented on that gate's page. A field is absent, not null, whenever the check that would have produced it didn't run. Don't treat a missing field as a negative result; check checks_skipped to see whether it ran at all.
checks_run and checks_skipped#
checks_run lists every check that actually executed. checks_skipped lists every check that didn't, with one of four reasons:
plan— the check exists but isn't available on your current plan.input— the check needs an optional field you didn't send.error— the check ran, failed or timed out, and the gate returned its partial result rather than failing the whole call.unavailable— a check this one depends on was itself skipped or failed, so this one never ran.
Don't proxy a raw checks_skipped array through to your own end users. It's a map of exactly what wasn't checked — useful for you to see what you'd gain from a different plan or a richer request, but a reconnaissance map if shown to whoever you're screening.