# 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. A number sent as a string (`'3'`) is a string and is left out of those |
| Boolean | `true` | |
| Array of strings | `['a', 'b']` | Up to 30 items of up to 500 characters. Non-string items are dropped |

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

An event keeps its first 30 properties. See [Limits](/docs/events#limits).

## Keys

Keys are normalized before storage: lowercased, any character outside
`a-z 0-9 _` becomes `_`, repeated underscores are collapsed, underscores at
the start and end are removed, and the key is cut to 64 characters.
`Plan Name` becomes `plan_name`, `userType` becomes `usertype`. A key with
nothing left (`'---'`) is dropped. 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
set [`data-props`](/docs/tracker/attributes) to the name of a global
function that returns them. It is called for every hit, and properties
passed to `privatus.track()` win over it.

Properties on pageviews are stored, and the
[Experiments report](/docs/features/experiments) reads them. `prop:<key>`
breakdowns and filters look at custom events only.

## Privacy

The [PII scrubber](/docs/privacy/pii-scrubbing) (on by default for every
site) runs on string values, including the strings in a list: emails, UUIDs, long hex ids, tokens and long
digit runs are replaced with placeholders such as `[redacted-email]`.
Property keys and event names are not scrubbed.
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>` (the normalized 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"]`, which keeps the visits with
a custom event that carries that value. Number properties also take `gt`,
`lt` and `between`. See [Filters](/docs/api/filters).
