> For the complete documentation index, see [llms.txt](https://docs.millimetric.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.millimetric.ai/reference/event-schema.md).

# Event schema

The single source of truth is [`packages/schema/src/index.ts`](https://github.com/) — the Zod schemas the API validates against and the SDKs export. This page is a human-readable mirror.

## TrackEventInput — `POST /v1/track` body

```ts
{
  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
}
```

| Field          | Required | Validation         | Notes                                                                |
| -------------- | -------- | ------------------ | -------------------------------------------------------------------- |
| `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`.                                            |
| `session_id`   | no       | 1–128 chars        | Auto-derived if omitted. See [Sessions](/core-concepts/sessions.md). |
| `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

```ts
{
  events: TrackEventInput[]   // 1–1000 events
}
```

## IdentifyInput — `POST /v1/identify` body

```ts
{
  anonymous_id: string,        // required, 1–128 chars
  user_id: string,             // required, 1–256 chars
  traits?: Record<string, unknown>
}
```

## ForgetInput — `POST /v1/forget` body

```ts
{ user_id: string }            // required, 1–256 chars
```

## ClassifiedSource — what the classifier returns

```ts
{
  source: string,                                  // "facebook", "google", "direct", ...
  medium: string,                                  // "paid", "organic", "social", "direct", ...
  campaign?: string,                               // utm_campaign
  confidence: "low" | "medium" | "high",
  rule_id: string                                  // which classifier rule fired
}
```

The full rule cascade is in [Attribution](/core-concepts/attribution.md).

## EventRow — what gets inserted into ClickHouse

```sql
project_id          UUID
timestamp           DateTime64(3)
event_id            String
event_name          String
anonymous_id        String
user_id             Nullable(String)
session_id          String

source              LowCardinality(String)
medium              LowCardinality(String)
campaign            Nullable(String)
source_confidence   LowCardinality(String)
source_rule_id      LowCardinality(String)

referrer            Nullable(String)
url                 Nullable(String)
path                Nullable(String)
country             LowCardinality(String)
device_type         LowCardinality(String)
browser             LowCardinality(String)
os                  LowCardinality(String)
ip_hash             FixedString(16)
properties          String              -- JSON-encoded
```

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

| Column    | Type                                                        |
| --------- | ----------------------------------------------------------- |
| `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](/core-concepts/sessions.md#the-sessions-materialised-view).

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

```json
{
  "error": "invalid_payload",
  "details": {
    "fieldErrors": {
      "event": ["Expected string, received undefined"],
      "url": ["Invalid url"]
    }
  }
}
```

## See also

* [Events](/core-concepts/events.md) — narrative version.
* [Properties](/core-concepts/properties.md) — what to put in `properties`.
* [Errors](/reference/errors.md) — all error codes in one table.
* [POST /v1/track](/api-reference/track.md) — the endpoint.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.millimetric.ai/reference/event-schema.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
