Skip to content

Custom events

A custom event is a named thing a visitor did. Pageviews are automatic; events are the parts of your product worth counting.

tracing("signup_completed");
tracing("signup_completed", { plan: "pro", seats: 5, trial: true });

The first argument is the name. The second is an optional flat object of properties.

The tracker replays anything queued on window.tracing.q when it boots, so a call that runs earlier than the script is not lost — as long as you create the stub yourself:

<script>
window.tracing = window.tracing || function () {
(window.tracing.q = window.tracing.q || []).push(arguments);
};
</script>
<script defer data-site="pk_live_xxxxxxxx" src="https://ingest.tracing.tools/t.js"></script>

Without the stub, tracing(...) before the script loads throws a ReferenceError. Guard with window.tracing?.("name") if you would rather not add one.

Any element with data-tracing-event fires that event when it — or anything inside it — is clicked. The listener is registered in the capture phase on document, so it works for elements added later.

<button data-tracing-event="cta_click">Start free</button>

Extra data-tracing-event-* attributes become properties. The suffix is camel-cased from the dataset key:

<button
data-tracing-event="cta_click"
data-tracing-event-position="hero"
data-tracing-event-variant="b">
Start free
</button>

sends:

{ "n": "cta_click", "p": { "position": "hero", "variant": "b" } }
Attribute Property key
data-tracing-event-position position
data-tracing-event-plan-name planName
data-tracing-event-id id

Values are always strings here — HTML attributes have no types. If you need a number the dashboard can average, send it from JavaScript instead.

Names are stored verbatim and truncated at 500 characters. Everything on the Events screen groups by the exact string, so pick one convention and keep it.

Do Avoid
signup_completed Signup Completed and signup_completed on different pages
checkout_started checkout started (v2)
One name, a step property checkout_step_1, checkout_step_2, checkout_step_3
tracing("plan_selected", {
plan: "pro", // string
seats: 5, // number
annual: true, // boolean
});
Rule Value
Properties per event first 50 are stored
Key length truncated to 200 characters
String value length truncated to 500 characters
Accepted types string, number, boolean
Booleans stored as the strings "true" / "false", typed boolean
Numbers stored in a numeric column, so they can be summed, averaged and bucketed
Nested objects / arrays not supported — flatten them yourself

Every property is written twice: once as JSON on the event itself (so the Sessions drawer and the occurrence feed can show it verbatim), and once as a flattened row keyed by name, so that “group by plan, where plan = pro” is a real query rather than a JSON scan.

On Events, selecting an event and opening Properties gives, per key:

  • Coverage — the percentage of that event’s occurrences in range that carried the key.
  • Values — distinct values seen, and the share each holds.
  • Numeric stats — for number keys: sum, average, min, max and median.
  • Histogram — equal-width bins for a number key with too many distinct values to list.

Only string values are clickable as filters. Number, boolean and date values are shown but cannot be filtered on — the dashboard says so in place.

Any screen accepts these as query parameters, and clicking a row sets them for you:

Parameter Meaning
event Keep only visitors who fired this named event
propKey Keep only events carrying this property key
propValue Narrow propKey to one value
/dashboard/<siteId>?range=7d&event=signup_completed&propKey=plan&propValue=pro

See Filters & dimensions.

Events are raw volume. A goal is an event you have named as a conversion, which unlocks conversion rate, per-source rates, time-to- convert and the goal filter.

From the Events screen, select an event and press Create goal — it opens the goal dialog prefilled through ?new=event:<name>.

Properties are stored as sent and are visible to everyone with dashboard access, including anyone holding a public share link if the data ever reaches a shared screen. Email addresses, names and user ids belong in tracing.identify(), where they are stored per visitor rather than per event — and even there, read the PII guidance first.