Events & properties

The anatomy of an OakData event: system vs custom events, the properties attached to every event, and context.

Everything OakData records is an event: a named action with a bag of properties, stamped with who did it, when, and in which session. Events come from two places — the SDK's autocapture, and your own oak.capture() calls.

Anatomy of an event

Each event the SDK sends looks roughly like this. The fields outside properties identify the actor and session; everything specific to the event lives inside properties.

event.json
json
{
  "id": "evt_5f2c…",
  "event": "signup_completed",
  "timestamp": "2026-06-11T14:03:21.881Z",
  "distinct_id": "user_8f3a",
  "anonymous_id": "a3c1…",
  "session_id": "s_a1b2c3",
  "session_number": 2,
  "session_started_at": "2026-06-11T13:58:10.020Z",
  "properties": { "plan": "pro", "$pathname": "/welcome" },
  "context": {
    "library": "oakdata-js",
    "library_version": "0.4.0",
    "page": { "url": "...", "path": "/welcome", "title": "Welcome", "referrer": "" }
  },
  "traits": { "email": "sam@acme.com" },
  "groups": { "company": { "id": "acme-co", "traits": { "name": "Acme" } } }
}

System vs custom events

Events whose names begin with $ are system events emitted by the SDK — $pageview, $click, $form_submit, $identify, $set, $group, and so on. Your own events should use plain names (signup_completed, invoice_paid). The same $ convention applies to system properties the SDK attaches automatically.

Naming convention

Use snake_case, past-tense, object-then-action names (checkout_completed, file_uploaded). Keep high-cardinality values (ids, emails) in properties, not in the event name.

Properties attached to every event

The SDK enriches each event with context the server stores as first-class columns — so you can filter and group without parsing JSON. Common keys:

PropertyTypeDescription
$current_url / $pathname / $host / $searchstringThe page URL and its parts.
$titlestringThe document title.
$referrer / referring_domainstringWhere the visitor arrived from.
browser / browser_version / os / os_versionstringParsed from the user agent.
device_typestringdesktop, mobile, or tablet.
viewport_width / viewport_heightnumberBrowser viewport size.
utm_source / utm_medium / utm_campaign / utm_term / utm_contentstringCampaign attribution, captured from the landing URL and carried via first/last touch.

Geo fields (country, region, city) are derived server-side from the request IP at ingest — the SDK never sends them.

Custom properties

Pass any JSON-serializable object as the second argument to capture. Keep property names stable and values low- cardinality where you intend to group by them.

custom
ts
oak.capture('plan_changed', {
  from: 'free',
  to: 'pro',
  annual: true,
  seats: 5,
})

Scrubbing sensitive data

Strip fields before they leave the browser with the property_denylist init option, or rewrite/drop whole events with before_send. See the SDK reference.