"}]},{"@type":"HowToStep","position":2,"name":"Fire a Signup goal when the form succeeds","text":"Two ways. Zero-JS: add data-admx-goal=\"Signup\" to your submit button — the pixel fires automatically on click and stitches to the next pageview. Programmatic: in your form-success handler, call window.admaxxer('Signup', { plan: 'free', source: 'organic' }). Either way, “Signup” appears as a goal in the dashboard within ~5 seconds.","itemListElement":[{"@type":"HowToDirection","text":"// Programmatic — preferred when you have rich metadata:\nfunction onSignupSuccess(user) {\n window.admaxxer('Signup', {\n plan: user.plan,\n source: getQueryParam('utm_source'),\n });\n}"}]},{"@type":"HowToStep","position":3,"name":"Identify the user post-signup","text":"After authenticating, call window.admaxxer.identify(userId, { email, plan }). This stitches the anonymous visitor to the named user — all downstream Stripe events tie back to the original session/source/medium.","itemListElement":[{"@type":"HowToDirection","text":"window.admaxxer.identify(user.id, {\n email: user.email,\n plan: user.plan,\n signed_up_at: user.createdAt,\n});"}]},{"@type":"HowToStep","position":4,"name":"Wire the Stripe webhook","text":"Connect Stripe in /integrations and add a webhook endpoint at https://admaxxer.com/api/pixel/webhooks/stripe/:website_id subscribed to: checkout.session.completed, payment_intent.succeeded, customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, customer.subscription.trial_will_end. Admaxxer fires “Trial Started”, “Trial Converted”, “Subscription Cancelled”, “Trial Ending Soon” goals automatically. See /documentation/install/stripe for restricted-key setup."},{"@type":"HowToStep","position":5,"name":"Pass admx_visitor_id to Stripe (optional but recommended)","text":"When creating a Stripe Checkout Session or Subscription server-side, pass the visitor ID as metadata so the conversion ties to the original anonymous session even if the user changes browsers or devices: { metadata: { admx_visitor_id: req.cookies.admx_visitor_id } } server-side, or window.admaxxer.getVisitorId() client-side. Without this, Admaxxer falls back to email-hash stitching (still works, slightly less accurate).","itemListElement":[{"@type":"HowToDirection","text":"// Client-side (Stripe.js):\nconst visitorId = window.admaxxer.getVisitorId();\nstripe.redirectToCheckout({\n sessionId: ...,\n // Pass at session-creation time on your server:\n});\n\n// Server-side (Node + Stripe SDK):\nconst session = await stripe.checkout.sessions.create({\n // ...your existing config\n metadata: { admx_visitor_id: req.cookies.admx_visitor_id || '' },\n});"}]},{"@type":"HowToStep","position":6,"name":"Verify the install","text":"Load any public page on your site in a fresh browser tab. Within a few seconds, the Admaxxer dashboard realtime view should show the event. If nothing lands after 2 minutes, re-check the snippet is actually in the rendered HTML <head> (View Source, not just DevTools)."}]}
Install guide · generic · ~8 min
Track signups → trials → MRR on your own SaaS site.
The end-to-end install for SaaS websites with signups, free trials, and recurring revenue. Five wiring points: (1) Pixel — paste script.plus.js in <head>. (2) Signup goal — fire when signup succeeds. (3) Identify — call window.admaxxer.identify() post-signup so anonymous visits stitch to the named account. (4) Stripe webhook — Admaxxer reads customer.subscription.* events and fires Trial Started / Trial Converted / Subscription Cancelled goals automatically. (5) Verify — place a $1 test trial and confirm all four events land in the dashboard.
Add script.plus.js (the Pro variant — auto-captures form submits, outbound clicks, file downloads, rage clicks) to your <head>. The Pro variant is recommended for SaaS because most signups are form-driven and the auto-form-submit goal saves you from wiring it manually.
<script defer
data-website-id="YOUR_WEBSITE_ID"
data-domain="yourdomain.com"
src="https://admaxxer.com/js/script.plus.js"></script>
Two ways. Zero-JS: add data-admx-goal="Signup" to your submit button — the pixel fires automatically on click and stitches to the next pageview. Programmatic: in your form-success handler, call window.admaxxer('Signup', { plan: 'free', source: 'organic' }). Either way, “Signup” appears as a goal in the dashboard within ~5 seconds.
// Programmatic — preferred when you have rich metadata:
function onSignupSuccess(user) {
window.admaxxer('Signup', {
plan: user.plan,
source: getQueryParam('utm_source'),
});
}
After authenticating, call window.admaxxer.identify(userId, { email, plan }). This stitches the anonymous visitor to the named user — all downstream Stripe events tie back to the original session/source/medium.
window.admaxxer.identify(user.id, {
email: user.email,
plan: user.plan,
signed_up_at: user.createdAt,
});
Connect Stripe in /integrations and add a webhook endpoint at https://admaxxer.com/api/pixel/webhooks/stripe/:website_id subscribed to: checkout.session.completed, payment_intent.succeeded, customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, customer.subscription.trial_will_end. Admaxxer fires “Trial Started”, “Trial Converted”, “Subscription Cancelled”, “Trial Ending Soon” goals automatically. See /documentation/install/stripe for restricted-key setup.
When creating a Stripe Checkout Session or Subscription server-side, pass the visitor ID as metadata so the conversion ties to the original anonymous session even if the user changes browsers or devices: { metadata: { admx_visitor_id: req.cookies.admx_visitor_id } } server-side, or window.admaxxer.getVisitorId() client-side. Without this, Admaxxer falls back to email-hash stitching (still works, slightly less accurate).
// Client-side (Stripe.js):
const visitorId = window.admaxxer.getVisitorId();
stripe.redirectToCheckout({
sessionId: ...,
// Pass at session-creation time on your server:
});
// Server-side (Node + Stripe SDK):
const session = await stripe.checkout.sessions.create({
// ...your existing config
metadata: { admx_visitor_id: req.cookies.admx_visitor_id || '' },
});
Load any public page on your site in a fresh browser tab. Within a few seconds, the Admaxxer dashboard realtime view should show the event. If nothing lands after 2 minutes, re-check the snippet is actually in the rendered HTML <head> (View Source, not just DevTools).
GET /api/pixel/healthcheck?website_id=YOUR_WEBSITE_ID — lastEventAt should be non-null and recent.customer.subscription.created (not just charge.succeeded). Trial starts move no money, so payment-only events miss them. In Stripe → Developers → Webhooks, click your Admaxxer endpoint and verify the event list includes all six recommended events.customer.subscription.updated with previous_attributes.status: 'trialing' and the new status: 'active' when a trial converts. Admaxxer reads both fields to detect the transition. If your trial never converts because the customer's card fails, you'll see customer.subscription.updated with status: 'past_due' instead — that fires no goal (intentional; we don't treat past_due as a conversion). Add a separate goal for failed-trial-conversion if you need that signal.POST /api/identify with status 200. If you see 4xx, your data-website-id doesn't match the Admaxxer site — verify it starts with admx_.initAdmaxxer() is SSR-safe (no-ops server-side). For server-side identify (e.g. when you create the user account in your API route), call POST /api/identify directly with the visitor cookie value forwarded — see API docs.subscription.items.data[0].price.unit_amount and recurring.interval + interval_count and normalizes to monthly. A $1200/year subscription contributes $100 MRR; a $99/month subscription contributes $99. See /documentation/revenue/stripe for the canonical formula.