Event schema
Full Zod schemas, ClickHouse columns, and what's required vs optional.
The single source of truth is packages/schema/src/index.ts — the Zod schemas the API validates against and the SDKs export. This page is a human-readable mirror.
TrackEventInput — POST /v1/track body
{
event: string, // required, 1–128 chars
event_id?: string, // 1–128 chars
timestamp?: string, // ISO 8601
anonymous_id?: string, // 1–128 chars
user_id?: string, // 1–256 chars
session_id?: string, // 1–128 chars
url?: string, // valid URL, ≤ 2048 chars
path?: string, // ≤ 2048 chars
referrer?: string, // ≤ 2048 chars
properties?: Record<string, unknown> // JSON-stringified ≤ 8 KB
}event
yes
1–128 chars
Use snake_case. $-prefix reserved for system events.
event_id
no
1–128 chars
Idempotency / join key. No server-side dedup yet.
timestamp
no
ISO 8601
Server clock if omitted.
anonymous_id
no
1–128 chars
Caller-supplied UUID. Server fabricates if omitted.
user_id
no
1–256 chars
Set after /v1/identify.
url
no
URL, ≤ 2048
Classifier reads this.
path
no
≤ 2048
Browser SDK fills this.
referrer
no
≤ 2048
Classifier reads this.
properties
no
≤ 8 KB stringified
Free-form JSON.
BatchInput — POST /v1/batch body
IdentifyInput — POST /v1/identify body
ForgetInput — POST /v1/forget body
ClassifiedSource — what the classifier returns
The full rule cascade is in Attribution.
EventRow — what gets inserted into ClickHouse
Partitioned by toYYYYMM(timestamp), ordered by (project_id, timestamp, event_id).
Materialised views
daily_rollup
Aggregates by (project_id, day, event_name, source, medium, country, device_type). Powers /v1/stats.
events
SimpleAggregateFunction(sum, UInt64)
uniques
AggregateFunction(uniq, String) (HLL over anonymous_id)
sessions
One row per (project_id, session_id, anonymous_id). Powers /v1/sources and revenue attribution. Schema in Sessions.
What's not in the schema
Email, name, address. Don't send PII.
Raw IP. Sent in the request, never persisted — we store
HMAC(ip, IP_SALT || UTC_date)only.City-level geo. Country only. We use Cloudflare's
cf-ipcountry.Cookies. None set by us.
Validation errors
A failed Zod parse returns 400 invalid_payload with the flattened error tree. Example:
See also
Events — narrative version.
Properties — what to put in
properties.Errors — all error codes in one table.
POST /v1/track — the endpoint.
Last updated
Was this helpful?