Documentation menu
Reference
API reference
The ValidAnytime API is a small, predictable REST surface — JSON in, JSON out, Bearer-authenticated — for creating monitors, ingesting events idempotently, and reading alarms.
Base URL & auth
All requests go to https://api.validanytime.com and carry your API key as a Bearer token.
curl https://api.validanytime.com/v1/monitors \
-H "Authorization: Bearer $VA_API_KEY"Endpoints
GET
/healthzLiveness check.
POST
/v1/monitorsCreate a monitor. The config is validated by constructing the real engine — an invalid config is a 422 here, never a surprise at first ingest. A template resolves server-side into the full config it runs; the resolved config is persisted and returned. Optional webhook_url turns on outbound alarm delivery (see below).
{ "name": "llm-answer-quality", "config": { "template": "llm_quality", "mu_baseline": 0.92 }, "webhook_url": "https://ops.example.com/hooks/validanytime" }GET
/v1/monitorsList every monitor for the tenant. Each config is the resolved config that actually runs (templates are already expanded).
GET
/v1/monitors/{id}Fetch a monitor.
PATCH
/v1/monitors/{id}Rename, reconfigure, and/or re-point the webhook. A config change resets the monitor's derived engine state (carry, learned baseline, fired-alarm latches — reported as state_reset: true) and applies to future events only. webhook_url is tri-state: omit to keep, set a URL, or pass null to clear — never a state reset.
{ "webhook_url": "https://ops.example.com/hooks/validanytime" }POST
/v1/monitors/{id}/eventsIngest one event. event_id makes it idempotent.
{ "value": 0.91, "event_id": "evt_1042", "ts": "2026-06-24T14:02:11Z", "label": 1.0 }POST
/v1/monitors/{id}/events:batchIngest a batch of events in order.
{ "events": [ { "value": 0.88, "event_id": "evt_1043" } ] }GET
/v1/monitors/{id}/eventsRead the event history (seq cursor: ?after_seq= & limit=). Each drained event also carries stats — the page tier's evidence at that step (log e-value for the coverage/risk e-processes, log Shiryaev–Roberts statistic for the e-detector), captured by the engine when the event was processed; null for events ingested before capture shipped.
GET
/v1/monitors/{id}/alarmsList alarms. Optional ?since= timestamp.
GET
/v1/fleet/fdrYour fleet's pooled certificate: online FDR (e-LOND) status across all e-value streams.
POST
/v1/onboarding/backtestReplay history through the gate (quiet on normal, fires on degradation). Omit config to run the default menu config, or pass an explicit one — this endpoint takes menu configs, not template ids.
{ "history": [0.92, 0.90, ...], "inject_shift": 4.0 }POST
/v1/onboarding/suggestSuggest a menu config from a sample and ratify it via the gate.
{ "sample": [0.9, 0.88, ...], "history": [0.92, 0.90, ...] }POST
/v1/monitors/{id}/ingest-tokensMint a least-privilege INBOUND ingest token (prefix vait_), scoped to this monitor, with a dot-path field map. The plaintext token + ingest path are returned once. Point any source at the URL — the token is the credential, no API key.
{ "value_path": "properties.score", "event_id_path": "event_id" }GET
/v1/monitors/{id}/ingest-tokensList a monitor's ingest tokens: id, prefix, mapping, created_at — never the secret.
DELETE
/v1/monitors/{id}/ingest-tokens/{tokenId}Revoke an ingest token — that URL stops ingesting immediately.
POST
/v1/ingest/{token}Inbound webhook receiver — NO Authorization header; the URL token is the credential. POST any source payload; the token's mapping pulls out value/event_id/ts (non-finite or missing value → 422). Metered and drained like an authenticated single ingest. This is the no-code ingest path (PostHog/Grafana/Zapier).
{ "properties": { "score": 0.91 }, "event_id": "evt_88" }GET
/v1/use-casesThe use-case → config matrix (public, no auth): each row's id, direction, when-to-use, rationale, and the resolved config it maps to.
GET
/v1/meThe signed-in caller's tenant and plan/status — the dashboard bootstrap call.
GET
/v1/keysList API keys: id, prefix, and created_at only — the secret is never returned.
POST
/v1/keysMint an API key. The plaintext key (va_...) is returned once and never stored or shown again — copy it immediately.
The monitor menu
A monitor config picks its detectors from a finite, documented menu of seven families across two tiers. Page-tier families carry an anytime-valid guarantee: their false-alarm control holds at every look at once, within a budget stated up front. Warning-tier families are the classical control charts (tagged heuristic_adaptive): sensitive and fast, but their false-warning calibration is model-based (iid standardized inputs) and is not anytime-valid — off-model, the false-warning rate can exceed the nominal rate by orders of magnitude, which is exactly why they warn rather than page.
coverage_e_process(page) — anytime-valid coverage monitor;alpha(defaults to the calibrator’s),delta(default 0.05): false-alarm probability ≤ delta, ever.e_detector(page) — Shiryaev–Roberts e-detector;arl_target(default 100),null_mean,input_field(default"miss"),detector(default"sr").risk_e_process(page) — anytime-valid risk-budget monitor;risk_budget(required),delta,risk_lo/risk_hi.cusum(warning) — self-standardizing CUSUM;k(default 0.5), exactly one ofhorarl_target(Siegmund-solved, a model-based promise),two_sided,train(default 30),input_field(default"loss").ewma_chart(warning) — EWMA chart with exact time-varying limits;lam(default 0.2),L(default 3),side,train,input_field(default"loss").static_threshold(warning) — mean ± k·sigma frozen from the firsttrainobservations;k(default 3),side,input_field(default"loss").rolling_band(warning) — trailing-window band that re-learns level as it goes;window(default 30),k(default 3),side,input_field(default"loss").
input_field selects what the detector watches: "miss" (the calibrated interval missed), "score" (the absolute residual), or "loss" — the signed residual value − baseline, which keeps direction, so a quality drop and a latency spike are different events.
Templates & the baseline
config: {"template": "llm_quality"} resolves server-side at create time into a complete two-tier config — an e_detector and a coverage_e_process on the budgeted page tier plus a directional control chart on the warning tier — and the resolved config is what gets persisted and returned, so GET /v1/monitors/{id} always shows exactly what runs. The ids (default, ml_drift, llm_quality, latency, kpi) map to distinct, gate-verified configs — they differ in calibrator stiffness and which direction the warn tier watches. See the use-case matrix for the full table, or GET /v1/use-cases for the resolved configs as JSON. template combines only with mu_baseline; anything else is a 422.
mu_baseline is the level your metric should hold — detectors measure deviation from it. Set it explicitly if you know it. Omit it and the monitor learns it at ingest: the median of your first 30 values, then frozen — frozen on purpose, so a slow regression is never re-learned as the new normal. (The very first event only seeds the learner; it is a no-op for every detector.) Every monitor response includes a read-only baseline summary — {"mode", "mu", "learning_count"} — showing the explicit value, the training progress, or the frozen learned value.
Validation
Configs are validated at create by constructing the actual engine: an unknown monitor family, an unknown top-level key, a malformed kwarg, or a bad template id returns 422 with the menu in the message — never a 500 at first ingest. Event values must be finite: an Infinity or NaN value is a 422 at ingest (a batch is rejected atomically, with the offending element named in the error), so a bad number in your pipeline can never corrupt a monitor’s stream.
Alarm delivery (webhooks)
A monitor with a webhook_url gets one POST per new alarm, sent after the alarm is persisted. Delivery is best-effort — a short timeout and one retry; a slow or down endpoint never blocks ingestion, and alarms stay readable via GET /v1/monitors/{id}/alarms either way. The tier field is the routing key: "page" alarms arrive within the monitor’s stated false-alarm budget; "warn" alarms are the sensitive classical tier and carry no budget.
{
"type": "alarm.fired",
"monitor_id": "mon_a1",
"monitor_name": "llm-answer-quality",
"alarm_id": "alm_2",
"seq": 78,
"tier": "page",
"guarantee_tag": "anytime_valid_under_conditional_mean_null",
"statistic": 3.01,
"e_value": 20.2,
"theorem_ref": "predictable_betting_e_process",
"message": "coverage_e_process fired at seq 78",
"fired_at": "2026-06-25T00:02:11Z",
"event_value": 0.87
}Example: alarms response
{
"alarms": [
{
"id": "alm_1",
"monitor_id": "mon_a1",
"seq": 68,
"fired_at": "2026-06-24T14:02:11Z",
"statistic": 8.30,
"guarantee_tag": "heuristic_adaptive",
"theorem_ref": "page_1954_cusum_siegmund_arl_calibration",
"e_value": null,
"discovery": null,
"message": "cusum fired at seq 68"
},
{
"id": "alm_2",
"monitor_id": "mon_a1",
"seq": 78,
"fired_at": "2026-06-25T00:02:11Z",
"statistic": 3.01,
"guarantee_tag": "anytime_valid_under_conditional_mean_null",
"theorem_ref": "predictable_betting_e_process",
"e_value": 20.2,
"discovery": true,
"message": "coverage_e_process fired at seq 78"
}
]
}Every alarm includes its seq (the 1-based event position at which it fired), its guarantee_tag — the guarantee in force — its theorem_ref, and a statistic. Warning-tier alarms carry guarantee_tag: "heuristic_adaptive" and always have e_value: null: a warning is a sensitive early hint whose calibration is model-based, not a budgeted page.
Fleet-wide FDR (e-LOND)
Alarms from e-process monitors also enter your tenant’s pooled e-LOND sequence: each alarm’s e_value is tested against a shared false-discovery budget, and the confirmed subset is marked discovery: true — controlling the false discovery rate at level α across all your streams, under arbitrary dependence between them. Detector-family alarms (e_detector) keep their own average-run-length guarantee and are not pooled, and warning-tier alarms never enter the pool at all — none of them emit e-values, so for both, e_value and discovery stay null.
{
"alpha": 0.1,
"hypotheses_tested": 7,
"discoveries": 2,
"gamma_remaining": 9993,
"guarantee_tag": "online_evalue_fdr_control",
"theorem_ref": "xu_ramdas_2024_e_lond_theorem_1"
}