Core concepts
Accounts
Everything belongs to an account (a tenant): signals, objects, webhooks, rules, API keys and the prepaid balance. API keys and console users each belong to exactly one account, and no request can reach another account's data.
Objects
An object is anything you want answers about: a user, a listing, an order, a conversation. You identify it with your own object_id (any string, such as user-42), and it exists as soon as you send its first event. There is no call to create one.
object_type (for example user or listing) is optional. It groups objects in the console's overview.
Events
Events are the evidence answers are computed from. Each has a type:
event: a discrete action, named withname, e.g.profile.updated,payout.requested,login.failed;message: free-form text incontent, such as a chat message or a review.
Either can carry metadata (any JSON object) and occurred_at (defaults to when the API received it). Events accumulate: every analysis looks at the object's whole history, not only the latest batch.
History and compaction
To keep analysis fast and bounded on long-lived objects, older events are folded into a rolling compacted summary (counts by action, sample messages, first seen) while the most recent events are kept verbatim. The analyzer gets both. You can inspect the summary with GET /v1/objects/{object_id}/state.
Signals
A signal is a question asked about every object: is this a scammer? It has a key (is_scammer), a type (noul, score or choice), instructions for the analyzer and type-specific criteria. Signals can be added, changed, disabled or removed at any time. See Signals.
Analysis
An analysis runs every enabled signal over one object's history and stores the latest answer for each.
- Debounced. Ingesting events schedules an analysis a few seconds later (the
analysis_delay_msin the response). Events that arrive in the meantime join the same analysis, so a burst of activity costs one analysis, not one per event. - Queued by priority. Live traffic (ingestion, re-analyzing one object) is served ahead of bulk work (re-analyzing everything, backfills), so a large backfill never delays real events.
- Latest answer wins. Each signal keeps its most recent answer, with the
modelthat produced it andcomputed_at. - Billed per analysis. Each analysis draws on the account's prepaid balance. See Billing and usage.
You can also force an analysis without new events: one object with POST /v1/objects/{object_id}/analyze, or every object with POST /v1/analyze.
Results
GET /v1/objects/{object_id} returns the latest answer per signal in analysis, and the signals that have no answer yet in pending (for example one added after the last analysis). Answers can also be pushed to you by webhooks and acted on by rules.
Idempotency and ordering
Ingestion is not idempotent: sending the same event twice records it twice. The SDKs therefore never retry POST requests automatically. The analyzer sees an object's events in occurred_at order, so set occurred_at when you send events some time after they happened.