# Capless documentation (all pages) > Capless is tiny, agent-native web analytics and session replay (one script tag: 2 KB analytics, 1.7 KB lazily loaded replay) on a headless data backend: append-only streams, CEL functions and ClickHouse materializations, with an HTTP API, an MCP server and a signup API that agents can call directly. # Capless documentation Capless is analytics infrastructure for software that is written, run and queried by AI agents. It has two halves that share one data model: * **Web analytics + session replay**: one script tag. 2.0 KB (brotli) for analytics; session replay is a separate 1.7 KB chunk loaded only for sampled sessions. No cookies. * **A headless data backend**: three primitives behind one HTTP API at `https://api.capless.sh/v1`. 1. **Streams**: durable, ordered, append-only collections of JSON records. 2. **Functions**: deterministic, bounded [CEL](https://cel.dev) expressions for validation, filtering, mapping and erasure. 3. **Materializations**: independently maintained copies of streams in ClickHouse, configured with real SQL. Web analytics events land in a stream called `web.events` and replays in `web.replay`, so everything below applies to your web data too. ## Start here * [Quickstart](https://capless.sh/docs/quickstart.md): account, snippet, first query. Five minutes. * [For agents](https://capless.sh/docs/agents.md): the signup API, MCP and a curl-only flow an agent can run end to end. * [MCP server](https://capless.sh/docs/mcp.md): connect Claude Code, Cursor or any MCP client. ## Reference * [Web SDK](https://capless.sh/docs/web-sdk.md): snippet, JavaScript API, collected fields, replay privacy. * [Streams & cursors](https://capless.sh/docs/streams.md): batches, positions, durability, idempotency. * [Reading & change feed](https://capless.sh/docs/reading.md): `/read`, `/changes`, pagination and limits. * [Time seeking](https://capless.sh/docs/time-seeking.md): jump to a commit time with `/locate`. * [CEL functions](https://capless.sh/docs/cel.md): variables, number and regex rules, examples. * [Erasures & GDPR](https://capless.sh/docs/erasures.md): delete by predicate, everywhere. * [ClickHouse materializations](https://capless.sh/docs/materializations.md): tables, sources, lineage, status. * [API reference](https://capless.sh/docs/api.md): every endpoint group; full [OpenAPI spec](https://capless.sh/openapi.yaml). * [Limits & quotas](https://capless.sh/docs/limits.md) · [Security & privacy](https://capless.sh/docs/security.md) ## Conventions * Base URL `https://api.capless.sh`. All endpoints are under `/v1`. * Authenticate with your project API key: `authorization: Bearer cpl_…`. * Bodies are JSON. Timestamps are UTC ISO 8601 with millisecond precision. * IDs (`str_`, `ent_`, `era_`, `mat_`, `src_`) and cursors (`cur_`) are opaque strings. * Errors are `{"error": {"code", "message", "details"?, "requestId"}}` with a stable `code`. * Lists are paginated with `limit` + `pageToken` → `{data, nextPageToken}`. Every page of these docs is also available as Markdown: append `.md` to its URL (this page: [/docs/index.md](https://capless.sh/docs/index.md)). [/llms.txt](https://capless.sh/llms.txt) indexes them, and [/llms-full.txt](https://capless.sh/llms-full.txt) is everything in one file. --- # Quickstart From zero to a SQL-free answer about your traffic in five minutes. You need an email address and a website you can edit. ## 1. Create an account Enter your email on the [home page](https://capless.sh/#signup) or call the signup API yourself: ```sh curl -s https://api.capless.sh/v1/signup \ -d '{"email":"you@example.com","siteName":"my-site","allowedOrigins":["https://example.com"]}' ``` The response contains everything you need. Save `apiKey`: it is shown once. ```json { "user": { "id": "…", "email": "you@example.com" }, "project": { "id": "prj_…", "name": "…", "plan": "trial", "trialEndsAt": "…" }, "apiKey": "cpl_…", "site": { "key": "cs_…", "snippet": "" }, "dashboardUrl": "https://app.capless.sh/…", "mcp": { "url": "https://mcp.capless.sh/mcp", "headers": { "Authorization": "Bearer cpl_…" } } } ``` * `dashboardUrl` is a one-time sign-in link, valid for 15 minutes. Later, sign in at [app.capless.sh/login](https://app.capless.sh/login). * We send a verification email. Your 14-day trial starts now, no credit card. * `409 email_taken` means you already have an account: [log in](https://app.capless.sh/login) instead. ## 2. Add the snippet Paste the snippet into every page, right before ``: ```html ``` That's it: pageviews, clicks, form submits, errors, Web Vitals and session replays are collected automatically. Single-page apps are detected (`pushState`, back/forward). See [Web SDK](https://capless.sh/docs/web-sdk.md) for custom events and user identification. ## 3. Check that data arrives Open your site in a browser, then read the newest events: ```sh export CAPLESS_API_KEY=cpl_… curl -s https://api.capless.sh/v1/streams/web.events -H "authorization: Bearer $CAPLESS_API_KEY" # → {"name":"web.events","entryCount":3,"lastCommittedAt":"…", …} curl -s https://api.capless.sh/v1/streams/web.events/read -H "authorization: Bearer $CAPLESS_API_KEY" \ -d '{"filter":"entry.type == \"pageview\"","map":"{\"path\": entry.path, \"at\": entry.timestamp}"}' ``` Or open the dashboard at [app.capless.sh](https://app.capless.sh) to watch sessions and replays. ## 4. Ask questions * **From your coding agent**: [connect the MCP server](https://capless.sh/docs/mcp.md) and ask "which pages had the most rage clicks this week?". * **With CEL**: filter and reshape events server-side on `/read` ([CEL functions](https://capless.sh/docs/cel.md)). * **With SQL** (Team plan): [materialize](https://capless.sh/docs/materializations.md) `web.events` into ClickHouse and run real SQL. ## Track your own events ```js capless("track", "checkout_started", { plan: "pro" }); capless("identify", "user_42", { email: "jane@example.com" }); ``` Custom events are `type: "custom"` records with `name` and `properties` in the same `web.events` stream. --- # For agents Capless is built to be set up and operated by coding agents. Everything a human can do in the dashboard, an agent can do with one HTTP API or the MCP server, and every docs page is plain Markdown. ## The one-liner Tell your coding agent: ```text Add Capless analytics to this site. Instructions: https://capless.sh/llms.txt ``` The agent follows [/AGENTS.md](https://capless.sh/AGENTS.md): create an account with the signup API, add the snippet, verify that events arrive, and hand you the dashboard link. ## Machine-readable docs | URL | What | |---|---| | [/llms.txt](https://capless.sh/llms.txt) | Index in [llmstxt.org](https://llmstxt.org) format, linking every page as Markdown | | [/llms-full.txt](https://capless.sh/llms-full.txt) | All docs in one file | | `/docs/.md` | Any docs page as Markdown, e.g. [/docs/cel.md](https://capless.sh/docs/cel.md) | | [/AGENTS.md](https://capless.sh/AGENTS.md) | Install instructions an agent can follow verbatim | | [api.capless.sh/v1/openapi.yaml](https://api.capless.sh/v1/openapi.yaml) | OpenAPI 3.1 spec | | `GET /v1/functions` | The CEL environment: variables per context, limits, number and regex rules | ## Signup API `POST https://api.capless.sh/v1/signup` creates a user, a project on the 14-day trial, an API key and (with `siteName`) a web analytics site, in one call. No authentication. | Field | Type | | |---|---|---| | `email` | string, required | The human's email. A verification email is sent to it. | | `password` | string, optional | Omit it to sign in with emailed links only. | | `projectName` | string, optional | | | `siteName` | string, optional | Creates a site and returns its snippet. | | `allowedOrigins` | string[], optional | Origins allowed to send events for the site, e.g. `["https://example.com"]`. | ```sh curl -s https://api.capless.sh/v1/signup \ -d '{"email":"jane@example.com","projectName":"acme","siteName":"acme.com","allowedOrigins":["https://acme.com"]}' ``` `201 Created`: ```json { "user": { "id": "…", "email": "jane@example.com" }, "project": { "id": "prj_…", "name": "acme", "plan": "trial", "trialEndsAt": "…" }, "apiKey": "cpl_…", "site": { "key": "cs_…", "snippet": "" }, "dashboardUrl": "https://app.capless.sh/…", "mcp": { "url": "https://mcp.capless.sh/mcp", "headers": { "Authorization": "Bearer cpl_…" } } } ``` * `apiKey` is shown once. Store it where the project keeps secrets (e.g. `.env` as `CAPLESS_API_KEY`); never commit it and never put it in client-side code. The site key `cs_…` is public and write-only, so it is safe in HTML. * `dashboardUrl` is a one-time sign-in link valid for 15 minutes. Give it to the human. * `409 email_taken`: the human already has an account. Ask them for an API key from [the dashboard](https://app.capless.sh/login) instead of creating another account. * `429 rate_limited`: wait and retry later; do not loop. ## End-to-end with curl No CLI, no SDK. Replace the email and origin. ```sh # 1. Account + site curl -s https://api.capless.sh/v1/signup \ -d '{"email":"jane@example.com","siteName":"acme.com","allowedOrigins":["https://acme.com"]}' > capless.json export CAPLESS_API_KEY=$(node -p 'require("./capless.json").apiKey') node -p 'require("./capless.json").site.snippet' # paste before node -p 'require("./capless.json").dashboardUrl' # give to the human H="authorization: Bearer $CAPLESS_API_KEY" # 2. After the site is loaded in a browser: events arrived? curl -s https://api.capless.sh/v1/streams/web.events -H "$H" # entryCount > 0 curl -s https://api.capless.sh/v1/streams/web.events/read -H "$H" -d '{"limitResultEntries":5}' # 3. Your own data: a stream with a validator, an idempotent insert, a filtered read curl -s https://api.capless.sh/v1/streams -H "$H" -d '{"name":"orders","validator":"entry.amount >= 0"}' curl -s https://api.capless.sh/v1/streams/orders/entries -H "$H" \ -d '{"idempotencyKey":"import-2026-10-30","entries":[{"user":"u1","amount":42},{"user":"u2","amount":7}]}' curl -s https://api.capless.sh/v1/streams/orders/read -H "$H" -d '{"filter":"entry.amount > 10"}' # 4. GDPR: erase one user everywhere curl -s https://api.capless.sh/v1/streams/web.events/erasures -H "$H" -d '{"filter":"entry.userId == \"user_42\""}' ``` Delete `capless.json` once the key is stored safely. ## MCP The signup response includes a ready-made MCP config. Install it for Claude Code: ```sh claude mcp add --transport http capless https://mcp.capless.sh/mcp --header "Authorization: Bearer cpl_…" ``` Then call the `get_started` tool. See [MCP server](https://capless.sh/docs/mcp.md) for other clients and the full tool list. ## Rules of thumb for agents * Retry writes with the same `idempotencyKey`; a retry returns the original result with `duplicate: true`. * Read with cursors, not offsets. Persist `nextCursor`; it never skips or repeats. * Dry-run CEL with `POST /v1/functions/evaluate` before using it on real data. * Errors carry a stable `code`; branch on it, not on the message. --- # MCP server Capless speaks the [Model Context Protocol](https://modelcontextprotocol.io), so Claude Code, Cursor and any MCP client can set up tracking, read your data and run SQL for you. * URL: `https://mcp.capless.sh/mcp` (Streamable HTTP) * Auth: header `Authorization: Bearer cpl_…` (your project API key) ## Install Claude Code: ```sh claude mcp add --transport http capless https://mcp.capless.sh/mcp --header "Authorization: Bearer cpl_…" ``` Cursor (`.cursor/mcp.json`), and most other clients: ```json { "mcpServers": { "capless": { "url": "https://mcp.capless.sh/mcp", "headers": { "Authorization": "Bearer cpl_…" } } } } ``` No API key yet? The [signup API](https://capless.sh/docs/agents.md#signup-api) returns one, together with this exact config in `mcp`. Keep the key out of version control: use your client's user-level config or an environment variable. ## Tools | Tool | What it does | |---|---| | `get_started` | Start here: what is set up in the project and what to do next | | `list_sites` | Web analytics sites and their snippets | | `create_site` | Create a site (`name`, `allowedOrigins`, `replaySampleRate`) | | `list_streams` | Streams with entry counts and heads | | `create_stream` | Create a stream with an optional CEL validator | | `insert_entries` | Insert a batch atomically (idempotent with a key) | | `read_stream` | Read entries with CEL `filter`/`map`, cursor pagination | | `locate` | Find the cursor for a commit time | | `erase_entries` | Erase entries matching a CEL predicate | | `list_materializations` | ClickHouse materializations | | `create_materialization` | Create an isolated ClickHouse database | | `run_sql` | Run one ClickHouse SQL statement in a materialization | | `connect_stream` | Connect a stream to a table (backfill, then follow) | | `materialization_status` | Insert and deletion freshness per source | | `get_usage` | This month's events and replays against your plan's allowance | | `cel_reference` | The CEL environment: variables, limits, number and regex rules | | `evaluate_cel` | Dry-run a CEL expression against a sample record before using it in a read, erasure or materialization | Each tool maps onto the [HTTP API](https://capless.sh/docs/api.md) with the same semantics, limits and error codes. ## Things to ask * "Is Capless installed correctly on this site? Check that events are arriving." * "Which pages had rage clicks in the last 7 days?" * "Set up a ClickHouse table for pageviews and show me the top referrers this month." * "Delete all data for user `user_42` and confirm when it is purged." * "How close are we to this month's event limit?" --- # Web SDK One tag. 2.0 KB (brotli) for analytics; session replay is a separate 1.7 KB chunk that is only downloaded for sampled sessions. No cookies, no dependencies. ```html ``` Put it before `` on every page. The site key (`cs_…`) is public and write-only. ## Sites A project can have several sites (one per domain or app). Sites are managed with the project API key: ```sh H="authorization: Bearer $CAPLESS_API_KEY" # Create curl -s https://api.capless.sh/v1/web/sites -H "$H" \ -d '{"name":"shop","allowedOrigins":["https://shop.example"],"replaySampleRate":1}' # → {"key":"cs_…","name":"shop","allowedOrigins":["https://shop.example"],"replaySampleRate":1,"snippet":"