# Add a site

> Add a website to Checkend, install the tracking script with the install prompt, and check that events arrive.

A *site* is a website that sends events to Checkend. This page adds a site, installs the tracking script and checks that events arrive.

## Add the site

Add the site in the web UI with **Add site**, or with `ce`:

```sh
ce sites add example.com
```

You need write access to the project of the site. Checkend shows two things:

- **The script tag.** Put it in the `<head>` of each page.
- **The install prompt.** Paste it into your coding agent. The agent installs the tag for you.

To select a privacy mode now, add `--privacy`:

```sh
ce sites add example.com --privacy strict
```

The default mode is `standard`. See [Privacy modes](#privacy-mode) below.

## The script tag

The tag loads the tracking script from your Checkend server:

```html
<script defer data-site="example.com" src="https://stats.example.com/js/script.js"></script>
```

The script is smaller than 2 KB gzip. It records page views, route changes of single-page apps, outbound links, file downloads, 404 pages and web vitals. It sets no cookies.

Some frameworks need a small change:

| Framework | File | Note |
|---|---|---|
| Next.js | `app/layout.tsx` (App Router) or `pages/_document.tsx` (Pages Router) | Put the plain `<script>` tag in `<head>`. Do not use `next/script` with a lazy strategy: the tag must keep `data-site`. |
| Astro | The layout of each page, for example `src/layouts/Layout.astro` | Add `is:inline` to the tag, so that Astro does not bundle the script. |
| Rails | `app/views/layouts/application.html.erb` | If the app sets a Content-Security-Policy, add your Checkend URL to `script-src` and `connect-src`. |
| WordPress | A child theme (`functions.php`) or a small plugin | Use a child theme, so that a theme update keeps the tag. |
| Static HTML | Each HTML file, or the shared header include | None. |

> **Note**
> An event URL must be on the site domain or on a subdomain of it. Checkend refuses events for other hosts, so other sites cannot fill your site with false data.

## The install prompt

The install prompt is a text for your coding agent. It contains:

- the site, the site ID and the URL of your Checkend server,
- a one-time install token,
- the script tag and the recipes for Next.js, Astro, Rails, WordPress and static HTML,
- the steps to add goal events to the signup and purchase forms,
- the optional first-party proxy route,
- a last step: the command that checks the install.

Copy the prompt into your agent, then let the agent do the work.

### The install token

The install token starts with `ce_inst_`. It expires after 24 hours. It can do only two things for its site:

- check the install,
- send one test event.

It cannot read your analytics, list sites, write data or sign in to the web UI.

To get a new prompt with a new install token, run:

```sh
ce sites prompt example.com
```

To copy only the prompt text, use `--plain`:

```sh
ce sites prompt example.com --plain
```

## Check the install

1. Deploy the site.
2. Open a page of the site in a browser.
3. Run the check:

   ```sh
   ce sites verify example.com
   ```

`ce sites verify` waits for the first page view. It exits with 0 when the first page view arrives. It exits with 4 when no event arrives within the wait time. The default wait time is 2 minutes.

| Flag | Meaning |
|---|---|
| `--wait 5m` | Wait longer for the first event. |
| `--send-test` | Send one test event ("Checkend install test") first. This checks that the server takes events. |

The agent can run the same check with the install token:

```sh
CHECKEND_URL=https://stats.example.com CHECKEND_TOKEN=ce_inst_... ce sites verify example.com
```

An MCP client can call the tool `verify_install`. The web UI also shows the first event live.

The install has one of three statuses:

| Status | Meaning |
|---|---|
| `waiting` | No event in the last 7 days. |
| `test_only` | Only the test event arrived. |
| `receiving` | Page views or custom events arrive. |

## The first-party proxy route

Ad blockers often stop scripts from other hosts. You can send the script and the events through the domain of your site. Then use this tag:

```html
<script defer data-site="example.com" data-api="/ce/e" src="/ce/script.js"></script>
```

The web server of your site then sends two paths to Checkend:

| Path on your site | Goes to |
|---|---|
| `/ce/script.js` | `https://stats.example.com/js/script.js` |
| `/ce/e` | `https://stats.example.com/api/e` |

The install prompt has tested snippets for Next.js, Nginx and Caddy. Nginx example:

```nginx
location = /ce/script.js {
    proxy_pass https://stats.example.com/js/script.js;
    proxy_set_header Host stats.example.com;
    proxy_ssl_server_name on;
}
location = /ce/e {
    proxy_pass https://stats.example.com/api/e;
    proxy_set_header Host stats.example.com;
    proxy_ssl_server_name on;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
```

> **Caution**
> Add the address of the site's proxy to `--trusted-proxies` on the Checkend server. Without it, Checkend sees the proxy address for each visitor, and the countries are wrong.

> **Caution**
> Do not trust a Next.js server alone. Next.js rewrites pass the `X-Forwarded-For` header of the visitor through and do not add the visitor address, so a visitor can send a false address. Trust Next.js only when a proxy in front of it sets `X-Forwarded-For`, and visitors cannot reach Next.js directly.

## Privacy mode

Each site has one privacy mode: `strict`, `standard` (the default) or `extended`. The mode controls what Checkend keeps about each visitor. See [Privacy](/docs/concepts/privacy.md).

Change the mode of a site:

```sh
ce sites set example.com --privacy strict
```

The new mode applies to new events. Ingest keeps sites in memory for a short time (5 seconds by default), so a change takes effect within this time.

## Next step

[Connect your agent](/docs/start/connect-your-agent.md).

## Prompt for a coding agent

```text
Add Checkend analytics to this project.
1. Find the production domain of this project. Ask me if it is not clear.
2. Run `ce sites add <domain>`. If the site already exists, skip this step.
3. Run `ce sites prompt <domain>` and read the `prompt` field of the JSON. It is the install prompt.
4. Follow the install prompt: add the script tag to the root layout, add the goal events for the signup and purchase forms, and add the goals.
5. After the deploy, run `ce sites verify <domain> --wait 5m`. Exit code 0 means that the first page view arrived.
If ce exits with code 2, tell me to run `ce login <url>`.
```
