データモデル
4 つのプリミティブがエンジンを構成します。他のすべてはその上に構築されます。
イベント
ユーザーが行った 1 つのこと。発生した瞬間に捕捉されます。イベントは、エンジンが状態に集約する生のシグナルです。
| 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.
ユーザー状態
ユーザーについて分かっているすべての、ライブで照会可能な投影(そのイベントから導出され、新しいものが到着した瞬間に更新されます)。これが、アナリティクス、フラグ、メッセージングが共有する信頼できる情報源です。
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.
意思決定
ユーザー状態に対して評価されるルール:誰がコホートに含まれるか、誰がフラグを受け取るか、誰がメッセージの対象になるか。意思決定はライブな状態を読み取るため、決して古くなりません。
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.
アクション
意思決定が発火したときにエンジンが行うこと:機能を公開する、メッセージを送る、ワークフローをトリガーする。
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.