Web Analyzer App
/ Docs

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
Script load timing: The tracker is loaded with 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._phantom and window.callPhantom
  • Headless Chrome — detected via missing navigator.languages or missing browser plugins
Testing tip: If you're testing your tracker integration with Selenium, Playwright, or Cypress, events will not appear in your dashboard. Use a regular browser window to verify tracking is working.

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.

Help & FAQ

Find answers instantly