User Guide
Custom Events
Track button clicks, form submissions, purchases — any action that matters to your business.
1. How events work
Every time the tracker fires an event it sends a small JSON payload to the Web Analyzer App ingestion API. Events are tied to the current session and visitor so you can see them in context on the Sessions and Visitors pages. They also appear aggregated on the Events dashboard page.
An event has:
- name — a short string you choose (e.g.
signup,purchase,video_play) - payload (optional) — a flat key/value object with extra data
2. Tracker JavaScript API
After the tracker script loads, a global Tracker object is available in window.Tracker. It exposes two methods:
| Method | Description |
|---|---|
| Tracker.track(name, payload?) | Fire a custom event. name is required (string). payload is an optional plain object. |
| Tracker.page(path?) | Manually send a page-view hit. Pass an optional path string; defaults to window.location.pathname. Useful in SPAs after a route change. |
Signature
Tracker.track(name: string, payload?: Record<string, any>): void
Tracker.page(path?: string): void
async. If you need to fire events before the tracker loads, use the pre-stub queue pattern below. Otherwise, use addEventListener('load', ...) or move your calls to DOM-ready handlers.
Pre-stub queue (fire events before tracker loads)
If you need to send events inline in a <script> tag before the tracker script has loaded, create a stub first. The tracker will process the queue once it initialises:
<script>
window.wa_key = 'YOUR_KEY';
window.wa_auto = true;
// Create stub so events can be queued before tracker loads
window.Tracker = { _q: [] };
</script>
<!-- Queue events immediately — they will be sent once the tracker initialises -->
<script>
Tracker._q.push(['purchase', { order_id: '1234', value: '49.99' }]);
</script>
<script src="https://webanalyzerapp.com/t.js" async defer></script>
3. Common examples
Button click
document.getElementById('signup-btn').addEventListener('click', function () {
Tracker.track('signup_click');
});
Form submission
document.getElementById('contact-form').addEventListener('submit', function () {
Tracker.track('form_submit', { form: 'contact' });
});
Purchase / checkout
// After a successful order
Tracker.track('purchase', {
order_id: '1234',
value: '49.99',
currency: 'USD',
plan: 'pro',
});
Video play
document.querySelector('video').addEventListener('play', function () {
Tracker.track('video_play', { title: this.dataset.title });
});
Outbound link
document.querySelectorAll('a[target="_blank"]').forEach(function (link) {
link.addEventListener('click', function () {
Tracker.track('outbound_link', { url: this.href });
});
});
4. Custom payload
The payload argument accepts any flat (non-nested) key/value object. Values can be strings, numbers, or booleans.
- Maximum 10 keys per event
- Key length: max 64 characters
- Value length: max 255 characters
- Nested objects are flattened or ignored
// Good
Tracker.track('upgrade', { plan: 'pro', value: '29', currency: 'USD' });
// Avoid nested objects — they will be stringified
Tracker.track('upgrade', { meta: { plan: 'pro' } }); // ✗
5. Single-page apps (SPA)
In a SPA (React, Vue, Svelte, etc.) the browser never does a full page reload, so the tracker's automatic page-view detection does not fire on route changes. Call Tracker.page() after each navigation:
React Router (v6)
import { useLocation } from 'react-router-dom';
import { useEffect } from 'react';
function Analytics() {
const location = useLocation();
useEffect(() => {
window.Tracker?.page(location.pathname);
}, [location]);
return null;
}
// Add <Analytics /> inside your <Router>
Vue Router
router.afterEach((to) => {
window.Tracker?.page(to.path);
});
Plain History API
const _pushState = history.pushState.bind(history);
history.pushState = function (...args) {
_pushState(...args);
window.Tracker?.page(location.pathname);
};
6. Server-side events (API)
You can also send events from your backend — for example after a successful payment webhook — using the tracker ingestion API directly.
POST /api/t/event
curl -X POST https://yourdomain.com/api/t/event \
-H "Content-Type: application/json" \
-d '{
"tracking_key": "YOUR_TRACKING_KEY",
"visitor_id": "VISITOR_UUID",
"session_id": 123,
"name": "purchase",
"payload": {
"order_id": "1234",
"value": "49.99"
}
}'
The visitor_id is the UUID stored in the visitor's localStorage under the key ot_vid. Pass it to your server on form submit or during checkout to correlate server-side events to the correct visitor record.
The session_id is the numeric session ID stored in sessionStorage under the key ot_sid. Both IDs are set by the tracker after the first page view.
7. Autotrack events
When window.wa_auto = true is set (enabled in the default snippet), the tracker automatically fires these events without any extra code:
| Event name | Trigger | Payload |
|---|---|---|
| error_page | Page title or body contains 404, 500, 403, 419, or 503 | { status: '404' } |
| site_search | URL contains ?q=, ?s=, ?search=, or ?query= |
{ query: 'user search term' } |
| outbound_link | Click on a link to an external domain | { url: 'https://...' } |
| file_download | Click on a link to a file (.pdf, .zip, .doc, .xls, .csv, etc.) | { url: '/file.pdf' } |
To customise site search parameters, add data-search-params="keyword,term" to the tracker script tag. To disable autotrack entirely, remove the window.wa_auto = true line from your snippet.
8. Bot & automation detection
The tracker automatically detects and silently skips automated browsers. No events or page views will be recorded when any of the following are detected:
- Selenium / ChromeDriver — detected via
navigator.webdriver - Playwright — detected via
navigator.webdriver - PhantomJS — detected via
window._phantomandwindow.callPhantom - Headless Chrome — detected via missing
navigator.languagesor missing browser plugins
9. Limits & quotas
Events count towards your monthly quota alongside page views.
| Plan | Monthly quota | At 80% | At 100% |
|---|---|---|---|
| Free | 1,000,000 | Warning in sidebar | Events rejected (402) |
| Pro | Unlimited | — | — |
Quotas reset on the 1st of each calendar month (UTC). Upgrade on the Billing page to increase your limits.