Products
Set up website analytics
Add the script to your website, connect revenue, set goals, and read visitors, sources and search keywords.
Analytics shows who visits the website of your business, where they come from, and which visits bring revenue. This page tells you how to add the script, name your visitors, track goals, connect revenue, read the dashboard and share it.
Before you begin
- You need the Analytics product. An owner or admin gives products to people in Settings > People.
- You need access to the code or the editor of your website, to add one script tag.
- To connect revenue, an owner or admin connects a payment provider: Stripe, Lemon Squeezy, Polar or Paddle.
A workspace has one website in Analytics. Other domains of the same business, such as a checkout domain, count as that website.
Set up Analytics
Open Products > Analytics. Until the first visit, the page shows 3 steps, one at a time. The steps over the card show where you are. Select a step that is done to change it.
-
Add site
Type the root Domain, for example
acme.com. Its subdomains count too. Setup fills in the website of your business, when you gave one. Pick the Time zone. The days of each report start at midnight there, and it is the time zone of the workspace too. Select Add website. -
Install script
Select the tab of the place where your site runs: Script, Next.js, npm or WordPress. Each tab says where the code goes. Select Use no cookies for the version that sets no cookies. This also turns on the No cookies setting. Copy the code, and put the tag in the
<head>of every page:html<script defer data-site="ts_YOUR_SITE_ID" data-domain="acme.com" src="https://troopy.ai/js/script.js"></script>Install with AI gives a prompt for an AI code editor, such as Cursor, Claude Code or Copilot. Select Copy the prompt, and paste it into the editor. The editor adds the script for you.
The step shows Waiting for the first visit until the script sends its first event. The page checks every 5 seconds. Select I added the script to go on.
-
Attribute revenue (optional)
Pick your payment provider, and connect it, so that Troopy ties each payment to the visit that brought it. For another provider, pick Another provider (Payment API). Read Connect revenue. Select Continue, or Skip for now. You can do this step at any time.
Then the page waits for the first visit. Select Open and the name of your site to send one. At the first visit, the dashboard takes the place of the steps. To track goals, read Track goals.
The script
The script is small, and it sets no cookies of other sites. Troopy never keeps the IP address of a visitor. It finds the country, the region and the city with the database of DB-IP, and then it drops the address.
The script sets 3 first-party cookies on the root domain: the visitor (1 year), the session (30 minutes) and the count
of sessions. Thus app.acme.com, blog.acme.com and acme.com share one visitor.
Options of the tag
| Attribute | What it does |
|---|---|
data-site | The id of your site. The tag needs it. |
data-domain | The root domain of the site. |
data-allowed-hostnames | Other domains of the site, apart by commas. A link to one of them carries the visitor over. |
data-allow-localhost="true" | Counts the visits of localhost, for a test. |
data-allow-file-protocol="true" | Counts the pages that open as file://. |
data-debug="true" | Counts a page inside an iframe. |
data-auto-pageviews="false" | Sends no pageview by itself. Your code calls troopy("pageview"). |
data-disable-payments="true" | Reads no payment from the address of a success page. |
data-disable-surveys="true" | Shows no survey on the page. |
data-disable-console="true" | Writes no lines in the console of the browser. |
data-api-url | Sends the events to another address, such as your own proxy. |
data-cookieless="true" | Sets no cookies, as script.cookieless.js does. Use it behind a proxy of /js/script.js. |
Single-page apps, hash routes and no cookies
- The script counts a page that changes with no reload, by itself.
- For hash routes, such as
/#/pricing, loadscript.hash.jsin the place ofscript.js. - For no cookies at all, load
script.cookieless.js.script.hash.cookieless.jsdoes both. - With no cookies, the script keeps the session in the tab only. The session ends when the tab closes, or after 30 minutes with no event.
- Troopy makes the id of the visitor from the IP address, the browser and the site, with a random salt for each UTC day. Troopy deletes the salt a day later. Then nobody can find the visitors of that day again.
- Thus each visitor counts one time a day. Troopy cannot tell a visitor who comes back from a new one.
- With no cookies, the dashboard has no Visit count filter, no Journeys tab and no Visitors tab. A link to another domain of the site does not carry the visitor over.
The No cookies switch in Analytics settings > General > Privacy changes the tag that the page shows. Put the new tag on your site. The old tag still sets cookies. With the setting on, Troopy counts each visitor one time a day for every version of the script.
The npm package
For a React, Next.js or Vue app, you can install the troopy-analytics package in the place of the tag:
npm install troopy-analyticsimport { initTroopy } from "troopy-analytics";Â const troopy = initTroopy({ siteId: "ts_YOUR_SITE_ID", domain: "acme.com" });troopy.track("signup", { plan: "pro" });The package loads the same script, so it counts the same things. It also has a part for React Native and Expo
(troopy-analytics/react-native).
Leave your own visits out
Open your site, then run this line in the console of the browser:
localStorage.setItem("troopy_ignore", "true");That browser then sends nothing. To count it again, run localStorage.removeItem("troopy_ignore").
Say who a visitor is
Until you say who a visitor is, Troopy gives the visitor a made-up name. After a person signs in to your product,
call identify with the id of the user:
troopy("identify", { user_id: user.id, name: user.name, email: user.email, plan: "pro",});user_idis required.namegives the visitor a name.- Each other key is a trait. Troopy keeps 10 traits at most, each of 255 characters at most.
- The same
user_idon two devices links the two devices. - Customers and Surveys read the same call. A survey shows only to a user that
identifynamed.
Your server can do the same, with an API key that has the analytics:write scope:
POST /api/v1/analytics/visitors/{visitor}/identify.
<script> window.troopy = window.troopy || function () { (window.troopy.q = window.troopy.q || []).push(arguments); };</script>Track goals
A goal is something that a visitor does that matters to you. Send a goal in one of these ways:
<button data-troopy-goal="signup" data-troopy-goal-plan="pro">Sign up</button><section data-troopy-scroll="viewed_pricing">...</section>troopy("signup", { plan: "pro" });data-troopy-goalsends the goal at each click on the element. Eachdata-troopy-goal-*attribute is a parameter.data-troopy-scrollsends the goal one time a page, when half of the element shows. Change the share withdata-troopy-scroll-threshold, from 0 to 1.- With
data-troopy-scroll-delay, the element must stay in view for that many milliseconds first. - A click on a link to another site counts as an exit link.
- From your server, send
POST /api/v1/analytics/goalswith the id of the visitor. The key needsanalytics:write.
A goal name has 64 characters at most: lowercase letters, digits, _, - and :. The script makes the letters
lowercase, and makes each space an underscore. A goal has 10 parameters at most.
Troopy records some goals itself. You cannot send them: identify, payment, free_trial,
trial_started, trial_converted, subscription_started, subscription_upgraded, subscription_downgraded,
subscription_renewed, subscription_cancel_scheduled, subscription_reactivated and subscription_ended.
Read and change goals
The Goals card of the dashboard has 4 tabs:
| Tab | What it shows |
|---|---|
| Goals | Each goal over the period, with its visitors and its conversion. Select a goal for its parameters. |
| Funnels | The steps that visitors take in order, and how many get through each step. A funnel has 2 to 8 steps. |
| Journeys | The pages that led to a goal. |
| Visitors | Each visitor, with a profile and its events. A public link never shows this tab. |
Under How it shows, a goal has a Label, a Colour and a Description. Hide it from the dashboard hides the goal, and its completions stay.
Connect revenue
Troopy reads payments from the connection of your workspace, the same one that your teammates read. Open Analytics settings > Revenue, or step 3 of the setup. Pick your provider. Owners and admins connect it there with a key, as in Settings > Apps.
Then tell the provider who paid. Do one of these:
| Provider | How to tie a payment to the visitor |
|---|---|
| Stripe | Add troopy_visitor_id to the metadata of Checkout or a PaymentIntent. |
| Stripe Payment Links | Send buyers back to your site with ?session_id={CHECKOUT_SESSION_ID} in the success URL. |
| Lemon Squeezy | Add ?order_id=[order_id] to the redirect URL of the product. For checkouts from your server, use its recipe in Payment API. |
| Polar | Add troopy_visitor_id to the metadata of the checkout, or ?checkout_id={CHECKOUT_ID} to the success URL. |
| Paddle | Open the checkout of Paddle.js with customData: { troopy_visitor_id }. |
| Any provider | On your thank-you page, call troopy("payment", { email }) with the email of the buyer. |
The value of troopy_visitor_id is the cookie of the same name. Troopy also ties a payment by the email or by the
customer of the provider.
- Troopy reads new payments every 5 minutes. The first read takes the last 30 days.
- Select Sync now to read at once.
- Owners and admins can read the history again from 90 days, 1 year or 2 years ago, with Read again.
- A renewal counts on the visitor who first subscribed.
- The name and the email of a payer name a visitor that
identifydid not name. - Troopy does not take refunds off the revenue.
The Payment API
For another provider, or for your own webhook, send each payment from your server. Make an API key with the
analytics:write scope in Settings > API keys. Each transactionId counts one time.
curl -X POST https://troopy.ai/api/v1/analytics/payments \ -H "Authorization: Bearer $TROOPY_API_KEY" -H "Content-Type: application/json" \ -d '{"transactionId":"pay_4821","amount":29,"currency":"USD","visitor":"<troopy_visitor_id>","email":"ada@example.com"}'Give amount in major units (29.99), or amountMinor in minor units (2999). DELETE /api/v1/analytics/payments
takes off what the API recorded: one payment, all the payments of a visitor, or a time range. The Payment API tab
has recipes for Dodo Payments and for the webhooks of Lemon Squeezy.
Read the dashboard
Everything that the dashboard shows comes from its address. Thus a copied link opens the same view.
| Part | What it shows |
|---|---|
| The totals | Visitors, Revenue, Conversion, Per visitor, Bounce rate, Session time and Online, with the change from the period before. |
| The main number | Revenue by default. Main KPI in the settings can make it MRR or a goal. |
| ROAS | Shows when your tracked Meta campaigns spent money in the period. |
| Subscriptions | MRR, Subscribers, In trial, Churn and LTV, from your payment providers. |
| The chart | Visitors as a line and revenue as bars. Notes adds a note to a day, and shows commits and mentions. |
| Sources | Channel, Referrer, Campaign, Keyword, Meta ads and Mentions. |
| Pages | Page, Entry page, Exit link and Hostname. |
| Location | Map, Country, Region and City. |
| Devices | Browser, OS and Device. |
| Goals | Goals, Funnels, Journeys and Visitors. |
| AI crawlers | The pages that AI crawlers read, when your server reports them. |
Each card shows visitors. Select Visitors at the top of a card to show revenue in the place of visitors. Select a row to filter the whole dashboard by it.
- The period menu picks the days. The arrows step to the period before or after.
- Compare shows the numbers of the period before beside each total.
- Filter adds a filter. Save as segment keeps the filters of the view under a name.
- Online opens the globe of the visitors who are on the site now. The globe can replay the last 8 hours. Its first button picks the map: Dark, Light, Outdoors, Satellite, Night lights, Blue Marble, Bright, Atlas, Neon or Emerald.
- Insights opens Revenue insights. They need 200 visitors and 5 attributed payments in the period.
| Key | What it does |
|---|---|
| N, T, Y, H | Now, Today, Yesterday, Last 24 hours |
| 7, 3 | Last 7 days, Last 30 days |
| W, M, A | This week, This month, All time |
| C | Compare with the period before |
| G | The globe |
| I | Revenue insights |
| Left and right arrows | The period before or after |
The Keyword tab needs Google Search Console. The Meta ads tab needs Meta Ads. The Mentions tab shows the posts on X and Reddit that name your site.
Get past ad blockers
Some ad blockers stop scripts that load from an analytics domain. Serve the script from your own domain to get past them.
Serve the script from your domain
Pass 2 paths of your site on to Troopy, with the IP address of the visitor: /js/script.js and /api/events. Then
load the script from your own domain. The script sends its events to the same domain by itself.
Analytics settings > General > Serve the script from your domain has a guide for 15 hosts: Next.js,
Vercel, Netlify, Cloudflare, Nginx, Caddy, Express, PHP, Laravel, Flask, FastAPI, Astro, Vue, Firebase and
DigitalOcean. For Next.js, add this to next.config.js:
async rewrites() { return [ { source: "/js/script.js", destination: "https://troopy.ai/js/script.js" }, { source: "/api/events", destination: "https://troopy.ai/api/events" }, ];}Then load the script from your domain:
<script defer data-site="ts_YOUR_SITE_ID" src="/js/script.js"></script>Use the managed proxy
The managed proxy needs no code on your server. Troopy serves the script from a subdomain of your site, and gets its certificate. Owners and admins set it up.
- Open Analytics settings > General > Managed proxy.
- Keep the subdomain that it offers, such as
a.acme.com, or type another. Select Set up. - At your DNS host, add the CNAME record that the card shows. Its target is
proxy.troopy.ai. - Turn off any proxy of your DNS host for that record. On Cloudflare, the cloud of the record must be grey.
- Select Check now. A DNS change can take an hour to show.
- When the status is Active, load the script from the subdomain, as the card shows.
Troopy checks a proxy that waits every 5 minutes, for a week. After a week, the status is Did not work.
Share a public link
A public link shows the dashboard, read-only, to anyone who has the link. It shows nothing else of the workspace.
Owners and admins make the link:
- Open Analytics settings > General > Public link.
- Select Make a link.
- Copy the link, and send it.
New link makes a new link, and the old link stops at once. Turn off stops the link. Viewers and approvers do not see the link.
The public view never shows MRR, subscribers, the Visitors tab, hidden goals or notes. It does not show the Keyword, Meta ads or Mentions tabs, AI crawlers or Insights.
Analytics settings
Select Analytics settings at the top of the dashboard. The pages of the settings are in a column at the left. On a phone, they are one menu. Each page holds boxes: a title, what the box does, its fields, and its Save.
| Page | What it holds |
|---|---|
| General | The script, the domain, other domains, the time zone, the main KPI, no cookies, the 2 proxies and the public link. |
| Revenue | The payment provider, the state of the last read of payments, and the Payment API. |
| Import | The history of your site from a CSV export of Plausible, as a zip file. |
| Reports | A weekly report and an email for a spike of traffic. |
| Alerts | An email to the maker of the alert when a visitor completes a goal, a payment arrives or a free trial starts. |
| Integrations | Google Search Console, the mentions on X and Reddit, the commits of a GitHub repo on the chart, and Meta Ads. |
| AI crawlers | The code that reports AI crawlers from your server, a bot token, and the crawlers to record. |
| Exclusions | The IP addresses, pages, hostnames and countries that never count. |
| Security | Attack mode. |
On Exclusions, type a value and select Add. The X of a value removes it. /admin is that page, and
/admin/* is each page under it. A country is its code of two letters, such as DE. Attack mode makes the checks
stricter under a flood of fake visits.
The weekly report goes on Monday from 8:00, in the time zone of the workspace. A spike is an hour with 3 times the usual pageviews, and 50 at least. Both emails go to the owners and admins of the organization.
Who can do what
| Person | What the person can do |
|---|---|
| Owner or admin | All of it. Only they connect payment providers, read the history again, make the public link, set up the managed proxy and make the bot token. |
| Member | Read the dashboard. Change the settings, goals, funnels, segments, notes and alerts. |
| Viewer or approver | Read the dashboard and the settings, and change nothing. |
The Growth analyst reads Analytics with read tools. No teammate changes it.
Limits
| Limit | Value |
|---|---|
| Websites in a workspace | 1 |
| Events from one visitor address | 120 a minute, or 30 in attack mode |
| Events of one site | 6,000 a minute |
Parameters of a goal, traits of identify | 10, each value of 255 characters at most |
| Steps of a funnel | 2 to 8 |
| Saved segments | 50 |
| Email alerts | 25 a site, and 60 emails an hour for each alert |
| Revenue insights | 200 visitors and 5 attributed payments in the period |
| The chance that a visitor pays | 1,000 visitors and 10 payments in the last 90 days |
| Words and X handles for mentions | 10 words of 4 to 15 characters, and 5 handles |
| An import from Plausible | A zip file of 20 MB at most |
Problems
The script writes why it sent nothing in the console of the browser, on a line that starts with [Troopy].
| Message | Why | What to do |
|---|---|---|
data-site is missing | The tag has no site id. | Copy the tag again from Analytics settings > General. |
Troopy does not track localhost | Troopy counts no local page. | Add data-allow-localhost="true" for a test. |
Troopy does not track pages inside an iframe | The page is in a frame. | Add data-debug="true" to the tag. |
a program controls this browser | A test tool or a robot drives the browser. | Open the site in a normal browser. |
localStorage.troopy_ignore tells Troopy to ignore this browser | You left this browser out. | Run localStorage.removeItem("troopy_ignore"). |
goal names are lowercase letters, digits, _ - : | The goal name breaks the rule. | Change the name. |
| "That doesn't look like a domain" | The domain has a wrong form. | Type the root domain only, such as acme.com. |
| "The Stripe key can't read ..." | The key lacks a read that revenue needs. | Replace the key with one from the link on its card. |
The answer of /api/events is always 202. Its x-troopy-result header says what Troopy did with the event:
| Result | Why |
|---|---|
ok | Troopy kept the event. |
foreign_host | The page is not on the domain of the site. Add the domain to Other domains. |
excluded | A rule of Exclusions left the event out. |
bot | The browser says that it is a robot. |
rate_limited | The address or the site sent too many events in a minute. |
unknown_site | No site has that id. |
invalid | The event has a wrong form. |
suspicious | Attack mode refused the event. |