Set up website tracking
Chartsy's tracking script connects the traffic arriving at your site to the customers who end up paying - so you can see which channel brought each signup, which ones convert, and which ones produce customers who stay.
Three steps are required. Two more sections apply only if your site spans several domains or shows a cookie banner. Platform-specific instructions - Webflow, Squarespace, Wix, WordPress, GTM, Framer - are in Install by platform.
What you need first#
- A Chartsy account with your site added under Settings → Domains.
- Access to your site's HTML - a shared layout file, your CMS's header-injection setting, or Google Tag Manager.
- A connected payment provider (Stripe or Paddle) if you want revenue by source. You can install tracking first and connect billing later.
Tracking is disabled on localhost by default, so pointing a real key at your dev environment will not burn your monthly event allowance.
1. Add the tracking script (required)#
Copy your snippet from Settings → Domains, or from the setup block on the Launches page. It carries your own site key:
<script async src="https://chartsy.fra1.digitaloceanspaces.com/production/static/track.js"
data-key="ch_pk_your_key_here"
data-api="https://dashboard.chartsy.app/api/attribution"></script>It must render in <head> on every page. Where to put it:
| Your setup | Where it goes |
|---|---|
| Framework with a shared root layout | app/layout.tsx, layouts/default.vue, +layout.svelte or your base template - once |
| WordPress, Webflow, Squarespace, Wix, Framer | The site-wide custom code / header injection setting |
| Google Tag Manager already installed | A Custom HTML tag firing on All Pages |
| Static multi-page HTML | Before </head> on every page |
Add it once per page load. Two copies double-count everything.
Script attributes
| Attribute | Required | What it does |
|---|---|---|
data-key | Yes | Your site's public key. Safe to expose - it is in your page source. |
data-api | Yes | Where events are sent. Use the value from your snippet. |
data-allow-localhost | No | Set to "true" to track on localhost while developing. Off by default. |
data-consent | No | Rarely needed. Duplicates the "Ask everyone first" cookie mode - use the setting instead. |
Check it worked
Load your site, then open Settings → Domains and click View setup checklist on the site's row. "Add tracking snippet" flips to done as soon as the first visit lands. That item is inferred from real traffic, so it is the only confirmation the snippet is actually firing rather than just pasted somewhere.
2. Tell Chartsy who signed up (required)#
Visits alone cannot be tied to revenue. Chartsy needs the email address at the moment someone creates an account. Pick whichever route matches your signup flow.
A real form
Add data-chartsy-signup to the <form>. The email field is picked up on submit - no JavaScript to write.
<form action="/signup" method="post" data-chartsy-signup>
<input type="email" name="email" required>
<button type="submit">Create account</button>
</form>JavaScript or single-page apps
Call identify yourself, right after the signup succeeds:
window.chartsy.identify({ email: 'person@example.com' });OAuth and social login
"Continue with Google" redirects the browser away and back, so there is no page left running to fire identify(). Handle it from your backend, right after the callback creates the user. The cookies the script already wrote survive the round trip:
// Node / Express - from your OAuth callback handler
const first = JSON.parse(decodeURIComponent(req.cookies.chartsy_attrib || '{}'));
const last = JSON.parse(decodeURIComponent(req.cookies.chartsy_attrib_last || '{}')) || first;
await fetch('https://dashboard.chartsy.app/api/attribution/identify/', {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
'Authorization': `Bearer ${process.env.CHARTSY_SECRET_KEY}`,
},
body: JSON.stringify({
email: user.email,
attribution_first: first,
attribution_last: last,
visitor_id: req.cookies.chartsy_visitor_id || '',
}),
});Keep the secret key server-side
Environment variable or secrets manager, never committed and never sent to the browser. Unlike the public key, it authenticates your backend.
Send the request even when the cookies are missing. A signup with no known source still counts; one that never arrives is invisible.
3. Connect revenue to sources (required for revenue)#
Chartsy matches signups to paying customers by email. That breaks when someone pays with a different address than they signed up with - a personal signup and a company billing address, for example.
Attaching the visitor id at checkout fixes it permanently:
// Stripe Checkout Session or PaymentIntent
metadata: { chartsy_visitor_id: req.cookies.chartsy_visitor_id }
// Paddle transaction or checkout
custom_data: { chartsy_visitor_id: req.cookies.chartsy_visitor_id }Revenue and churn then follow the visitor rather than the address, and keep working when the two diverge.
If your site spans more than one domain#
Install the same snippet, with the same key, on every domain - and register each of them under Settings → Domains. Both halves matter: the script has to be present to measure anything, and the domain has to be registered for Chartsy to recognise it as yours rather than as an outside referral.
Subdomains - example.com and app.example.com - need nothing else. Registering the root covers every subdomain, and the two share a cookie automatically.
Separate root domains - example.com and exampleapp.io - cannot share a cookie at all; browsers keep them in separate jars. Chartsy handles the handover: when a visitor clicks from one of your registered domains to another, their identifier travels on the link as a _chid parameter, and it is removed from the address bar as soon as it is read. A signup on the second domain is attributed to the campaign that landed on the first, and the whole crossing counts as one visit rather than two.
It follows real links
The handover attaches to <a href> clicks. A button that navigates with window.location = … or a router push will not carry it; framework link components such as Next.js <Link> render real anchors and work fine. Pages viewed before crossing stay with the domain they were viewed on - attribution crosses, the page-by-page journey does not.
If your site has a cookie banner#
Never put the tracking script behind your banner's consent check
And never remove it when consent is refused. This is the one setup mistake that looks correct and silently collects nothing.
The script is what enforces the consent decision. It asks Chartsy, per visitor, whether that particular person may be given cookies - decided from their country composed with your cookie mode - and goes cookieless on its own when the answer is no. A script that was never loaded cannot do any of that. It just stops measuring, which shows up as "no traffic" rather than "declined", and it leaves any cookies written before the refusal sitting on the device.
Load it on every page unconditionally - the same tag as step 1, unchanged. A banner never changes the tag.
Whether this site needs an opt-in before cookies is not a property of the tag either. It is the site's cookie mode, under Settings → Domains → Cookies & consent. Pick Ask everyone first if your banner should gate cookies for every visitor, or leave it on Cookies where permitted to let each visitor's country decide.
Then wire your banner's buttons:
onAccept: window.chartsy.consent('granted'); // cookies on
onWithdraw: window.chartsy.consent('denied'); // cookies off, still counted
onReject: window.chartsy.optOut(); // stop tracking entirely| Call | What happens |
|---|---|
consent('granted') | Full cookie attribution: 90-day first touch, multi-day journeys, revenue matched by visitor id. |
consent('denied') | No cookies, but the visitor is still counted. Same-day attribution and signup matching still work; first touch and multi-day journeys do not. The right state for someone who never answered the banner. |
optOut() | Everything stops on that browser for a year, and anything already stored is cleared. Use it for a Reject button. optOut(false) reverses it. |
No cookie banner at all? Install the plain snippet from step 1 and stop. Chartsy already withholds cookies from EEA and UK visitors by default, so those visitors stay cookieless and you owe no banner for the tracking itself.
What about data-consent="required"? There is such an attribute, and you almost certainly do not need it. It duplicates the Ask everyone first cookie mode, and nothing is stored before the policy answer arrives either way. Reach for it only to pin the stricter behaviour in your own source code, where a dashboard setting could be changed without you noticing. Never set it alongside a cookie mode that says otherwise - the attribute wins, so your dashboard would report Cookies where permitted while the site actually asked everybody, and every non-EEA visitor would go cookieless for nothing.
Cookie modes#
Set per site under Settings → Domains → Cookies & consent. Every mode measures every visitor; what cookies add is cross-session identity - who came back, and what first brought them.
| Mode | Behaviour | Cost |
|---|---|---|
| Cookies where permitted (default) | Visitors whose country allows first-party analytics cookies get full 90-day attribution. EEA and UK visitors are measured cookielessly, automatically. | None. Best accuracy. |
| Ask everyone first | Nothing is stored for anyone until your banner grants it. Needs the wiring above. | Visitors who do not answer stay cookieless. |
| Never use cookies | No device storage for anyone, so no banner is needed at all. | Same-day attribution only - no first touch, no multi-day journeys. |
Switching to Never also clears cookies already on returning visitors' devices, the next time they load a page.
Excluding yourself#
Visit any tracked page once with ?chartsy_ignore=1:
https://yoursite.com/?chartsy_ignore=1That browser is never tracked again - no cookies, no events, nothing counted against your plan. Use ?chartsy_ignore=0 to undo it. Do this on each browser and device you use to check your own site. Traffic from private and internal IP addresses is excluded automatically.
Troubleshooting#
Every signup says "Unknown source"
Almost always the script is missing from the page where visitors first arrive. If your marketing site and app are on different subdomains, the script has to be on both - the crossing between them is internal navigation, so if the marketing side never recorded the original visit, there is nothing left to attribute. Check the banner too: if the script is gated behind consent, it never runs for the majority of people, who never answer.
No data at all
Check, in order:
- The domain is registered under Settings → Domains
- The script renders in
<head>on the page you are testing - You are not on
localhostwithoutdata-allow-localhost="true" - You have not opted this browser out
- No ad blocker or content blocker is active
It worked, then stopped after a site change
Most often a JavaScript optimisation feature - "combine scripts", "defer JavaScript", "minify and inline" - in a caching or speed plugin, or a build step that rewrites script tags. Chartsy reads its settings off its own <script> tag, so anything that strips its attributes or inlines it without them leaves it running with no key, measuring nothing. Exclude track.js from combining, deferring and inlining.
Visits but no signups
Step 2 is not wired, or your signup goes through OAuth and needs the server-side call.
Signups but no revenue
Link a payment account to the site under Settings → Domains, and check step 3 - without the metadata, matching falls back to email, which fails when billing and signup addresses differ.
Fewer visits than another analytics tool
Expected. Chartsy counts one visit per session per source, not one per pageview, and excludes bots, internal IPs and opted-out browsers.
What gets collected#
Page URLs and referrers, UTM parameters, the landing page, and the last few pages of the session that converted. Country, derived from the IP address. A visitor identifier - either a first-party cookie or, in cookieless mode, a server-side hash that rotates every 24 hours. At signup, the email address your own code passes.
No biometric data. No third-party cookies. No ad networks. IP addresses are not stored on any attribution record - they are used in flight to resolve a country, limit abuse and compute the identifier, then dropped. The identifier is scoped per site, so the same person visiting two different Chartsy customers is two unrelated records.


