Anomaly examples — worked API samples over the anomaly-insight ledger
The anomaly-insight ledger is the platform’s append-only record of what its own detectors have raised against your account traffic — spend spikes, toll-fraud blocks, SIM-swap flags, webhook-delivery anomalies. This page is the API-samples counterpart of Anomaly detection — reading usage and delivery anomalies, which walks the same data through the dashboard. Everything below is real: the three endpoints, the exact envelope shape, and the query parameters are the ones the platform serves today. All three endpoints are tenant-scoped GETs under/api/v1/insights and
return the standard Orbit envelope: data carries the payload, meta
carries the request id. Reads are idempotent and safe to poll; there is no
object to create and no decision to post from your side — triage
(acknowledge / dismiss / resolve) happens on the Insights → overview Fraud
alerts card, not over these endpoints.
Sample 1 — list anomalies by scope (the issues listing)
The by-scope endpoint answers “is anything wrong right now?” with one aggregate row per forensics scope (wallet, webhook). This is the
listing your poller should hit first, because it is the cheapest way to
decide whether a deeper page through /history is even needed.
scopesalways carrieswalletandwebhookaggregate rows plus the all-scopes total, regardless of yourscope=filter; the filter only narrows which rows fed the aggregate.open_countis the decision input — anything above zero means a detector raised something nobody has triaged yet.high_or_critical_counttells you the entry is a same-day item, not a queue-for-review one (severity rankslow → medium → high → critical).truncatedisnullon a healthy window. When a busy window runs into the bounded row scan, it carries{ "row_scan_bound": N }and you should page through/historyinstead of trusting the totals for that window — the platform surfaces the bound rather than silently undercounting.
days
(default 30, maximum 365), an explicit from/to ISO datetime pair (which
overrules days), and scope=wallet|webhook.
Sample 2 — the trendline (decision pass over day buckets)
The timeseries endpoint is the “blip or pattern?” pass. Day buckets are zero-filled across the window — a day with no detections returns0,
never a missing day — so a consumer can plot it without gap handling and a
poller can compare “today” against a clean baseline of zeros.
Sample 3 — the history rows (the full envelope split by webhook scope)
The history endpoint returns the raw ledger rows, newest first, paged at up to 100 entries per request. Filter it toscope=webhook and you get the
inbound-side family — webhook-delivery and phone-number anomalies — which
is exactly the webhook shape your receiver-side tooling cares about:
details carries only tenant-safe hints (an
endpoint id, a destination mask, a reason code) — provider-internal
payloads never survive the read path. channel is null on webhook-scope
rows and carries the spend channel (sms, whatsapp, email, rcs,
voice, agents, or all) on wallet-scope rows. The row status moves
open → acknowledged → resolved as your team works it on the Insights →
overview card, so polling /history also tells you what is already being
handled. The full triage workflow is covered in
Debug inbound carrier webhooks; this
page covers the anomaly ledger, not the per-webhook debug records, so
nothing here duplicates that guide.
The consistent 200 / 422 envelope
All three endpoints share one envelope contract:- 200 —
datacarries the payload shown above,metacarries therequest_id. An empty window is a valid answer: emptyitems, zeroed buckets, zeroedopen_count— an empty ledger is the healthy account state, not an error. - 422 — the same standard Orbit validation envelope every endpoint
returns on a malformed query (
daysabove 365, ascopeoutsidewallet|webhook, an unparseablefrom/to). Fix the parameter and re-send; nothing about the response shape changes.
Sample 4 — Node SDK (orbit.request) — the trigger-or-pass decision call
The Node SDK exposes the platform-genericorbit.request method, which carries any of these GETs with
the retry, idempotency, and timeout posture the SDK applies to everything.
The snippet below is the pass this page actually ships: poll the by-scope
aggregate, pass on a quiet window, and only page through /history when
something is genuinely open.
truncated before trusting the totals — that
is the one failure shape this API can otherwise hide, and the guard above
is the correct handling for it. orbit.request applies the SDK’s retry
posture on transient 5xxs, so a poller loop around anomalyDecisionPass
is safe to run on a fixed cadence.
See also
- Anomaly detection — reading usage and delivery anomalies — the dashboard walkthrough of the same ledger.
- Usage and delivery anomaly alert rules — author your own threshold rules on top of the automatic detectors.
- Debug inbound carrier webhooks — the per-webhook debug records, which the webhook-scope anomalies reference.
- Insights dashboards — the hub for every Insights surface.