Event tracking recipes
Copy-paste event tracking for signups, checkout funnels, file downloads, outbound links, 404 pages, scroll milestones, video, A/B tests and user plans.
Signup#
On the page after a successful signup (or in your signup success handler):
privatus.track('Signup', { plan: 'free', method: 'email' })
Better still, send it from the server once the account exists, so blocked scripts and double submits don't matter. See server-side examples.
Then create an Event goal named Signup.
Checkout funnel#
privatus.track('Checkout started', { items: cart.items.length })
// …
privatus.track('Payment details entered')
// on the confirmation page:
privatus.track('Purchase', { revenue: order.total, currency: order.currency, items: order.items.length })
Build a funnel with the three events as steps. Never send order ids, emails or addresses.
File downloads#
Automatically, with the auto module (File Download events with a file
property):
<script defer src="https://privatusanalytics.com/js/pa.js" data-site="pa_YOURSITEID" data-modules="engage,auto"></script>
Or for a single link:
<a href="/files/price-list.pdf" data-privatus-event="Price list download">Price list (PDF)</a>
Outbound links#
The auto module sends Outbound Link with domain and url. Create an
Outbound click goal for a specific domain, e.g. your app's signup page
on another domain.
404 pages#
With the auto module, add this to your 404 template:
<meta name="privatus-404" content="true">
Then look at the 404 event's path property to find broken links, or
use the Not found tab of the Pages panel. Without auto:
<script>
window.addEventListener('load', function () {
privatus.track('404', { path: location.pathname });
});
</script>
Scroll milestones#
The engage module already reports scroll depth for every page. For an
event at a specific point (e.g. the end of an article):
const end = document.querySelector('#article-end')
new IntersectionObserver((entries, observer) => {
if (entries.some((e) => e.isIntersecting)) {
privatus.track('Article finished', { section: 'blog' })
observer.disconnect()
}
}).observe(end)
Or create an Engagement goal (scroll ≥ 75% on /blog/*) with no code.
Video#
HTML5 video:
document.querySelectorAll('video').forEach((video) => {
const name = video.dataset.name || video.currentSrc.split('/').pop()
video.addEventListener('play', () => privatus.track('Video play', { video: name }), { once: true })
video.addEventListener('ended', () => privatus.track('Video complete', { video: name }))
})
YouTube embeds need the YouTube IFrame API: listen to onStateChange and
track YT.PlayerState.PLAYING and ENDED the same way.
Experiments (A/B tests)#
Send the variant as a property on every hit, so every metric can be split by variant:
privatus.props({ experiment: 'pricing_2026_09', variant: variant })
with manual mode if the variant isn't known
until after load. Then break down any goal by prop:variant. The
Experiments report calculates conversion
rates per variant and their statistical significance.
Logged-in state or plan#
privatus.props({ logged_in: true, plan: 'business' })
Never send a user id: it would make visitors identifiable and defeat the cookieless design.