Custom Events
Pageviews, clicks and recordings describe what a visitor's browser did. Custom events describe what your product saw them do: "opened the configurator", "uploaded artwork", "saw the price". You send them from your own page with one call, and StorePilot stores each one against the visit it happened in.
StorePilot.track('configurator_opened', { entry: 'upload_step' });
StorePilot.track exists on every page that loads the StorePilot script, on the WordPress plugin and on a scriptless install alike. No plugin update is needed.
Calling it before the script has loaded
The StorePilot script loads asynchronously, so your code may run first. Push onto the queue instead, and StorePilot sends it once it starts:
window.StorePilot = window.StorePilot || {};
(window.StorePilot.q = window.StorePilot.q || []).push(['configurator_opened', { entry: 'upload_step' }, Date.now()]);
Each entry is [name, attributes, timestamp]. The timestamp is optional. Without it, the event is dated to the moment StorePilot read the queue.
Do not replace window.StorePilot with a new object. On WordPress the plugin writes its configuration there before your scripts run, and replacing the object deletes that configuration.
Names and attributes
| Rule | Limit |
|---|---|
| Event name | lowercase letters, digits and underscores, starting with a letter; at most 64 characters |
| Attributes per event | up to 10 |
| Attribute value | a string of up to 100 characters, a number, or true/false |
| Events per request | 50 (the script batches for you) |
An event name that breaks the rule is refused, never rewritten. Configurator Opened is not turned into configurator_opened, because two spellings you meant as two events would then silently merge. The browser console prints one warning for each refused name, which is where you will be looking when you write the call.
Attributes must be flat. Nested objects and arrays are dropped.
What is never stored
The attributes are written by your page, so StorePilot applies the same privacy floor it applies everywhere else:
- An attribute named like a credential or a contact field is refused whatever its value:
email,password,token,phone,first_name, a street address or a postcode. - An email address or telephone number inside a value is masked before storage.
"ring +1 555 123 4567"is stored as"ring [number]". - The page address goes through the same URL scrub as pageviews.
Send what happened, not who it happened to. The visit and the visitor are already attached to every event.
Consent
StorePilot.track follows the same consent rules as every other collector. Where your site requires consent, a call made before the visitor has agreed is dropped, not held back. That includes anything your page pushed onto window.StorePilot.q before consent was given. A visitor whose browser sends Global Privacy Control, and traffic identified as automated, are never recorded.
Which product an event belongs to
On a WordPress product page, each event is tied to the product your shop's server named when it rendered that page. The browser cannot claim a product, so a script on the page has no way to attribute events to a different one. On a scriptless site, events carry no product.
Where events appear
Every event is stored with its visit and counts towards the same monthly event allowance as clicks. Events are kept for 90 days, the same window as pageviews and clicks.
- Analytics ▸ Events lists each event with how many times it was sent, in how many visits, and the paid orders and revenue of those visits. A visit is counted once per event, and its order credited once, however many times the event was sent in it and however many products it was sent on. Pick an event to see the values of one of its attributes, or narrow the screen to one product.
- Conversion funnels accept an event as a step, optionally narrowed to one attribute value. See Funnels.
- Session replay marks each event on the timeline as a diamond, at the moment it happened in the visit.
- Recordings can be filtered to visits that sent an event, visits that did not, or both at once. For example, "opened the configurator" but not "added to cart" gives the visits most worth watching.
Every figure on these screens counts visits by people only. Automated traffic is left out, as on every other analytics screen. A visitor's events are included in their data export and removed with the rest of their data on erasure.
Events are separate from recordings. An event is stored whether or not the visit was recorded, so the counts cover every visit, and the replay only displays events that are already stored.
Events in an error's trail
Each event your page sends is also a step in the trail before an error: its name and up to three of its attributes, after the same privacy rules. An issue then reads "opened the configurator, uploaded artwork, then it broke" rather than a list of clicks on elements you never named. A name that was refused is not a step.
Two more calls sit beside StorePilot.track on the same script: StorePilot.captureException reports a failure your own code caught, with the same visit, trail and recording link as an error the browser reported, and StorePilot.identity() gives your page the visit's ids to send to your own backend. Both are described under Error Tracking.
Defining an event without code
An event can also be made from clicks StorePilot has already collected, with no change to your site. On Analytics ▸ Events, press Define an event, choose a page and an element on it, and give the event a name. On the Heatmap, each element in the ranking has a Make event button that does the same for that element.
From then on, every click on that element on that page is stored as the event, with the visit it happened in. Clicks collected before you defined it are counted too, within the 90 days clicks are kept; that history is filled in within about ten minutes, and the list of defined events shows how far back it has got. A defined event appears everywhere a sent one does: the Events page, funnel steps, the replay timeline and the recordings filter.
- Only clicks count. Repeated angry clicks and clicks that changed nothing are separate signals and are not counted as the event.
- One page and one element. The same element on a different page is not the same event.
- One name, one source. A name your site already sends with
StorePilot.track()cannot be defined, and once a name is defined,StorePilot.track()calls with that name are not stored. Otherwise two different things would be counted as one event. - Switching an event off stops counting clicks. Switched back on, it counts from that moment; the clicks in between are not counted.
- Removing an event deletes its events, and past days stop counting them. The clicks themselves stay, so defining it again counts them again.
Defining, switching and removing events needs the admin role. Everyone on the team can see them.
Elements of one list share a description, so an event defined on a menu link or a carousel card counts clicks on every item of that list. To count one element, name it with data-sp as described below.
Naming an element with data-sp
Every click is stored with a short description of the element: its tag, its id or first class, up to four levels up. Elements of one list share that description. Every link in a menu, every card in a carousel and every colour swatch look alike. On our own measurement, most clickable elements on a page share their description with at least one other element.
To give one element a name of its own, put data-sp on it:
<a href="/pricing" class="inline-flex px-4 py-2" data-sp="pricing-cta">See pricing</a>
In a Next.js or React component it is one property:
<Link href="/pricing" data-sp="pricing-cta">See pricing</Link>
On WordPress, add it in your theme's template, or as a custom attribute in the block or page builder that renders the button.
The click is then described as a[data-sp="pricing-cta"], and that element has its own row on the heatmap's element ranking. The value you write is the name. It is kept to letters, digits, - and _, up to 60 characters. It is never read from what a visitor types.
Only elements that carry the attribute change. Elements without it are described exactly as before, so the history of your heatmap stays as it was. A click recorded before you added the attribute keeps its old description.