When the action does not land on a page of its own and is not simply a click — a form that validated, a step that completed, an upload that finished — call the function at the moment you know it succeeded.
window.truestat("signup");The goal appears on the dashboard the first time it fires.
Why this rather than a click
A click fires whether or not the thing worked. Calling the function yourself means you choose the moment:
const response = await fetch("/api/signup", { method: "POST", body });
if (response.ok) {
window.truestat("signup");
}Now the goal counts successful signups, not attempts. If you want both, send two goals with different names and the funnel will show the gap between them.
Adding parameters
window.truestat("initiate_checkout", {
plan: "growth",
traffic_step: "200k",
price: 59,
});The Goals card can then break the conversion down by each property — yearly against monthly, one plan against another.
Keep each parameter to one route. A number sent from JavaScript and the
same parameter sent as an HTML attribute arrive as 59 and "59", and group
as two different values.
Firing before the script has loaded
The script is loaded asynchronously, so a goal fired during page load can
arrive before window.truestat exists. Add the queue stub and nothing is lost:
<script>
window.truestat = window.truestat || function () {
(window.truestat.q = window.truestat.q || []).push(arguments);
};
</script>Calls made before the script arrives are replayed once it does. See custom events for the detail.
When your server has to send it instead
The browser can only report what it is present for. A renewal charged at 3am, a webhook from a payment provider, an overnight job — nobody is on the site, so use From your server.
Naming
Lowercase letters, numbers, and _, -, : only, up to 64 characters. See
naming rules.
Further detail
Custom events covers single-page apps, the parameter limits, the byte ceiling and the reserved names.