Track events
Updated 2026-11-02
Send an event
Once the script loads, it adds a headstream global.
window.headstream?.track("signup", { plan: "starter" });
The first argument is the event name, up to 200 characters. The second is an optional object of properties. Use optional chaining, since the script loads with defer.
Events are batched. They're sent every 2 seconds, at 20 events, or when the visitor leaves.
Name events plainly
Goals and funnels match the exact event name, so settle on a convention. signup and Sign Up are two different events.
Properties are stored with the event and appear in your export. Goals and funnels use only the name. Keys that look like secrets, such as password and token, are removed, and card numbers are redacted.
Events sent for you
You don't need to send these:
| Type | Name | Notes |
|---|---|---|
pageview | Page loads and route changes | |
track | form_submit | With a form property holding the form's ID |
click | The element's selector | Used by heatmaps |
scroll | Scroll depth for each page |
Call it before the script loads
For a call that runs early, set a queue in an inline script placed above the Headstream tag:
<script>
window.headstream = { q: [["track", "pricing_seen", { plan: "growth" }]] };
</script>
The script runs each queued call once it loads, then replaces the global. After that, call window.headstream.track as usual.
Record a pageview by hand
The script handles the history API. If your app changes screens some other way, call:
window.headstream?.page();
A/B test variants
variant returns a stable variant for each visitor and records exposure, with no network call to pick one:
const variant = window.headstream?.variant("pricing-headline", ["control", "treatment"]);
The API and MCP server return the code to use when you create a test.
Use your events
Billing
Pageviews, custom events and identify calls count toward your plan. Bots, clicks, scroll, experiment and recording events don't. See pricing.