Quickstart
Sign in, add the tag, add the hook and see your first attributed install. It takes about five minutes if you can edit your install page and your installer.
-
#Sign in
Open the dashboard and choose Continue with Google, or enter your email address and choose Email me a sign-in link (see Sign in). Your first sign-in creates your own organisation, on the Free plan, with one install path. Websites, destinations, install paths and billing belong to the organisation, so you can invite your team later.
-
#Choose a plan
Attribution runs on Standard ($99 a month) and Enterprise ($499 a month). On Free, CLItrail counts visits and installs and shows their browsers and systems, but matches nothing: installs reported while on Free are never matched later. The organisation’s owner upgrades in Billing. See Plans and limits.
-
#Register your website
Give the website a name and the exact origin people install from.
https://acme.devandhttps://www.acme.devare different origins. Add other domains, such as a docs site, in Settings: 5 domains per website on Free, 10 on Standard and 25 on Enterprise, counting the primary one. -
#Add the website tag
Copy the tag from the Setup tab: it contains your website ID. Place it on every page that shows your install command, after your existing analytics tags. The framework guides show the exact file for Next.js, Astro, Docusaurus, Hugo and the rest.
<script defer src="https://YOUR_SERVICE/browser.js" data-website="YOUR_WEBSITE_ID"></script>If your site asks for analytics consent first, start the SDK only after consent. See Consent and opt-out.
-
#Add the installer hook
Run the hook only after your installation succeeded, as the same OS user who visited the website. Setup shows one hook per install path; pick the example that matches how people install your CLI:
# Run only after your installation succeeded. curl -fsS --connect-timeout 3 --max-time 10 \ https://YOUR_SERVICE/v1/projects/YOUR_WEBSITE_ID/report.sh \ 2>/dev/null | sh || trueDownloads your website’s hook and runs it.
|| truekeeps a reporting problem from ever failing your installer. To ship the script with your package instead, downloadclitrail.shfrom Setup and use one of the other tabs.# After your installation succeeds: sh clitrail.sh --event install_completed || trueCompleted install. Bundle your website’s generated clitrail.sh with the installer. Run it only after installation succeeds. A pre-install call may use install_started, which is not a conversion and reaches webhooks only. Reference
{ "scripts": { "preinstall": "sh scripts/clitrail.sh --event install_started || true", "postinstall": "sh scripts/clitrail.sh --event install_completed || true" } }Install attempt + completion. Include scripts/clitrail.sh in your package. Preserve existing lifecycle commands and report completion after they succeed. npm script settings can block these hooks; first-run reporting is a fallback. These shell examples target macOS and Linux. Reference
import { spawn } from 'node:child_process'; import { fileURLToPath } from 'node:url'; if (process.platform !== 'win32') { const hook = fileURLToPath( new URL('../scripts/clitrail.sh', import.meta.url) ); spawn('sh', [hook, '--event', 'first_run'], { detached: true, stdio: 'ignore' }).on('error', () => {}).unref(); } // Continue your CLI’s normal startup.First run. Bundle scripts/clitrail.sh alongside your CLI. npx can run cached packages, so report from the CLI entry point. This records first_run, not a new installation on every invocation; the service deduplicates it. Reference
# In your formula’s install method, after building acme: libexec.install "acme", "clitrail.sh" (bin/"acme").write <<~SH #!/bin/sh (sh "#{libexec}/clitrail.sh" --event first_run \ >/dev/null 2>&1 || true) & exec "#{libexec}/acme" "$@" SH (bin/"acme").chmod 0755First run. Adapt acme to your CLI and preserve its existing build steps. The generated launcher uses the formula’s installed path, so Homebrew’s bin symlink still works. Reporting runs at first launch, not during bottle construction. Reference
from pathlib import Path import subprocess def report_first_run(): try: subprocess.Popen( ['sh', str(Path(__file__).with_name('clitrail.sh')), '--event', 'first_run'], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True, ) except OSError: pass # Call from your CLI entry point, not on module import. report_first_run()First run. Include clitrail.sh as package data and invoke it from your console entry point. PyPI is a registry; wheel installation has no general package-defined post-install hook. Avoid setup.py/build hooks, which run in the wrong lifecycle. Reference
-
#Check it end to end
Open your install page in Chrome or Firefox on macOS or Linux and run
await InstallAttribution.readyin the console: it resolves to{ ok: true, … }. Then run your installer in a terminal on the same computer and refresh Install events. To see what the hook can find without sending anything, run it with--doctor:# With the downloaded script: sh clitrail.sh --doctor # Or straight from the service: curl -fsS https://YOUR_SERVICE/v1/projects/YOUR_WEBSITE_ID/report.sh \ | sh -s -- --doctorNo match? See Common problems.
-
#Add a destination
In Destinations, choose Add destination and paste the platform’s credentials: there is no “connect” sign-in for any destination. Every destination starts in test mode: CLItrail prepares the exact payload and lists it under Install events, but sends nothing. Use Send test, then Go live.