The website script is one tag served from https://cdn.tapll.com/v0/u.js: under 5 KB gzipped, no dependencies, source on GitHub. The v0 in the URL is a major version, so fixes reach your site without a snippet change. There is no npm package: keep the tag on the CDN rather than bundling it.
Add a website to a project
An app and its website share one project, so they share one write key and one privacy mode. In Settings → General → Platforms, choose Add website (with the domains it will run on, or none yet).
Install the tag
Put this once in the <head> of every page. The key is a client key: it is public by design and safe to commit.
<script defer src="https://cdn.tapll.com/v0/u.js"
data-key="av_…"></script>
- Page views come from the page load and from
history.pushStatenavigation, so single-page apps need no router hook. Only the path is sent: never the query string or the fragment. UTM parameters are read from the landing URL by the script. - Outbound links and file downloads are tracked on their own.
data-auto="false"turns that off. - If the project lists domains, a page on any other host is refused, so a copied snippet cannot report into your project.
localhost,127.*and*.localreport as development (the Sandbox scope).data-env="development"forces it for a staging host.- Optional:
data-version="<deploy id>"turns on Releases for the site, anddata-exclude="/admin/*"skips paths.
Privacy modes on the web
| Strict anonymous | Product analytics | |
|---|---|---|
| Stored in the browser | Nothing: no cookie, no localStorage | An anonymous id and the session, in localStorage |
| Visitor recognised | Within one UTC day, by a server-side hash with a daily salt | Across visits and, after tapll.identify, across devices |
| Consent banner | Not needed | Needed where the law asks for it |
| Retention and people | No | Yes |
Strict anonymous is the default. In product analytics, add data-mode="product_analytics" and let Tapll ask:
<script defer src="https://cdn.tapll.com/v0/u.js"
data-key="av_…"
data-mode="product_analytics"></script>
Tapll’s consent banner is optional and off by default. Add data-consent="banner" and nothing is stored and nothing is sent until the visitor chooses. Accept and Decline carry equal weight, and the answer is remembered. If the site already has a consent tool, use data-consent="required" and call tapll.consent(true) or tapll.consent(false) from its callbacks.
Events and features
Name the moments that matter and declare the funnels in code:
const signup = tapll.feature('signup');
signup.start('signup_started');
signup.success('signup_completed', { plan: 'pro' });
tapll.track('pricing_toggled', { period: 'yearly' });
A button can also carry data-tapll-event="cta_clicked" and send the event without any JavaScript. Event names are lowercase snake_case; an invalid event is dropped with a console warning, never sent truncated, and the script never throws into your page.
Revenue from the site
Connect Stripe or Paddle in Settings, then pass the visit into checkout: tapll.sessionId() goes into Stripe Checkout metadata or Paddle.js customData as tapll_session_id. Tapll ties the payment to that visit and its first-touch channel and campaign. See Connect revenue.
Ad blockers
Some blockers stop third-party analytics hosts. If your site has a server or edge layer, proxy the script and the endpoint through your own domain and point the tag at it with data-api:
// next.config.js
async rewrites() {
return [
{ source: '/_tapll/u.js', destination: 'https://cdn.tapll.com/v0/u.js' },
{ source: '/_tapll/v1/:path*', destination: 'https://in.tapll.com/v1/:path*' },
];
}
// <script defer src="/_tapll/u.js" data-api="/_tapll" data-key="…"></script>
What the script does not do
It does not measure Web Vitals, report JavaScript errors, record sessions or draw heatmaps, and it ships no framework plugins.
Using Claude Code, Cursor or Codex? Settings → SDK setup has a setup prompt filled in with your project’s key, privacy mode and domains. A generic version is in llms.txt.