# 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.

1. Sign in
  Open the [dashboard](https://clitrail.com/app?signup=1) and choose **Continue with Google**, or enter your email address and choose **Email me a sign-in link** (see [Sign in](https://clitrail.com/docs/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](https://clitrail.com/docs/organisations) later.
2. 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](https://clitrail.com/docs/plans).
3. Register your website
  Give the website a name and the exact origin people install from. `https://acme.dev` and `https://www.acme.dev` are 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.
4. 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](https://clitrail.com/docs/frameworks) show the exact file for Next.js, Astro, Docusaurus, Hugo and the rest.
  ```html
  <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](https://clitrail.com/docs/sdk#consent-and-opt-out).
5. 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](https://clitrail.com/docs/install-paths); pick the example that matches how people install your CLI:
  **One-liner**
  ```sh
  # 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 || true
  ```
  Downloads your website’s hook and runs it. `|| true` keeps a reporting problem from ever failing your installer. To ship the script with your package instead, download `clitrail.sh` from **Setup** and use one of the other tabs.
  **Shell**
  ```sh
  # After your installation succeeds:
  sh clitrail.sh --event install_completed || true
  ```
  **Completed 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](https://pubs.opengroup.org/onlinepubs/9799919799/utilities/sh.html)
  **npm**
  ```json
  {
    "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](https://docs.npmjs.com/cli/using-npm/scripts/)
  **npx**
  ```js
  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](https://docs.npmjs.com/cli/commands/npm-exec/)
  **Homebrew**
  ```rb
  # 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 0755
  ```
  **First 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](https://docs.brew.sh/Formula-Cookbook)
  **Python / PyPI**
  ```py
  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](https://packaging.python.org/en/latest/specifications/entry-points/)
6. Check it end to end
  Open your install page in Chrome or Firefox on macOS or Linux and run `await InstallAttribution.ready` in 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`:
  ```sh
  # 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 -- --doctor
  ```
  No match? See [Common problems](https://clitrail.com/docs/troubleshooting).
7. 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**.

> Note
> 
> Nothing is backfilled. Installs recorded before a destination existed, while it was in test mode, or while the organisation was on Free, are not sent when it goes live.

## Next steps

- [**Set up with your agent**Let Claude Code write the changes from one prompt.](https://clitrail.com/docs/agent-setup)
- [**Installer hook**Flags, environment variables and exactly what it reads.](https://clitrail.com/docs/hook)
- [**Handoff command**Attribution for Safari, remote agents and containers.](https://clitrail.com/docs/handoff)
- [**How delivery works**Test mode, going live, retries and statuses.](https://clitrail.com/docs/destinations)
