# Custom event properties

> Add typed custom event properties (strings, numbers, booleans and arrays) to events and pageviews, see how keys are normalized and query them with prop filters.

Properties are key-value pairs sent with an event or pageview:

```js
privatus.track('Download', { file_type: 'pdf', size_mb: 2.4, gated: false, tags: ['guide', 'pricing'] })
```

## Types

| Type | Example | Notes |
|---|---|---|
| String | `'pro'` | Up to 500 characters. Longer values are cut |
| Number | `49.99`, `3` | Must be finite. Sum, average, median and p90 are available in the Events explorer |
| Boolean | `true` | |
| Array of strings | `['a', 'b']` | Up to 30 items. Non-string items are dropped |

`null`, `undefined`, objects and nested arrays are dropped. Dates: send an
ISO 8601 string (`'2026-09-29'`).

## Keys

Keys are normalised before storage: lowercased, any character outside
`a-z 0-9 _` becomes `_`, repeated underscores are collapsed, and the key is
cut to 64 characters. `Plan Name` becomes `plan_name`, `userType` becomes
`usertype`. Use `snake_case` to avoid surprises.

## Reserved keys

`revenue` and `currency` are taken out of the properties and stored as the
event's [revenue](/docs/events/revenue).

## Properties on every hit

```js
privatus.props({ logged_in: true, theme: 'dark' })
```

adds properties to every later pageview and event on the page load. Or
point [`data-props`](/docs/tracker/attributes) at a function that returns
them.

## Privacy

The [PII scrubber](/docs/privacy/pii-scrubbing) runs on every string value:
emails, long ids, tokens and long digit runs are replaced with
`[redacted-…]`. It's a safety net, not a license: never send names, emails,
user ids or free text typed by visitors.

## Querying properties

Use `prop:<key>` as a dimension anywhere the API or dashboard accepts one:

```text
/sites/pa_7Q2K9XH3AB/breakdown/prop:plan.json?filters=[["event","is","Signup"]]
```

or as a filter: `["prop:plan", "is", "pro"]`. See [Filters](/docs/api/filters).
