> ## Documentation Index
> Fetch the complete documentation index at: https://docs.e2e.army/llms.txt
> Use this file to discover all available pages before exploring further.

# Telemetry

> What the CLI sends, what it never sends, and how to turn it off.

The `e2e` CLI sends anonymous usage data so we can see which commands,
engines, and features are used and where runs fail. It is on by default and
off with one command. Nothing about your tests, your app, or your credentials
leaves the machine.

## Opt out

Any one of these turns it off:

```bash theme={"theme":"catppuccin-mocha"}
npx e2e telemetry disable   # saved in ~/.config/e2e/telemetry.json
export E2E_TELEMETRY_DISABLED=1          # for a shell or a CI job
export DO_NOT_TRACK=1                    # the cross-tool convention
```

`npx e2e telemetry` prints the status and, when it is off, why.
`enable` reverses a saved `disable`; a variable set in the environment still
wins. A `disable` takes effect at once, even for a command already running in
another terminal: every command reads the file again before it sends.

## See what would be sent

```bash theme={"theme":"catppuccin-mocha"}
E2E_TELEMETRY_DEBUG=1 npx e2e run
```

prints every event to stderr as `[telemetry] {...}` and sends nothing. What you
see there is the exact payload.

## What is sent

Two events, sent together in one request when the command ends. Everything
telemetry does after the command shares one two-second deadline, so it never
holds a command up for longer.

Every event carries these properties:

| Property                                                                     | Value                                                                                                                                                                                                                                                                                                                             |
| ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `distinct_id`                                                                | A random id generated on this machine and kept in the preferences file. In CI, `ci:<vendor>` instead, so a fleet of runners does not count as a crowd of users                                                                                                                                                                    |
| `project_id`                                                                 | SHA-256 of the repository's root commit, which every clone shares and no one can compute without the repository. A shallow clone, such as a CI checkout with `fetch-depth: 1`, has no root to offer and counts as outside git. Outside git, the project path hashed with a random local salt; in CI, where there is no salt, none |
| `session_id`                                                                 | A random id for this one invocation                                                                                                                                                                                                                                                                                               |
| `os`, `os_release`, `arch`, `cpu_count`, `memory_gb`, `docker`, `wsl`, `tty` | The machine class                                                                                                                                                                                                                                                                                                                 |
| `ci`, `ci_name`                                                              | Whether `CI` is set, and the vendor when one announces itself (`github-actions`, `gitlab`, `circleci`, ...)                                                                                                                                                                                                                       |
| `coding_agent`                                                               | The coding agent whose shell ran the command, when one announces itself (`claude-code`, `cursor`, `codex`, ...)                                                                                                                                                                                                                   |
| `e2e_version`, `node_version`, `package_manager`                             | Versions and the package manager that invoked the CLI                                                                                                                                                                                                                                                                             |
| `$lib`, `$lib_version`, `$process_person_profile`                            | `e2e`, its version, and `false`: PostHog keeps no person profile                                                                                                                                                                                                                                                                  |

`e2e_cli_session`, once per command:

| Property  | Value                                                                           |
| --------- | ------------------------------------------------------------------------------- |
| `command` | `init`, `run`, `list`, `cache ls`, `guide`, `telemetry`, ...                    |
| `flags`   | The names of the flags given, such as `--headed` or `--tag`. Never their values |

`e2e_run_completed`, once per `e2e run` or `e2e explore`, built from the numbers in `report.json`:

| Property                                                                                                                         | Value                                                                                                                                                                                                                                                                                                                         |
| -------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`, `exit_code`, `duration_ms`                                                                                             | The run's outcome                                                                                                                                                                                                                                                                                                             |
| `flags`                                                                                                                          | The names of the flags given to the run command, never their values                                                                                                                                                                                                                                                           |
| `tests_discovered`, `tests_selected`, `tests_executed`, `tests_passed`, `tests_failed`, `tests_flaky`, `tests_skipped`           | The summary counts                                                                                                                                                                                                                                                                                                            |
| `targets`, `platforms`, `engines`                                                                                                | How many targets, on which platforms, and with which engines and versions, as your config declares them: `web`, `ios`, `playwright@0.6.1`, or your own engine's name and version. A name that is not a plain token of letters, digits, dots, dashes, and underscores is reported as `other`                                   |
| `steps_total`, `steps_agent`, `steps_locator`, `steps_assertion`, `steps_screen`, `steps_app`, `steps_session`, `steps_resource` | Step counts by kind                                                                                                                                                                                                                                                                                                           |
| `agent_steps_replayed`, `agent_steps_partial`, `agent_steps_missed`, `agent_steps_vision`                                        | How the trace cache served the agent steps, and how many used pixel evidence                                                                                                                                                                                                                                                  |
| `model_gateway`, `model_provider`, `model_id`, `model_calls`, `model_tokens`, `model_cached_tokens`, `estimated_cost_usd`        | For the first model-backed step: the AI SDK provider that served it (`gateway` for the Vercel AI Gateway, `openrouter.chat`, `openai.chat`, ...), the vendor (the first segment of a `vendor/model` id, else the provider), and the public model id (a fine-tuned or routed id is reported as `other`); then the run's totals |
| `artifact_bytes`                                                                                                                 | Size of the artifacts the run wrote                                                                                                                                                                                                                                                                                           |
| `errors`, `error_codes`                                                                                                          | How many run-level errors, and the distinct error codes seen anywhere in the run (`LOCATOR_NOT_FOUND`, `APP_UNREACHABLE`, ...), at most 20. A code that is not an upper-case token, the shape of every runner code, is reported as `OTHER`                                                                                    |

## What is never sent

Test titles, file paths, URLs, origins, instructions, observations, page
content, screenshots, error messages, stack traces, environment variables,
credentials, or the contents of any file. The event builders read counts,
versions, and the short names your config declares for engines, platforms, and
models out of the report and nothing else, and the unit tests assert that a
report full of recognizable strings yields a payload with none of them.

## In CI

CI runs are attributed to the CI vendor rather than to a machine, no
preferences file is written on the runner, and no notice is printed. Set
`E2E_TELEMETRY_DISABLED: '1'` in the job's `env` to send nothing from CI.

## Where it goes

Events go to a PostHog project we operate, in one request to
`eu.i.posthog.com`. PostHog keeps no person profile for the ids; the data is
read in aggregate. Telemetry falls under the
[security policy](https://github.com/tester-army/e2e/blob/main/SECURITY.md),
which also lists every other outbound connection a run can make.

## The notice

The first command on a machine prints two lines on stderr, once, saying that
telemetry is on and how to turn it off. The notice never goes to stdout, so
`--reporter json` output stays parseable. When what is collected changes
enough to matter, the notice shows again once.
