Modèle de données
Quatre primitives forment le moteur. Tout le reste est construit par-dessus.
Événement
Une chose qu'un utilisateur a faite, capturée au moment où elle se produit. Les événements sont le signal brut que le moteur replie en état.
| Field | What it carries |
|---|---|
| distinct_id | Who did it: the identifier your code knows the user by |
| event_name | What happened, as a lowercase dotted name (order.completed); a leading $ marks a name the platform owns |
| properties | Anything else about the moment, as a JSON object; three reserved keys mutate user state (below) |
| timestamp | When it happened, stamped by the sender |
| session_id | The visit it belongs to: the web SDK mints one and rotates it after thirty minutes idle or twenty-four hours; replay shares it |
| context | Where it came from: locale, OS, SDK and SDK version |
Events are immutable: nothing edits one after the fact, so the history stays trustworthy and everything current-state lives in user state instead. The exact wire rules, bounds and error codes are on the Ingest API page; the console browses the stream on Events.
État utilisateur
Une projection vivante et interrogeable de tout ce que l'on sait d'un utilisateur (dérivée de ses événements, mise à jour dès qu'un nouveau arrive). C'est la source de vérité que partagent l'analytique, les flags et la messagerie.
The profile is cumulative, one per person, and any event may mutate it through three reserved property keys:
| Key | Effect on the profile |
|---|---|
| $set | Overwrites each named property with the new value |
| $set_once | Sets each named property only where none exists yet |
| $unset | Removes the listed properties |
{
"event_name": "plan.upgraded",
"distinct_id": "u_8f3a",
"properties": {
"$set": { "$email": "ada@example.com", "plan": "scale" },
"$set_once": { "first_seen_source": "organic" }
}
}Keys with a $ prefix are the platform’s: $email, $name, $avatar and $phone are the reserved traits the console renders as a person’s fields, and everything unprefixed is yours, rendered as a table. The distinction is deliberate: an unprefixed email stays a custom property and is never promoted to the person’s address, because your email may mean something else entirely. A malformed mutation is dropped and reported; the event itself is kept, because it genuinely happened.
Identity
Every event names a distinct_id, and a person may accumulate several: the anonymous id a browser minted before sign-up, the user id your backend knows. $identify (sent by SDKs, or POST /v1/identify) declares two ids the same person; the engine merges them to one canonical person, folds their profiles together, and every id keeps working. $group associates a person with a group and its traits, so state can be carried by an account as well as an individual. The merged people are what People shows and cohorts select over.
Décision
Une règle évaluée sur l'état utilisateur : qui est dans une cohorte, qui obtient un flag, qui est éligible à un message. Comme les décisions lisent l'état en direct, elles ne sont jamais obsolètes.
Concretely: a flag rule, a cohort definition and a message audience are all predicates over the same live profile, which is why a person who upgrades is in the new cohort, the new rollout and the new audience the moment the $set lands, with no sync between three products.
Action
Ce que fait le moteur lorsqu'une décision se déclenche : exposer une fonctionnalité, envoyer un message, déclencher un workflow.
Actions close the loop: a message lands, a workflow runs, a feature turns on, and each of those produces events of its own for the next decision to read.