A deferred script records page views, sources and campaigns. One call records the actions that matter, and your server reports the sales and the AI crawlers a browser cannot see.
Page views, sources and campaigns from a single deferred script, with single-page navigation followed for you.
Named actions
One call records an enquiry sent or a brochure downloaded, ready to become a conversion goal.
Server-side sales
Report completed purchases and refunds from your own server, joined to the visit that led to them.
AI crawler requests
Record the AI services reading your site from your server or edge, kept apart from human visits.
Quickstart
Add the website in WebTrack, copy its snippet from Settings, Installation, and paste it into the head of every page. Publish, open the site, and the visit shows up in Realtime.
Counting enquiries? If your form leads to a thank-you page, create a page goal for that page in WebTrack and you need no more code. If it sends without leaving the page, record a named action once your server accepts it.
The script tag
The script loads once per page, records the first page view, and does nothing else until the page changes or you record an action.
Attributes
srcrequired
The tracker, served from https://app.webtrack.co.za/tracker.js. Events are sent to the same origin the script came from.
data-websiterequired
Your website’s id, shown with its snippet in Settings, Installation. Without it the script does nothing.
defer
Recommended. The script runs after your page has been parsed, so it never holds up the first paint.
Content Security Policy
If your site sends a Content-Security-Policy header, add the WebTrack origin to the script-src and connect-src directives in its value, alongside the sources already there.
Events are accepted only from the website’s own domain and its www form. Any other subdomain needs a website of its own in WebTrack.
Conventions
Names
A letter, then up to 119 letters, digits or underscores: enquiry_submitted, brochure_downloaded. Anything else is ignored by the script and refused by the server.
Ids
UUIDs, in either case. They are stored in lower case.
Times
ISO 8601 in UTC. Reports use UTC days, so a day boundary is midnight UTC.
Money
Integer minor units with an explicit currency: 49500 with ZAR is R495.00.
Personal data
Never. No names, email addresses or account numbers in paths, names, campaign labels or sources.
Named actions
Call window.webtrack.track(name) when something worth counting has actually happened, after the server said yes rather than when the button was pressed. Optional chaining stops an error when the script has not loaded yet or is blocked, but the action is not recorded then, so make the call from a handler that runs after the page has loaded. It returns nothing.
Then create a goal for the same name in WebTrack. Matching actions already recorded are counted as soon as the goal exists.
Single-page sites
The script follows pushState, replaceState and popstate, so a React, Angular or Vue site counts each page as it is shown, with no router integration. A change to the query string or fragment alone is not a new page.
What the script sends
Each event carries the page path, the referring site, any utm_source and utm_campaign, the viewport width, the time and two random ids. Query strings and fragments are stripped before sending, and the script sets no cookies.
Visits
The ids live in session storage for one tab and are replaced after 30 minutes without activity. A new tab or device is a new visitor.
Location
WebTrack looks up an approximate country and city from the IP address an event arrives from, then discards the address without storing it. A private or unrecognised address is recorded as Unknown.
Opting out
With Do Not Track or Global Privacy Control on, the script records nothing at all.
Bots
Browsers that identify themselves as bots or crawlers are not counted as visitors.
Server keys
Server events are authenticated with a key that belongs to one website. Create it in Settings, Installation; it is shown once, so store it in your server’s secrets straight away. Creating a new key revokes the previous one immediately.
A server key can write revenue to your reports. Keep it on your server, never in a web page or a mobile app, and create a new one if it is ever exposed.
Server events
POST /api/server-events
Record an event from your server
Records one event for one website: a purchase, a refund, a named action your server saw, or an AI crawler request. Send each event once; a retry with the same id and the same body is accepted again without counting twice. Server events never add visitors or sessions, and carry no location, because they come from your server rather than the visitor’s browser.
AuthServer key
Body
iduuidrequired
Your own unique id for this event. Keep it when you retry. The same id with a different body is refused.
websiteIduuidrequired
The website the key belongs to.
visitoruuidrequired
The anonymous visitor id from window.webtrack.session(), or a random UUID when your checkout has none. With a random id the sale still counts as revenue, but it joins no visit and adds no visitor.
sessionuuidrequired
The anonymous session id from window.webtrack.session(), or a random UUID when your checkout has none, as with the visitor id.
atdatetimerequired
When it happened, as an ISO 8601 UTC timestamp. Accepted from 90 days ago to one minute ahead.
namestringrequired
A letter, then up to 119 letters, digits or underscores, such as purchase_completed. Use crawler_request for an AI crawler.
pathstringrequired
The page the event belongs to, starting with /, up to 500 characters.
revenueinteger
The amount in minor units: 49500 is R495.00. Negative for a refund. Zero for anything that is not money. Default 0
currencystring
ZAR, USD, EUR, GBP, AUD or CAD. Required whenever revenue is not zero; an amount is never assumed to be in a default currency.
sourcestring
Where the visit came from, if your server knows, up to 120 characters. Default Server
providerstring
Crawler requests only, and required for them: the AI company, such as OpenAI.
purposestring
Crawler requests only, and required for them: fetch, indexing, training or general.
Returns202 { "ok": true }
Purchases and refunds
Send a purchase once your payment provider has confirmed it and you have checked its signature and amount. Pass the ids from window.webtrack.session() to your checkout, so the sale joins the visit that led to it.
Your checkout will not always have them. The tracker is absent when the browser sends Do Not Track or Global Privacy Control, or when the script is blocked, so send a random UUID for each id your checkout did not receive. The sale is still recorded as revenue, but it joins no visit and adds no visitor.
Set at to the time of the refund in UTC, such as new Date().toISOString() in JavaScript. It must fall within the accepted window, from 90 days ago to one minute ahead, or the event is refused.
AI crawler requests
Most crawlers never run a page’s scripts, so only your server or hosting edge sees them. When a request comes from an AI service, send a crawler_request with the provider and why it came. Crawler requests carry no revenue and never count as visitors.
A failed request returns JSON with a message to show or log, rate limits included. A validation failure adds issues, one per field.
JSON
{
"message": "Check the submitted fields.",
"issues": [{ "path": "currency", "message": "A currency is required with revenue." }]
}
Check the submitted fields.400
A field is missing or invalid, and issues names each one. A body sent without a JSON Content-Type is not read at all, so every required field is reported missing. Correct the fields named in issues. If every required field is named, send Content-Type: application/json.
The request body is not valid JSON.400
The body was sent as JSON but could not be parsed. Check the body is valid JSON, with double-quoted keys and no trailing commas.
Event timestamp is outside the accepted window.400
at is more than a minute ahead, or older than the window for this kind of event. Send the real time of the event, in UTC.
A valid server key is required.401
The Authorization header is missing, or the key is not in the right form. Send Authorization: Bearer followed by the key, exactly as it was shown.
This server key is invalid or revoked.401
The key is not one WebTrack knows, or it was replaced when a new key was created for the website. Create a new key in Settings, Installation and update your server.
This key cannot write to that website.403
websiteId is not the website the key was created for. Use the website’s own key, or correct websiteId.
This organisation is suspended.403
The organisation that owns the website is suspended, so its events are refused. Contact SyncFac to restore the organisation, then send the events again.
Website domain does not match.403
Browser collection only: the page is not on the website’s domain or its www form. Use the snippet on the website it was made for. Any other subdomain needs a website of its own.
Website not found.404
Browser collection: data-website names no website WebTrack knows, or one since deleted. Copy the snippet again from Settings, Installation.
This event identifier already belongs to a different event.409
The id was already used with a different body. Keep the body identical when you retry, and use a new id for a new event.
Event payload is too large.413
The body is over 8 KB. Send one event per request, with only the documented fields.
Too many events for this key. Try again shortly.429
The server key sent more than 600 events in a minute. Wait for the seconds in Retry-After, then retry with the same id and body.
Too many rejected requests. Try again shortly.429
More than 60 requests from your address were refused within a minute. Fix the cause of the earlier errors, then wait for the seconds in Retry-After.
Too many events. Try again shortly.429
Browser collection only: one address sent more than 240 events in a minute. The script does not retry. Check the page is not calling track() in a loop.
Rate limits
A limited request returns 429 with a JSON message and a Retry-After header giving the seconds to wait. The browser and per-key limits also send RateLimit and RateLimit-Policy headers with the allowance that is left. Wait rather than retrying immediately, and keep the same id and body when you do.
Browser collection
240 events a minuteKeyed by Visitor address
Server events
600 events a minuteKeyed by Server key
Rejected server requests
60 a minute, then a short pauseKeyed by Sending address
Questions
In WebTrack, under Settings, Installation, for the website you chose. The snippet there already carries the id. A server key is shown once, when you create it.
The script sets no cookies and keeps its anonymous visit id in session storage, which is gone when the tab closes. Whether a site needs a banner depends on everything else it runs, so check with whoever advises it on privacy.
Your browser may be sending Do Not Track or Global Privacy Control, in which case the script records nothing. Automated browsers that announce themselves as bots are not counted either.
Not yet. Send one event per request; the limit is 600 events a minute per key.
The website’s domain and its www form are one website. Any other subdomain is a website of its own, with its own snippet.
For developers
Stuckonaninstall?Askthepeoplewhobuiltit.
Sign in for your snippet and server key, or send us a question and a developer will reply.