# 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 with `name`, e.g. `profile.updated`,
  `payout.requested`, `login.failed`;
- `message`: free-form text in `content`, 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](https://sigwise.ai/docs/guide/signals.md).

## 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_ms` in 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
  `model` that produced it and `computed_at`.
- **Billed per analysis.** Each analysis draws on the account's prepaid
  balance. See [Billing and usage](https://sigwise.ai/docs/guides/billing.md).

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](https://sigwise.ai/docs/guides/webhooks.md) and acted on by [rules](https://sigwise.ai/docs/guides/rules.md).

## 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.
