# Agent Analytics Docs > Guides, installation pages, reference pages, and the full API reference for Agent Analytics. ## Guides URL: https://docs.agentanalytics.sh/guides/ Task-oriented guides for setting up Agent Analytics and running experiments through your AI agent. Use these guides when you want to tell an AI agent what to do, not manually stitch together the low-level steps yourself. ## Start with the right guide If you need environment-specific setup, use the [installation hub](/installation/). If you need low-level browser or endpoint details, continue to [Tracker.js](/reference/tracker-js/) or the [API reference](/api/). ## Getting Started URL: https://docs.agentanalytics.sh/getting-started/ Create a project, install Agent Analytics in your agent, and verify the first query. This is the shortest path from zero to a working Agent Analytics setup. ## 1. Get your API key Sign up at [app.agentanalytics.sh](https://app.agentanalytics.sh) and generate an API key from the dashboard. - Use the API key (`aak_*`) for reads, project management, and agent skill setup. - Use the project token (`aat_*`) in the tracking snippet that goes on your site. ## 2. Pick and complete an install path Go to the [installation hub](/installation/) and complete the setup for the environment you actually use: - [Claude Code](/installation/claude-code/) - [Claude Desktop / Cowork](/installation/claude-desktop-cowork/) - [Cursor](/installation/cursor/) - [OpenClaw](/installation/openclaw/) - [OpenAI Codex](/installation/openai-codex/) If none of those are a fit, the [API reference](/api/) stays available for direct integration. ## 3. Create your first project After your agent is connected, ask it to create the first project for you: - `Create a project called my-site.com` - `Create a project called my-site.com and give me the tracking snippet` If your agent has code write access to your site, ask it: - `Set up analytics for my-site.com` Your agent should create the project and either: - return the tracker snippet for you to paste, or - install the tracker itself if it can edit the site ## 4. Add the tracker manually if needed If your agent already added the tracker to your site, skip this step. If it only created the project and returned a snippet, add the script before ``: ```html ``` Page views are tracked automatically. Add custom events later with `data-aa-event` attributes or `window.aa.track()`. If your site uses Astro, add `is:inline` to that tracker tag. For advanced tracker options like declarative events, cross-domain identity, consent, scroll depth, vitals, and error tracking, use the [Tracker.js guide](/reference/tracker-js/). If you want your agent to launch your first browser-side A/B test after setup, continue with [AI Agent Experiment Tracking](/guides/ai-agent-experiment-tracking/). ## 5. Verify the loop Once the install is working and the tracker is live, ask your agent one of these: - `List my projects` - `How is my-site doing this week?` - `What are the top pages for my-site this week?` - `Show bot traffic for my-site this week` If the setup is correct, the agent should answer without you hand-writing requests. ## Next - Use [Installation](/installation/) for the fastest per-agent setup. - Use [AI Agent Experiment Tracking](/guides/ai-agent-experiment-tracking/) when you want your agent to launch and read browser-side experiments for you. - Use [Tracker.js](/reference/tracker-js/) for browser-side tracking options. - Use [Bot Traffic](/reference/bot-traffic/) to inspect filtered automated callers separately from normal analytics. - Use [Authentication](/reference/authentication/) when you need the read token vs write token rules. - Use [API Reference](/api/) when you need endpoint-level details. ## AI Agent Experiment Tracking URL: https://docs.agentanalytics.sh/guides/ai-agent-experiment-tracking/ Run website and app A/B tests through your AI agent with prompt-first experiment setup, declarative variants, QA, and results review. Use this guide when you want to run browser-side A/B tests on a real website or app by directing your AI agent to do the work for you. This guide is about page elements like headlines, CTAs, pricing copy, and signup flows. It is not for prompt evals, model comparisons, or internal agent workflow testing. ## Before you start - Your site already has `tracker.js` installed. - The project already exists in Agent Analytics. - You are on a paid plan, because experiments are not available on the free tier. - You have one business goal event in mind, such as `signup`, `checkout`, or another real conversion event. If you still need the initial setup, start with [Getting Started](/getting-started/). ## 1. Pick one goal and one UI element to test Start with a narrow change. Good first experiments are usually one CTA, one headline, or one pricing message tied to one business event. Copyable prompt: ```text I want to run an A/B test on my-site. Help me choose one page element to test and one goal event that reflects a real business outcome. Keep the scope to a single CTA or headline. ``` Avoid using `page_view` as the goal unless the page itself is the product outcome. Most of the time, `signup`, `checkout`, or a core activation event is a better goal. ## 2. Ask your agent to verify or add the goal event Before the experiment exists, make sure the goal event is already tracked and named consistently with the rest of the site. Copyable prompt: ```text Add a signup tracking event to this CTA and keep the event name consistent with the existing site naming. If a matching event already exists, reuse it instead of inventing a new one. ``` If the event is simple, your agent should usually prefer declarative tracking in markup rather than extra JavaScript. ## 3. Ask your agent to create the experiment Once the goal event is ready, ask the agent to create an experiment with a clear name and simple variants. Copyable prompt: ```text Create an experiment for the signup CTA on my-site with control and new_cta variants, using signup as the goal event. ``` If you want the exact request and response shape behind that step, use the [Experiments API reference](/api/#tag/experiments). Keep experiment names in `snake_case`, and keep the scope easy to explain later. `signup_cta` is better than something vague like `homepage_test`. ## 4. Ask your agent to wire the variant declaratively For most sites, declarative variants are the cleanest path. The original HTML stays as the control, and the variant content lives in `data-aa-variant-*` attributes. Copyable prompt: ```text Wire this hero headline into the signup_cta experiment using declarative tracker.js attributes. Keep the existing text as control and add the new variant in the markup. ``` Example result: ```html

Start your free trial

``` Use `window.aa?.experiment()` only when the UI is too dynamic for declarative HTML. For the lower-level mechanics, see [Tracker.js](/reference/tracker-js/). ## 5. Ask your agent to QA both variants Before you trust the data, make sure both variants actually render and the goal event still fires. Copyable prompt: ```text Show me how to force each variant locally so I can QA both versions, then verify that the signup event still fires correctly for each one. ``` The forced-variant URLs look like this: - `?aa_variant_signup_cta=control` - `?aa_variant_signup_cta=new_cta` Your agent should use those URLs to check both versions without waiting for hash-based assignment. ## 6. Ask your agent to read results and recommend a winner Once the experiment has traffic, ask your agent to check whether you have enough data and what the current recommendation is. Copyable prompt: ```text Check results for signup_cta and tell me whether we have enough data to pick a winner. If we do, recommend whether to keep running it, pause it, or complete it with a winner. ``` For the underlying HTTP endpoints for reading, pausing, resuming, completing, or deleting experiments, use the [Experiments API reference](/api/#tag/experiments). Make the decision on the business goal event, not on raw traffic. A variant with more exposures is not automatically better if it does not improve `signup` or `checkout`. ## Common mistakes - Testing too many elements at once, which makes the result hard to interpret. - Using `page_view` as the goal instead of a conversion event. - Creating the experiment before the goal event is actually tracked. - Forgetting to QA forced variants before sending traffic through the test. - Packing layout, copy, and offer changes into one experiment when one focused change would be easier to learn from. - Leaving an experiment active after you already know the winner. ## Related - [Guides](/guides/) - [Getting Started](/getting-started/) - [Tracker.js](/reference/tracker-js/) - [CLI vs MCP vs API](/reference/cli-mcp-api/) - [Experiments API](/api/#tag/experiments) - [API Reference](/api/) ## Installation Overview URL: https://docs.agentanalytics.sh/installation/ Choose the right install path for Claude, Cursor, OpenClaw, Codex, and related AI tools. Choose the environment your agent already runs in. Each page leads with the recommended path for that environment, then covers alternatives where they matter. ## Recommended install paths ## Claude Code URL: https://docs.agentanalytics.sh/installation/claude-code/ Install Agent Analytics in Claude Code with the hosted plugin first, then use the skill path before dropping to raw MCP setup.
Claude Code
Use the plugin path first. It gives Claude Code both the MCP server connection and the analytics-specific workflow layer in one install. ## Prerequisites - An Agent Analytics account at [app.agentanalytics.sh](https://app.agentanalytics.sh) - Claude Code installed locally - Access to the same GitHub or Google identity you use for Agent Analytics ## Recommended: install the plugin ```bash /plugin marketplace add Agent-Analytics/agent-analytics-plugin /plugin install agent-analytics ``` This is the shortest hosted path for Claude Code because it packages the MCP connection and the usage guidance together. ## Verify the install Ask Claude Code: - `List my Agent Analytics projects` - `How is my-site doing this week?` - `What are the top pages for my-site this week?` If the plugin is working, Claude Code should be able to reach your account without asking you to hand-roll HTTP requests. If you have not created your first real project yet, go back to [Getting Started](/getting-started/#3-create-your-first-project) and do that next. ## Fallback: install the Claude Code skill If you do not want the full plugin, use the skill path before raw MCP: ```bash npx skills add Agent-Analytics/agent-analytics-mcp ``` This path teaches Claude Code how to set up tracking, query analytics, and run experiments. It still requires a valid Agent Analytics API key in the environment used by Claude Code. ## Lower-level fallback: add only the MCP server If you want the MCP server without the plugin or skill layer: ```bash claude mcp add agent-analytics --transport http https://mcp.agentanalytics.sh/mcp ``` Use `--transport http`. The hosted MCP server is not configured for legacy SSE transport. ## Troubleshooting - Make sure Claude Code is using the same GitHub or Google account as your Agent Analytics dashboard account. - If the skill installs but Claude Code cannot query data, confirm the environment exposes `AGENT_ANALYTICS_API_KEY`. - If the MCP command fails, verify you used `--transport http`. ## Related - [Getting Started](/getting-started/) - [Claude Desktop / Cowork](/installation/claude-desktop-cowork/) - [Authentication](/reference/authentication/) - [API Reference](/api/) ## Claude Desktop / Cowork URL: https://docs.agentanalytics.sh/installation/claude-desktop-cowork/ Connect the hosted MCP server in Claude Desktop or Cowork and verify analytics tools directly in chat.
Claude Desktop / Cowork
Claude Desktop and Cowork both fit the hosted MCP flow. Use the connector UI instead of manually managing API keys in prompts. ## Prerequisites - An Agent Analytics account at [app.agentanalytics.sh](https://app.agentanalytics.sh) - Claude Desktop or Cowork access - GitHub or Google sign-in matching your Agent Analytics account ## Recommended: add the hosted connector 1. Open **Settings** 2. Open **Connectors** 3. Choose **Add** 4. Choose **Custom** 5. Enter: ```text https://mcp.agentanalytics.sh/mcp ``` You will be asked to sign in with GitHub or Google. Use the same identity that owns the projects in Agent Analytics. ## Verify the install Ask Claude: - `List my projects` - `Show me a 7 day overview for my-site` - `Where do users drop off between signup and purchase?` This path is the best hosted experience for conversational analytics because the MCP server can return structured tool results and rich UI where supported. If you have not created your first real project yet, go back to [Getting Started](/getting-started/#3-create-your-first-project) and do that next. ## Manual notes - The hosted MCP server URL stays the same across Claude Desktop and Cowork. - You do not need to manually pass an `X-API-Key` once the connector sign-in succeeds. ## Troubleshooting - If authentication succeeds but no projects appear, confirm the signed-in account matches your Agent Analytics dashboard account. - If a custom connector URL is rejected, re-enter the exact MCP endpoint: `https://mcp.agentanalytics.sh/mcp` - If you need lower-level debugging, switch to the [API reference](/api/) and test the same workflow with direct HTTP calls. ## Related - [Getting Started](/getting-started/) - [Claude Code](/installation/claude-code/) - [Cursor](/installation/cursor/) - [API Reference](/api/) ## Cursor URL: https://docs.agentanalytics.sh/installation/cursor/ Install the Agent Analytics skill in Cursor and use CLI-style workflows first. Use MCP only when you specifically want connector-style tool calls.
Cursor
For Cursor, start with the Agent Analytics skill plus CLI-style workflows. In practice this is usually faster, cheaper on tokens, and easier to steer than MCP for day-to-day use. ## Prerequisites - An Agent Analytics account at [app.agentanalytics.sh](https://app.agentanalytics.sh) - Cursor installed - `npx` available in the environment Cursor uses - A valid Agent Analytics API key available as `AGENT_ANALYTICS_API_KEY` ## Recommended: install the agent skill ```bash npx skills add Agent-Analytics/agent-analytics-mcp export AGENT_ANALYTICS_API_KEY=aak_... ``` This gives Cursor the Agent Analytics workflow layer plus CLI-oriented execution in the same environment. That is usually the best tradeoff when you want lower latency and less token overhead than MCP tool calls. ## Verify the install Ask Cursor: - `List my Agent Analytics projects` - `How is my-site doing this week?` - `What are the top pages for my-site this week?` If you have not created your first real project yet, go back to [Getting Started](/getting-started/#3-create-your-first-project) and do that next. ## Alternative: add a custom MCP server Use MCP in Cursor if you specifically want connector-style tool calls instead of skill + CLI execution. 1. Open the Command Palette 2. Search for `MCP` 3. Choose **Cursor Settings > Tools & MCP** 4. Click **Add Custom MCP** 5. Add this to your `mcp.json`: ```json { "mcpServers": { "agent-analytics": { "url": "https://mcp.agentanalytics.sh/mcp" } } } ``` Save the file and reload Cursor if the tool list does not refresh automatically. MCP works, but it usually adds more latency and token overhead than the skill path. ## Lower-level fallback: direct API If you want to bypass both the skill and MCP, call the hosted API directly: ```bash curl "https://api.agentanalytics.sh/stats?project=my-site&since=7d" \ -H "X-API-Key: aak_..." ``` That lower-level path is useful for debugging auth, but the skill + CLI flow is the recommended installation for day-to-day Cursor usage. ## Troubleshooting - If the skill installs but queries fail, confirm `AGENT_ANALYTICS_API_KEY` is available in the environment Cursor actually uses. - Confirm the `mcp.json` entry is valid JSON if you choose the MCP path. - Reload Cursor after adding the custom MCP server if the tools panel still shows the old state. - If Cursor can see the MCP server but not your projects, verify the hosted sign-in completed with the correct account. ## Related - [Getting Started](/getting-started/) - [CLI vs MCP vs API](/reference/cli-mcp-api/) - [Claude Desktop / Cowork](/installation/claude-desktop-cowork/) - [OpenAI Codex](/installation/openai-codex/) - [API Reference](/api/) ## OpenClaw URL: https://docs.agentanalytics.sh/installation/openclaw/ Install the Agent Analytics skill in OpenClaw from ClawHub, expose your API key once, and use the official CLI from chat. For OpenClaw, the cleanest path is the hosted ClawHub skill. It keeps setup agent-native and works well with CLI-oriented workflows. The important detail: `npx` is only the launcher. The skill is using the official Agent Analytics CLI, and that CLI wraps the same documented HTTP API shown in the docs. ## Prerequisites - An Agent Analytics account at [app.agentanalytics.sh](https://app.agentanalytics.sh) - A valid Agent Analytics API key stored as `AGENT_ANALYTICS_API_KEY` - Access to the OpenClaw environment where you want the skill installed ## Recommended: install from ClawHub Tell OpenClaw: > Install the Agent Analytics skill from ClawHub. My API key is available in `AGENT_ANALYTICS_API_KEY`. Use the official Agent Analytics CLI. The hosted listing is here: - [Agent Analytics on ClawHub](https://clawhub.ai/dannyshmueli/agent-analytics) Once installed, OpenClaw can create projects, generate tracking snippets, query stats, inspect funnels, and manage experiments from the same conversation loop. The public docs still matter because they show exactly what the CLI is wrapping: - [CLI vs MCP vs API](/reference/cli-mcp-api/) - [API Reference](/api/) That is the right explanation if a security review asks what the `npx` command is doing. ## Verify the install Ask OpenClaw: - `List my Agent Analytics projects` - `How is my-site doing this week?` - `Send me a daily analytics summary every morning` If you have not created your first real project yet, go back to [Getting Started](/getting-started/#3-create-your-first-project) and do that next. ## Troubleshooting - If ClawHub flags the skill because of `npx`, point reviewers to [CLI vs MCP vs API](/reference/cli-mcp-api/) and [API Reference](/api/). The CLI is the official wrapper around those documented endpoints. - If the skill installs but cannot query data, confirm `AGENT_ANALYTICS_API_KEY` is available in the OpenClaw environment instead of pasted into chat. - If OpenClaw can create projects but not read analytics, verify the key belongs to the same Agent Analytics account you expect. - If you need endpoint-level debugging, use the [API reference](/api/) with `curl` before returning to the skill flow. ## Related - [Getting Started](/getting-started/) - [Claude Code](/installation/claude-code/) - [OpenAI Codex](/installation/openai-codex/) - [API Reference](/api/) ## OpenAI Codex URL: https://docs.agentanalytics.sh/installation/openai-codex/ Install the Agent Analytics skill for OpenAI Codex, set the API key once, and use direct queries or guided workflows from there.
OpenAI Codex
For OpenAI Codex, the cleanest path today is the Agent Skills install. It keeps the workflow agent-native and does not require the MCP connector flow. ## Prerequisites - An Agent Analytics account at [app.agentanalytics.sh](https://app.agentanalytics.sh) - A valid Agent Analytics API key - `npx` available in the environment Codex uses ## Recommended: install the Agent Analytics skill ```bash npx skills add Agent-Analytics/agent-analytics-mcp ``` Then expose your API key to the agent environment: ```bash export AGENT_ANALYTICS_API_KEY=aak_... ``` The skill teaches Codex how to set up tracking, query analytics, inspect project health, and run experiments from the same conversation loop. ## Verify the install Ask Codex: - `List my Agent Analytics projects` - `How is my-site doing this week?` - `Create an experiment for the signup CTA on my-site` If you have not created your first real project yet, go back to [Getting Started](/getting-started/#3-create-your-first-project) and do that next. ## Troubleshooting - If the skill installs but queries fail, check that `AGENT_ANALYTICS_API_KEY` is present in the environment the agent actually runs in. - If `npx` is unavailable, install the required Node.js runtime first or use the direct API route temporarily. - If you need endpoint-level debugging, use the [API reference](/api/) and test with `curl`. ## Related - [Getting Started](/getting-started/) - [Claude Code](/installation/claude-code/) - [Authentication](/reference/authentication/) - [API Reference](/api/) ## Tracker.js URL: https://docs.agentanalytics.sh/reference/tracker-js/ Add Agent Analytics to any site with one script tag, then turn on declarative events, consent, performance tracking, and other browser-side features. `tracker.js` is the browser-side part of Agent Analytics. Use it when you want page views, custom events, and client-side experiments without shipping a heavy SDK. The API reference now treats `GET /tracker.js` as the script endpoint only. The setup and feature guide lives here. ## Base snippet Add this before ``: ```html ``` Required attributes: - `data-project`: your project name - `data-token`: the public project token (`aat_*`) The tracker automatically collects page URL, pathname, referrer, browser, OS, device, language, timezone, UTM params, session count, and first-touch attribution. No cookies are required. Automated traffic that reaches the tracker is filtered out of your normal analytics. Use [Bot Traffic](/reference/bot-traffic/) if you want to inspect those automated requests separately. If your agent can edit code, ask it to add the snippet for you. If not, create the project in [Getting Started](/getting-started/#3-create-your-first-project) and paste the returned snippet manually. ## Common options | Attribute | What it does | | --- | --- | | `data-link-domains="example.com"` | Link anonymous identity across sibling domains or subdomains | | `data-do-not-track="true"` | Respect the browser Do Not Track signal | | `data-heartbeat="15"` | Measure active time on page while the tab is visible | | `data-track-outgoing="true"` | Track external link clicks as `outgoing_link` | | `data-track-clicks="true"` | Track `` and ` ``` That fires a `signup` event with `{ plan: "pro" }`. This is usually the easiest path for agents too. They can add attributes to existing markup instead of wiring `onclick` handlers or editing application code. ## Impressions Track whether a section was actually seen: ```html
...
``` When the element becomes visible, the tracker sends an `$impression` event. ## `window.aa` API Use the JavaScript API when the event depends on runtime state: ```js window.aa?.track('checkout_started', { plan: 'pro' }); window.aa?.identify('user_123'); window.aa?.set({ plan: 'pro', team: 'acme' }); ``` Useful methods: - `aa.track(event, properties)`: send a custom event - `aa.page(name)`: manually send a page view - `aa.identify(id)`: link anonymous behavior to a known user ID - `aa.set(properties)`: attach global properties to future events - `aa.experiment(name, variants)`: assign variants deterministically client-side - `aa.grantConsent()` / `aa.revokeConsent()`: manage consent mode ## Common recipes ### Cross-domain identity ```html ``` ### Privacy and consent ```html ``` ```js window.aa?.grantConsent(); window.aa?.revokeConsent(); ``` ### Experiments ```html

Start your free trial

``` If you want the full prompt-first workflow for creating, wiring, QAing, and reading an experiment through your agent, use [AI Agent Experiment Tracking](/guides/ai-agent-experiment-tracking/). ### Local development On localhost, the tracker switches to dev mode and logs activity to the browser console instead of sending production data. That keeps local testing out of your real analytics. ## Related - [Getting Started](/getting-started/) - [AI Agent Experiment Tracking](/guides/ai-agent-experiment-tracking/) - [Bot Traffic](/reference/bot-traffic/) - [Authentication](/reference/authentication/) - [API Reference](/api/) ## Bot Traffic URL: https://docs.agentanalytics.sh/reference/bot-traffic/ See which automated callers hit your tracker, how many events were filtered, and which actors generated that traffic. Bot Traffic shows automated requests that reached Agent Analytics and were filtered out of your normal analytics. Use it when you want to answer questions like: - Did ChatGPT or another agent actually hit this site? - Are search crawlers or preview bots touching the tracker? - How many analytics events were dropped because they came from automation? ## Important scope This feature is intentionally narrower than CDN or reverse-proxy logs. - It is **not** full site traffic visibility like Cloudflare. - It only includes automated requests that reached `POST /track` or `POST /track/batch`. - It stores daily aggregates, not raw request logs. - It does **not** store IPs. - It does **not** add bot hits to your normal event analytics, sessions, or billing. ## What you get back Project and account views return: - `automated_requests`: how many filtered tracking requests were received - `dropped_events`: how many analytics events were discarded inside those requests - `categories`: grouped buckets like `ai_agent`, `search_crawler`, `social_preview`, `monitoring_perf` - `actors`: normalized sources like `ChatGPT-User`, `Googlebot`, `ClaudeBot`, `curl` - `time_series`: zero-filled daily rollup for the selected period ## Access paths ### CLI ```bash npx @agent-analytics/cli bot-traffic my-site --period 7d --limit 5 npx @agent-analytics/cli bot-traffic --all --period 7d --limit 10 ``` ### MCP Use: - `bot_traffic_overview` for one project - `all_sites_bot_traffic` for account scope ### API ```bash curl "https://api.agentanalytics.sh/bot-traffic?project=my-site&period=7d&limit=5" \ -H "X-API-Key: aak_..." ``` ```bash curl "https://api.agentanalytics.sh/account/bot-traffic?period=7d&limit=10" \ -H "X-API-Key: aak_..." ``` ## Project vs account scope Use project scope when you want top actors and category breakdown for one site. Use account scope when you want: - total filtered automation across all active projects - which projects are seeing that traffic - a lightweight all-sites overview instead of per-project details Deleted projects are excluded from account rankings and totals. ## Availability Bot traffic overview is available on both hosted free and pro plans through API, CLI, and MCP. ## Related - [Tracker.js](/reference/tracker-js/) - [CLI vs MCP vs API](/reference/cli-mcp-api/) - [Rate Limits](/reference/rate-limits/) - [API Reference](/api/) ## Authentication URL: https://docs.agentanalytics.sh/reference/authentication/ Understand the difference between API keys and project tokens before you wire an agent or a tracker. Agent Analytics uses two different credentials. They serve different jobs and should not be swapped. ## API key (`aak_*`) Use the API key for: - reading analytics data - creating or listing projects - account-level endpoints - direct API access from scripts, tools, and agents Pass it with the `X-API-Key` header or the `?key=` query parameter. ```bash curl "https://api.agentanalytics.sh/stats?project=my-site&since=7d" \ -H "X-API-Key: aak_..." ``` Treat it as secret material. ## Project token (`aat_*`) Use the project token for: - `POST /track` - `POST /track/batch` - the browser tracker snippet embedded on your site The token is public by design. It identifies the project for event ingestion and is expected to appear in HTML. ```html ``` ## Common mistake Do not put the API key in the client-side tracker. The tracker uses the public project token only. ## CLI auth helpers If you use the official CLI, it provides two local auth convenience commands around the API key: - `npx @agent-analytics/cli login --token aak_...` saves the API key locally for later CLI reads. - `npx @agent-analytics/cli logout` clears the saved local CLI auth. `logout` does not revoke the API key on the server. Use `revoke-key` when you want to invalidate the old key and issue a new one. If you set `AGENT_ANALYTICS_API_KEY` in your shell environment, the CLI will continue to use that env var even after `logout` until you unset it. ## Related - [Getting Started](/getting-started/) - [CLI vs MCP vs API](/reference/cli-mcp-api/) - [Error Format](/reference/error-format/) - [API Reference](/api/) ## CLI vs MCP vs API URL: https://docs.agentanalytics.sh/reference/cli-mcp-api/ Agent Analytics can be used through MCP, the CLI, or raw HTTP. Choose the interface that fits the environment your agent already has. Agent Analytics has three real access modes: - `MCP` for chat-native and editor-native tool use - `CLI` for shell-oriented agent workflows - `API` for raw HTTP control The CLI is a convenience wrapper around the same public HTTP API. If an environment is strict about transient package execution or security scanners dislike `npx`, use the API docs directly and keep the same underlying workflows. Which one is best depends on what your agent can already do. ## When to use each ### MCP Use MCP when your agent already runs inside a tool that supports connectors or MCP servers, such as Claude Desktop, Cowork, Cursor, or Claude Code plugin flows. MCP is usually the best fit when: - you want the install to feel native inside chat - you want tool calls instead of shell commands - you do not want to hand-roll auth headers or request payloads - you want quick project or account summaries like `analytics_overview`, `bot_traffic_overview`, or `all_sites_bot_traffic` Tradeoff: - MCP often adds more latency and token overhead than skill + CLI flows because the model has to manage more tool-call round trips and tool result payloads. ### CLI Use the CLI when your agent already has terminal access and is comfortable executing commands. CLI is usually the best fit when: - your agent already lives in a shell-first environment - you want predictable command output - you prefer command composition over tool integration - you want lower overhead than MCP in editor-style agents like Cursor - you want simple local auth helpers like `login` and `logout` around the same API ### API Use the API when you want strict control over requests, retries, and response parsing. API is usually the best fit when: - you are integrating from your own code - you need exact HTTP-level behavior - you are debugging auth or payload shape directly ## CLI to API mapping Most CLI workflows map directly to an HTTP endpoint. The main exception is local auth convenience commands such as `logout`, which only modify local CLI state: | CLI Command | API Endpoint | | --- | --- | | `npx @agent-analytics/cli stats my-site` | `GET /stats?project=my-site` | | `npx @agent-analytics/cli all-sites --period 7d` | `GET /account/all-sites?period=7d` | | `npx @agent-analytics/cli bot-traffic my-site --period 7d` | `GET /bot-traffic?project=my-site&period=7d` | | `npx @agent-analytics/cli bot-traffic --all --period 7d` | `GET /account/bot-traffic?period=7d` | | `npx @agent-analytics/cli events my-site` | `GET /events?project=my-site` | | `npx @agent-analytics/cli query --project my-site --metrics event_count` | `POST /query` | | `npx @agent-analytics/cli funnel my-site --steps "page_view,signup,purchase"` | `POST /funnel` | | `npx @agent-analytics/cli retention my-site --period week --cohorts 8` | `GET /retention?project=my-site&period=week&cohorts=8` | | `npx @agent-analytics/cli experiments list my-site` | `GET /experiments?project=my-site` | | `npx @agent-analytics/cli experiments create my-site --name signup_cta --variants control,new_cta --goal signup` | `POST /experiments` | | `npx @agent-analytics/cli experiments get exp_abc123` | `GET /experiments/{id}` | | `npx @agent-analytics/cli projects` | `GET /projects` | | `npx @agent-analytics/cli logout` | None. Local-only command that clears saved CLI auth and does not call the API. | `logout` clears the API key saved by the CLI on disk. It does not revoke the key on the server. If you exported `AGENT_ANALYTICS_API_KEY` in your shell, the CLI will still authenticate with that environment variable until you unset it. ## Quick rule of thumb - Choose `CLI` first in shell-capable environments like Cursor when the agent can run commands directly. - Choose `MCP` when you specifically want native connector-style tool use or do not have a good shell path. - Choose `API` when you need full control or lower-level debugging. ## Related - [Bot Traffic](/reference/bot-traffic/) - [Installation Overview](/installation/) - [Authentication](/reference/authentication/) - [API Reference](/api/) ## Rate Limits URL: https://docs.agentanalytics.sh/reference/rate-limits/ Hosted Agent Analytics uses account-level API limits and event-volume limits that vary by plan. ## Standard limits | Tier | Requests / minute | Event limit | Data retention | | --- | --- | --- | --- | | Free | 10 | 100,000 / month | 90 days | | Pro | 1,000 | Unlimited | 365 days | Free hosted accounts also get: - up to 2 projects - 500 agent/API read actions per month - MCP access for `list_projects`, `create_project`, `all_sites_overview`, `analytics_overview`, `bot_traffic_overview`, and `all_sites_bot_traffic` Bot traffic overview stays available on both hosted tiers through API, CLI, and MCP. Paid plans unlock the full API, CLI, and MCP surface. Free accounts receive `PRO_REQUIRED` on query, funnels, retention, experiments, and the richer analytics endpoints. ## Streaming limits | Limit | Value | | --- | --- | | Concurrent SSE streams | 10 per account | | `/live` rate | Standard API rate limit | | Inactivity timeout | 30 minutes | | Ring buffer | 5 minutes / 10,000 events | ## What to do if you hit a limit - back off and retry instead of hammering the same endpoint - reduce polling frequency for overview queries - batch ingestion where possible - move experiments and heavier analysis to a pro account if that is the bottleneck ## Related - [Bot Traffic](/reference/bot-traffic/) - [Error Format](/reference/error-format/) - [API Reference](/api/) ## Error Format URL: https://docs.agentanalytics.sh/reference/error-format/ All API errors return a machine-readable code plus a human-readable message. All errors return JSON with an `error` code and a `message`. ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ## Common error codes - `AUTH_REQUIRED` - `FORBIDDEN` - `PROJECT_REQUIRED` - `MISSING_FIELDS` - `RATE_LIMITED` - `INVALID_TOKEN` - `BATCH_TOO_LARGE` - `INVALID_METRIC` - `INVALID_GROUP_BY` - `INVALID_FILTER_OP` - `INVALID_PROPERTY_KEY` - `NOT_FOUND` - `EXPERIMENT_NOT_FOUND` - `EXPERIMENT_NAME_EXISTS` - `INVALID_VARIANTS` - `INVALID_EXPERIMENT_STATUS` - `PRO_REQUIRED` - `STREAM_LIMIT` - `INVALID_FUNNEL_STEPS` ## How to debug 1. check the `error` field first and branch on that in your code 2. use the `message` field for logs and human-readable tool output 3. confirm whether the failure came from auth, a missing project, or invalid query shape before retrying ## Related - [Authentication](/reference/authentication/) - [Rate Limits](/reference/rate-limits/) - [API Reference](/api/) ## API Reference # Agent Analytics API - **OpenAPI Version:** `3.1.0` - **API Version:** `1.0.0` Web analytics platform with an HTTP API that AI agents can query. **Docs:** [Home](https://docs.agentanalytics.sh) · [Getting Started](https://docs.agentanalytics.sh/getting-started/) · [Installation](https://docs.agentanalytics.sh/installation/) · [API Reference](https://docs.agentanalytics.sh/api/) · [OpenAPI Spec](https://docs.agentanalytics.sh/openapi.yaml) **Quick start:** 1. Sign up at [app.agentanalytics.sh](https://app.agentanalytics.sh) and get an API key. 2. Create a project with `POST /projects`. 3. Add the returned tracker snippet to your site. 4. Query analytics with `GET /stats`. **Authentication:** - `aak_*` API keys are secret and used for reads plus project management. - `aat_*` project tokens are public and used for ingestion only. Installation guides for Claude Code, Claude Desktop / Cowork, Cursor, and OpenAI Codex now live in the docs site instead of this OpenAPI intro block. ## Servers - **URL:** `https://api.agentanalytics.sh` - **Description:** Production (hosted) ## Operations ### Track a single event - **Method:** `POST` - **Path:** `/track` - **Tags:** Ingestion Record an event. Called automatically by the `tracker.js` script running in your visitors' browsers — you don't need to call this directly. Add the tracker to your site and it handles everything: ```html ``` The response is `202 Accepted` — events are queued and written asynchronously. Automated traffic that reaches this endpoint is filtered out of normal analytics and reported separately via `/bot-traffic` and `/account/bot-traffic`. #### Request Body ##### Content-Type: application/json - **`event` (required)** `string` — Event name (max 256 chars) - **`token` (required)** `string` — Project token (\`aat\_\*\`) - **`properties`** `object | null` — Arbitrary key-value properties (max 8KB JSON). The tracker auto-populates \`path\`, \`url\`, \`hostname\`, \`title\`, \`referrer\`, \`screen\`, \`browser\`, \`browser\_version\`, \`os\`, \`device\`, \`language\`, \`timezone\` (IANA), \`utm\_\*\` params, \`session\_count\`, \`days\_since\_first\_visit\`, and \`first\_utm\_\*\` (first-touch attribution). - **`session_id`** `string | null` — Session identifier (max 256 chars). The tracker auto-generates one via sessionStorage with 30-min inactivity timeout. - **`timestamp`** `number | null` — Unix timestamp in milliseconds. Defaults to \`Date.now()\`. Must be within 30 days ago to 5 minutes in the future. - **`user_id`** `string | null` — Anonymous user identifier (max 256 chars). The tracker auto-generates one via localStorage. **Example:** ```json { "token": "aat_51da22cdcab084ae1cdb50fd4841c642f0dafdb1d9adfa6b", "event": "page_view", "properties": { "path": "/pricing", "referrer": "https://google.com", "browser": "Chrome", "os": "macOS" }, "user_id": "usr_abc123", "session_id": "ses_xyz789", "timestamp": 1706745600000 } ``` #### Responses ##### Status: 202 Event queued ###### Content-Type: application/json - **`ok`** `boolean` - **`queued`** `boolean` **Example:** ```json { "ok": true, "queued": true } ``` ##### Status: 400 Validation error (missing fields, invalid event name, properties too large) ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Invalid or missing token ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 429 Rate limited ###### Content-Type: application/json **All of:** - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message * **`reason`** `string`, possible values: `"rate_limit", "lifetime_event_limit", "monthly_spend_cap"` **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required", "reason": "rate_limit" } ``` ### Track multiple events - **Method:** `POST` - **Path:** `/track/batch` - **Tags:** Ingestion Record up to 100 events in a single request. All events must use the same project token. Like `/track`, this is called by the browser tracker — not by agents or CLI. Automated traffic that reaches this endpoint is filtered out of normal analytics and reported separately via `/bot-traffic` and `/account/bot-traffic`. #### Request Body ##### Content-Type: application/json - **`events` (required)** `array` — Array of events (max 100 per batch) **Items:** - **`event` (required)** `string` - **`token` (required)** `string` — Project token (\`aat\_\*\`). All events must use the same token. - **`properties`** `object | null` - **`session_id`** `string | null` - **`timestamp`** `number | null` - **`user_id`** `string | null` **Example:** ```json { "events": [ { "token": "aat_51da22cdcab084ae1cdb50fd4841c642f0dafdb1d9adfa6b", "event": "page_view", "properties": null, "user_id": null, "session_id": null, "timestamp": null } ] } ``` #### Responses ##### Status: 202 Events queued ###### Content-Type: application/json - **`count`** `integer` - **`ok`** `boolean` - **`queued`** `boolean` **Example:** ```json { "ok": true, "queued": true, "count": 5 } ``` ##### Status: 400 Validation error (empty array, exceeds 100 events, invalid event) ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Invalid or missing token ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 429 Rate limited ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Link anonymous user to authenticated identity - **Method:** `POST` - **Path:** `/identify` - **Tags:** Ingestion Maps `previous_id` (typically an anonymous tracker ID like `anon_xxx`) to `user_id` (the authenticated identity). All historical events and sessions with `previous_id` are backfilled to use `user_id`. Called automatically by the tracker's `aa.identify("user_123")` method. Can also be called server-side after authentication to stitch identities. - **Idempotent**: sending the same mapping twice is a no-op - **Re-identify**: updating the mapping triggers a new backfill - **Project-scoped**: only affects events/sessions in the specified project #### Request Body ##### Content-Type: application/json - **`previous_id` (required)** `string` — The anonymous or previous user ID to replace - **`token` (required)** `string` — Project token (\`aat\_\*\`) - **`user_id` (required)** `string` — The authenticated user ID to assign **Example:** ```json { "token": "aat_abc123", "previous_id": "anon_k7x9m2abc", "user_id": "user_12345" } ``` #### Responses ##### Status: 202 Identity mapping queued for processing ###### Content-Type: application/json - **`ok`** `boolean` - **`queued`** `boolean` **Example:** ```json { "ok": true, "queued": true } ``` ##### Status: 400 Validation error (missing fields, same IDs, exceeds length) ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Invalid or missing token ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 429 Rate limited ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Aggregated stats overview - **Method:** `GET` - **Path:** `/stats` - **Tags:** Analytics Returns an overview of a project's analytics: time series, top events, session metrics, and totals. Bounce metrics are computed from event names at query time. A session is a bounce when it has only non-interactive events: `page_view`, `$impression`, `$scroll_depth`, `$error`, `$time_on_page`, `$performance`, `$web_vitals`. **CLI:** `npx @agent-analytics/cli stats my-site --since 7d` #### Responses ##### Status: 200 Stats overview ###### Content-Type: application/json - **`events`** `array` — Top event names by count (max 20) **Items:** - **`count`** `integer` - **`event`** `string` - **`unique_users`** `integer` - **`period`** `object` - **`from`** `string` - **`groupBy`** `string`, possible values: `"hour", "day", "week", "month"` - **`to`** `string` - **`project`** `string` - **`sessions`** `object` - **`avg_duration`** `integer` — Average session duration in milliseconds - **`bounce_rate`** `number` — Bounce rate computed from event names at query time. A session is a bounce when it has only non-interactive events: \`page\_view\`, \`$impression\`, \`$scroll\_depth\`, \`$error\`, \`$time\_on\_page\`, \`$performance\`, \`$web\_vitals\`. - **`pages_per_session`** `number` - **`sessions_per_user`** `number` - **`total_sessions`** `integer` - **`timeSeries`** `array` **Items:** - **`bucket`** `string` - **`total_events`** `integer` - **`unique_users`** `integer` - **`totals`** `object` - **`total_events`** `integer` - **`unique_users`** `integer` **Example:** ```json { "project": "my-site", "period": { "from": "2025-01-01", "to": "2025-01-07", "groupBy": "day" }, "totals": { "unique_users": 142, "total_events": 1247 }, "timeSeries": [ { "bucket": "2025-01-01", "unique_users": 1, "total_events": 1 } ], "events": [ { "event": "page_view", "count": 1, "unique_users": 1 } ], "sessions": { "total_sessions": 1, "bounce_rate": 0.45, "avg_duration": 1, "pages_per_session": 1, "sessions_per_user": 1 } } ``` ##### Status: 400 Missing project parameter ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Raw event log - **Method:** `GET` - **Path:** `/events` - **Tags:** Analytics Returns individual events, newest first. Filter by event name and/or session ID. **CLI:** `npx @agent-analytics/cli events my-site --event page_view --since 7d --limit 50` #### Responses ##### Status: 200 Event list ###### Content-Type: application/json - **`events`** `array` **Items:** - **`country`** `string | null` — ISO 3166-1 alpha-2 country code derived from the request IP at ingestion time - **`date`** `string` - **`event`** `string` - **`id`** `string` - **`project_id`** `string` - **`properties`** `object | null` - **`session_id`** `string | null` - **`timestamp`** `number` - **`user_id`** `string | null` - **`project`** `string` **Example:** ```json { "project": "", "events": [ { "id": "", "project_id": "", "event": "", "properties": null, "user_id": null, "session_id": null, "timestamp": 1, "date": "", "country": "US" } ] } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Flexible analytics query - **Method:** `POST` - **Path:** `/query` - **Tags:** Analytics Run custom analytics queries with metrics, grouping, filtering, and ordering. This is the most powerful endpoint for building custom reports. **Metrics:** `event_count`, `unique_users`, `session_count`, `bounce_rate`, `avg_duration` **Group by:** `event`, `date`, `user_id`, `session_id`, `country` **Filter operators:** `eq`, `neq`, `gt`, `lt`, `gte`, `lte`, `contains` **Filterable fields:** `event`, `user_id`, `date`, `country`, `session_id`, `timestamp`, and any `properties.*` field (e.g. `properties.path`, `properties.browser`) **CLI:** `npx @agent-analytics/cli query --project my-site --metrics event_count --group-by event --filter '[{"field":"country","op":"eq","value":"US"}]'` #### Request Body ##### Content-Type: application/json - **`project` (required)** `string` — Project name - **`date_from`** `string` — Start date (ISO 8601 or \`Nd\` shorthand). Defaults to 7 days ago. - **`date_to`** `string` — End date (ISO 8601). Defaults to today. - **`filters`** `array` **Items:** - **`field`** `string` — Field to filter on. Built-in: \`event\`, \`user\_id\`, \`date\`, \`country\`, \`session\_id\`, \`timestamp\`. Property fields: \`properties.path\`, \`properties.browser\`, etc. - **`op`** `string`, possible values: `"eq", "neq", "gt", "lt", "gte", "lte", "contains"` - **`value`** `object` — Value to compare against - **`group_by`** `array`, default: `[]` — Fields to group results by **Items:** `string`, possible values: `"event", "date", "user_id", "session_id", "country"` - **`limit`** `integer`, default: `100` - **`metrics`** `array`, default: `["event_count"]` — Metrics to compute **Items:** `string`, possible values: `"event_count", "unique_users", "session_count", "bounce_rate", "avg_duration"` - **`order`** `string`, possible values: `"asc", "desc"`, default: `"desc"` - **`order_by`** `string`, possible values: `"event_count", "unique_users", "session_count", "date", "event"` — Field to sort by **Example:** ```json { "project": "my-site", "metrics": [ "event_count" ], "group_by": [], "filters": [ { "field": "event", "op": "eq", "value": "page_view" } ], "date_from": "2025-01-01", "date_to": "2025-01-07", "order_by": "event_count", "order": "desc", "limit": 100 } ``` #### Responses ##### Status: 200 Query results ###### Content-Type: application/json - **`count`** `integer` - **`group_by`** `array` **Items:** `string` - **`metrics`** `array` **Items:** `string` - **`period`** `object` - **`from`** `string` - **`to`** `string` - **`project`** `string` - **`rows`** `array` **Items:** **Example:** ```json { "project": "", "period": { "from": "", "to": "" }, "metrics": [ "" ], "group_by": [ "" ], "rows": [ { "additionalProperty": "anything" } ], "count": 1 } ``` ##### Status: 400 Invalid query (bad metric, group\_by, filter, or missing project) ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Discover event names and property keys - **Method:** `GET` - **Path:** `/properties` - **Tags:** Analytics Returns all event names with counts, and a list of known property keys from recent events. Useful for building dynamic filters and understanding what data is available. **CLI:** `npx @agent-analytics/cli properties my-site` #### Responses ##### Status: 200 Event names and property keys ###### Content-Type: application/json - **`events`** `array` **Items:** - **`count`** `integer` - **`event`** `string` - **`first_seen`** `string` - **`last_seen`** `string` - **`unique_users`** `integer` - **`project`** `string` - **`property_keys`** `array` **Items:** `string` **Example:** ```json { "project": "", "events": [ { "event": "page_view", "count": 1, "unique_users": 1, "first_seen": "", "last_seen": "" } ], "property_keys": [ "browser", "device", "hostname", "language", "os", "path", "referrer", "screen", "title", "url" ] } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Property keys by event name - **Method:** `GET` - **Path:** `/properties/received` - **Tags:** Analytics Returns which property keys appear on which event names, sampled from recent events. More detailed than `/properties` — shows the event-to-property mapping. #### Responses ##### Status: 200 Property-to-event mapping ###### Content-Type: application/json - **`project`** `string` - **`properties`** `array` **Items:** - **`event`** `string` - **`key`** `string` - **`sample_size`** `integer` - **`since`** `string` **Example:** ```json { "project": "", "sample_size": 5000, "since": "", "properties": [ { "key": "path", "event": "page_view" } ] } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### List sessions - **Method:** `GET` - **Path:** `/sessions` - **Tags:** Analytics Returns individual session records with duration, entry/exit pages, event count, and bounce status. Filter by user ID and/or bounce status. `is_bounce` is computed from event names at query time. `1` means the session has only non-interactive events: `page_view`, `$impression`, `$scroll_depth`, `$error`, `$time_on_page`, `$performance`, `$web_vitals`. #### Responses ##### Status: 200 Session list ###### Content-Type: application/json - **`project`** `string` - **`sessions`** `array` **Items:** - **`date`** `string` - **`duration`** `integer` — Duration in milliseconds - **`end_time`** `number` - **`entry_page`** `string | null` - **`event_count`** `integer` - **`exit_page`** `string | null` - **`is_bounce`** `integer`, possible values: `0, 1` — Computed from event names at query time. \`1\` means the session has only non-interactive events: \`page\_view\`, \`$impression\`, \`$scroll\_depth\`, \`$error\`, \`$time\_on\_page\`, \`$performance\`, \`$web\_vitals\`. - **`project_id`** `string` - **`session_id`** `string` - **`start_time`** `number` - **`user_id`** `string | null` **Example:** ```json { "project": "", "sessions": [ { "session_id": "", "user_id": null, "project_id": "", "start_time": 1, "end_time": 1, "duration": 1, "entry_page": null, "exit_page": null, "event_count": 1, "is_bounce": 0, "date": "" } ] } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Top property values - **Method:** `GET` - **Path:** `/breakdown` - **Tags:** Analytics Breaks down events by a specific property, showing the top values by count. Useful for top pages, referrers, browsers, countries, UTM sources, etc. Use `property=country` to see visitor geography (ISO 3166-1 alpha-2 codes). Country is stored as a dedicated column for fast queries. **CLI:** `npx @agent-analytics/cli breakdown my-site --property path --event page_view` #### Responses ##### Status: 200 Property breakdown ###### Content-Type: application/json - **`event`** `string | null` - **`project`** `string` - **`property`** `string` - **`total_events`** `integer` - **`total_with_property`** `integer` - **`values`** `array` **Items:** - **`count`** `integer` - **`unique_users`** `integer` - **`value`** `string` **Example:** ```json { "project": "", "property": "path", "event": null, "values": [ { "value": "/pricing", "count": 342, "unique_users": 201 } ], "total_events": 1, "total_with_property": 1 } ``` ##### Status: 400 Missing property parameter or invalid property key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Period-over-period comparison - **Method:** `GET` - **Path:** `/insights` - **Tags:** Analytics Compares the current period against the previous period of the same length. Returns change metrics and an overall trend indicator (`growing`, `stable`, `declining`). `bounce_rate` is computed from event names at query time. A session is a bounce when it has only non-interactive events: `page_view`, `$impression`, `$scroll_depth`, `$error`, `$time_on_page`, `$performance`, `$web_vitals`. **CLI:** `npx @agent-analytics/cli insights my-site --period 7d` #### Responses ##### Status: 200 Insights with trend ###### Content-Type: application/json - **`current_period`** `object` - **`from`** `string` - **`to`** `string` - **`metrics`** `object` - **`avg_duration`** `object` - **`change`** `number` - **`change_pct`** `integer | null` — Percentage change. Null when previous period was 0 but current is > 0. - **`current`** `number` - **`previous`** `number` - **`bounce_rate`** `object` - **`change`** `number` - **`change_pct`** `integer | null` — Percentage change. Null when previous period was 0 but current is > 0. - **`current`** `number` - **`previous`** `number` - **`total_events`** `object` - **`change`** `number` - **`change_pct`** `integer | null` — Percentage change. Null when previous period was 0 but current is > 0. - **`current`** `number` - **`previous`** `number` - **`total_sessions`** `object` - **`change`** `number` - **`change_pct`** `integer | null` — Percentage change. Null when previous period was 0 but current is > 0. - **`current`** `number` - **`previous`** `number` - **`unique_users`** `object` - **`change`** `number` - **`change_pct`** `integer | null` — Percentage change. Null when previous period was 0 but current is > 0. - **`current`** `number` - **`previous`** `number` - **`previous_period`** `object` - **`from`** `string` - **`to`** `string` - **`project`** `string` - **`trend`** `string`, possible values: `"growing", "stable", "declining"` **Example:** ```json { "project": "", "current_period": { "from": "", "to": "" }, "previous_period": { "from": "", "to": "" }, "metrics": { "total_events": { "current": 1, "previous": 1, "change": 1, "change_pct": null }, "unique_users": { "current": 1, "previous": 1, "change": 1, "change_pct": null }, "total_sessions": { "current": 1, "previous": 1, "change": 1, "change_pct": null }, "bounce_rate": { "current": 1, "previous": 1, "change": 1, "change_pct": null }, "avg_duration": { "current": 1, "previous": 1, "change": 1, "change_pct": null } }, "trend": "growing" } ``` ##### Status: 400 Invalid period ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Entry/exit page stats - **Method:** `GET` - **Path:** `/pages` - **Tags:** Analytics Returns top entry pages, exit pages, or both — with session count, bounce rate, and average duration per page. `bounce_rate` is computed from event names at query time. A session is a bounce when it has only non-interactive events: `page_view`, `$impression`, `$scroll_depth`, `$error`, `$time_on_page`, `$performance`, `$web_vitals`. **CLI:** `npx @agent-analytics/cli pages my-site --type entry` #### Responses ##### Status: 200 Page stats ###### Content-Type: application/json - **`entry_pages`** `array` **Items:** - **`avg_duration`** `number` — Average session duration in milliseconds - **`avg_events`** `number` - **`bounce_rate`** `number` — Bounce rate computed from event names at query time. A session is a bounce when it has only non-interactive events: \`page\_view\`, \`$impression\`, \`$scroll\_depth\`, \`$error\`, \`$time\_on\_page\`, \`$performance\`, \`$web\_vitals\`. - **`bounces`** `integer` - **`page`** `string` - **`sessions`** `integer` - **`exit_pages`** `array` **Items:** - **`avg_duration`** `number` — Average session duration in milliseconds - **`avg_events`** `number` - **`bounce_rate`** `number` — Bounce rate computed from event names at query time. A session is a bounce when it has only non-interactive events: \`page\_view\`, \`$impression\`, \`$scroll\_depth\`, \`$error\`, \`$time\_on\_page\`, \`$performance\`, \`$web\_vitals\`. - **`bounces`** `integer` - **`page`** `string` - **`sessions`** `integer` - **`project`** `string` **Example:** ```json { "project": "", "entry_pages": [ { "page": "/pricing", "sessions": 1, "bounces": 1, "bounce_rate": 0.45, "avg_duration": 1, "avg_events": 1 } ], "exit_pages": [ { "page": "/pricing", "sessions": 1, "bounces": 1, "bounce_rate": 0.45, "avg_duration": 1, "avg_events": 1 } ] } ``` ##### Status: 400 Invalid page type ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Session duration histogram - **Method:** `GET` - **Path:** `/sessions/distribution` - **Tags:** Analytics Returns a histogram of session durations in predefined buckets, with the median bucket and engaged session percentage (sessions >= 30 seconds). `bounces` in each bucket are computed from event names at query time. A session is a bounce when it has only non-interactive events: `page_view`, `$impression`, `$scroll_depth`, `$error`, `$time_on_page`, `$performance`, `$web_vitals`. **CLI:** `npx @agent-analytics/cli sessions distribution my-site` #### Responses ##### Status: 200 Session duration distribution ###### Content-Type: application/json - **`distribution`** `array` **Items:** - **`avg_events`** `number` - **`bounces`** `integer` — Number of bounce sessions in this duration bucket (computed from event names at query time). - **`bucket`** `string`, possible values: `"0s", "1-10s", "10-30s", "30-60s", "1-3m", "3-10m", "10m+"` - **`pct`** `number` — Percentage of total sessions - **`sessions`** `integer` - **`engaged_pct`** `number` — Percentage of sessions >= 30 seconds - **`median_bucket`** `string | null` - **`project`** `string` **Example:** ```json { "project": "", "distribution": [ { "bucket": "0s", "sessions": 1, "bounces": 1, "avg_events": 1, "pct": 1 } ], "median_bucket": null, "engaged_pct": 1 } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Day-of-week x hour traffic grid - **Method:** `GET` - **Path:** `/heatmap` - **Tags:** Analytics Returns a grid of events by day-of-week and hour-of-day (UTC), with peak detection. Useful for understanding when your users are most active. **CLI:** `npx @agent-analytics/cli heatmap my-site` #### Responses ##### Status: 200 Heatmap grid ###### Content-Type: application/json - **`busiest_day`** `string | null` - **`busiest_hour`** `integer | null` - **`heatmap`** `array` **Items:** - **`day`** `integer` — Day of week (0=Sunday, 6=Saturday) - **`day_name`** `string` - **`events`** `integer` - **`hour`** `integer` — Hour of day (0-23) - **`users`** `integer` - **`peak`** `object | null` - **`day`** `integer` - **`day_name`** `string` - **`events`** `integer` - **`hour`** `integer` - **`users`** `integer` - **`project`** `string` **Example:** ```json { "project": "", "heatmap": [ { "day": 1, "day_name": "Monday", "hour": 1, "events": 1, "users": 1 } ], "peak": { "day": 1, "day_name": "", "hour": 1, "events": 1, "users": 1 }, "busiest_day": "Monday", "busiest_hour": 14 } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Funnel analysis - **Method:** `POST` - **Path:** `/funnel` - **Tags:** Funnels Ad-hoc funnel analysis: track where users drop off across a sequence of 2-8 events. Each step can have optional property filters (e.g. only count `page_view` events where `path=/pricing`). The conversion window limits how long users have from entering step 1 to completing the final step. Step 1 is capped at 10,000 users to prevent unbounded scans. **CLI:** `npx @agent-analytics/cli funnel my-site --steps "page_view,signup,purchase" --window 168 --breakdown country` #### Request Body ##### Content-Type: application/json - **`project` (required)** `string` — Project name - **`steps` (required)** `array` — Funnel steps (2-8). Each step has an event name and optional property filters. **Items:** - **`event` (required)** `string` — Event name for this step - **`filters`** `array` — Optional property filters for this step **Items:** - **`op` (required)** `string`, possible values: `"eq", "neq", "contains"` — Filter operator - **`property` (required)** `string` — Property key (alphanumeric + underscores, max 128 chars) - **`value` (required)** `string` — Filter value - **`breakdown`** `string` — Optional property key to segment funnel by (e.g. 'variant', 'country'). Extracted from step 1 events only. - **`breakdown_limit`** `number`, default: `10` — Max breakdown groups, ordered by step 1 users descending (1-50, default 10) - **`conversion_window_hours`** `number`, default: `168` — Max hours from step 1 entry to final step (1-8760, default 168 = 7 days) - **`count_by`** `string`, possible values: `"user_id", "session_id"`, default: `"user_id"` — Count by unique users or sessions - **`since`** `string`, default: `"30d"` — Lookback period (ISO date or shorthand like '30d'). Default: 30d **Example:** ```json { "project": "my-site", "steps": [ { "event": "page_view", "filters": [ { "property": "path", "op": "eq", "value": "/pricing" } ] } ], "conversion_window_hours": 168, "since": "30d", "count_by": "user_id", "breakdown": "country", "breakdown_limit": 10 } ``` #### Responses ##### Status: 200 Funnel analysis results ###### Content-Type: application/json - **`breakdowns`** `array` — Per-group funnel results (only present when \`breakdown\` param is set). Ordered by step 1 users descending. **Items:** - **`overall_conversion_rate`** `number` — End-to-end conversion for this group - **`steps`** `array` — Same shape as top-level steps array **Items:** - **`avg_time_to_next_ms`** `number | null` - **`conversion_rate`** `number` - **`drop_off_rate`** `number` - **`event`** `string` - **`step`** `integer` - **`users`** `integer` - **`value`** `string | null` — Breakdown property value (null for events missing the property) - **`overall_conversion_rate`** `number` — Fraction of step 1 users that completed the final step - **`steps`** `array` **Items:** - **`avg_time_to_next_ms`** `number | null` — Average time in ms from this step to the next step (null for last step) - **`conversion_rate`** `number` — Fraction of users from previous step that reached this step (step 1 is always 1.0) - **`drop_off_rate`** `number` — Fraction of users lost from previous step (step 1 is always 0) - **`event`** `string` - **`step`** `integer` - **`users`** `integer` — Number of users (or sessions) that reached this step **Example:** ```json { "steps": [ { "step": 1, "event": "page_view", "users": 500, "conversion_rate": 0.6, "drop_off_rate": 0.4, "avg_time_to_next_ms": 3600000 } ], "overall_conversion_rate": 0.12, "breakdowns": [ { "value": "US", "steps": [ { "step": 1, "event": "", "users": 1, "conversion_rate": 1, "drop_off_rate": 1, "avg_time_to_next_ms": null } ], "overall_conversion_rate": 0.15 } ] } ``` ##### Status: 400 Validation error (invalid steps, filters, window, or count\_by) ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 404 Project not found ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Cohort retention analysis - **Method:** `GET` - **Path:** `/retention` - **Tags:** Analytics Track what percentage of users return over time using cohort analysis. "Of users who first appeared in week X, what % came back in subsequent weeks?" By default uses session-based retention — a user is "retained" if they have any return visit (session) in a subsequent period. When `event` is specified, switches to event-based retention and only counts that specific event for first-seen and return. Capped at 10,000 users per query. Older cohorts appear first (top-down). **CLI:** `npx @agent-analytics/cli retention my-site --period week --cohorts 8` #### Responses ##### Status: 200 Cohort retention data ###### Content-Type: application/json - **`average_rates`** `array` — Weighted average retention rate per period offset (weighted by cohort size) **Items:** `number` - **`cohorts`** `array` **Items:** - **`date`** `string`, format: `date` — Start date of this cohort period - **`rates`** `array` — Retention rate per period (retained\[i] / users, rounded to 3 decimals) **Items:** `number` - **`retained`** `array` — Users active in each subsequent period (index 0 = full cohort) **Items:** `integer` - **`users`** `integer` — Number of users who first appeared in this period - **`period`** `string`, possible values: `"day", "week", "month"` - **`users_analyzed`** `integer` — Total users across all cohorts (capped at 10K per query) **Example:** ```json { "period": "week", "cohorts": [ { "date": "2026-01-27", "users": 142, "retained": [ 142, 64, 55, 46 ], "rates": [ 1, 0.451, 0.387, 0.324 ] } ], "average_rates": [ 1, 0.441, 0.373, 0.324 ], "users_analyzed": 560 } ``` ##### Status: 400 Invalid period or cohorts value ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 404 Project not found ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Live event stream (SSE) - **Method:** `GET` - **Path:** `/stream` - **Tags:** Streaming Server-Sent Events stream of real-time events as they arrive. Events are delivered as `event: track` SSE messages. Optionally filter by event name and/or property values. The stream sends a `:heartbeat` comment every 30 seconds as a keepalive. Auto-disconnects after 30 minutes of no matching events. **curl example:** ```bash curl -N "https://api.agentanalytics.sh/stream?project=my-site&events=page_view,signup" \ -H "X-API-Key: aak_..." ``` #### Responses ##### Status: 200 SSE event stream ###### Content-Type: text/event-stream `string` — SSE stream with events: - \`event: connected\` — sent once on connection with active filters - \`event: track\` — each matching event with \`id\` for reconnection - \`:heartbeat\` — keepalive comment every 30s **Example:** ```json true ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 404 Project not found ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 429 Too many concurrent streams (max 10 per account) ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Live snapshot (real-time) - **Method:** `GET` - **Path:** `/live` - **Tags:** Streaming Returns a point-in-time snapshot of active visitors, sessions, events per minute, top pages, top events, and recent events. Reads from an in-memory ring buffer — no D1 query. Data is available only while events are being streamed (the ring buffer evicts events older than 5 minutes). **curl example:** ```bash curl "https://api.agentanalytics.sh/live?project=my-site&window=60" \ -H "X-API-Key: aak_..." ``` #### Responses ##### Status: 200 Live snapshot ###### Content-Type: application/json - **`active_sessions`** `integer` — Unique session\_ids in the time window - **`active_visitors`** `integer` — Unique user\_ids in the time window - **`events_per_minute`** `integer` - **`project`** `string` - **`recent_events`** `array` — Last 10 events, newest first **Items:** - **`event`** `string` - **`properties`** `object | null` - **`timestamp`** `number` - **`user_id`** `string | null` - **`timestamp`** `number` - **`top_events`** `array` **Items:** - **`count`** `integer` - **`event`** `string` - **`top_pages`** `array` **Items:** - **`path`** `string` - **`visitors`** `integer` - **`window_seconds`** `integer` **Example:** ```json { "project": "my-site", "window_seconds": 60, "timestamp": 1708000060000, "active_visitors": 12, "active_sessions": 8, "events_per_minute": 47, "top_pages": [ { "path": "/", "visitors": 5 } ], "top_events": [ { "event": "page_view", "count": 32 } ], "recent_events": [ { "event": "", "properties": null, "user_id": null, "timestamp": 1 } ] } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 404 Project not found ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### List all projects - **Method:** `GET` - **Path:** `/projects` - **Tags:** Projects Returns all projects under your account with event/read counts and tier info. **CLI:** `npx @agent-analytics/cli projects` #### Responses ##### Status: 200 Project list ###### Content-Type: application/json - **`has_api_key`** `boolean` - **`projects`** `array` **Items:** - **`allowed_origins`** `string` - **`id`** `string` - **`name`** `string` - **`project_token`** `string` - **`total_events`** `integer` - **`total_reads`** `integer` - **`tier`** `string`, possible values: `"free", "pro"` **Example:** ```json { "projects": [ { "id": "", "name": "my-site", "project_token": "aat_...", "allowed_origins": "*", "total_events": 1, "total_reads": 1 } ], "has_api_key": true, "tier": "free" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Create a new project - **Method:** `POST` - **Path:** `/projects` - **Tags:** Projects Creates a new project and provisions a D1 database if needed. Returns the project token and a ready-to-use tracking snippet. Project names must be alphanumeric with hyphens, underscores, or dots (max 64 chars). If a project with the same name already exists, returns the existing project (idempotent). **CLI:** `npx @agent-analytics/cli init my-new-site` #### Request Body ##### Content-Type: application/json - **`name` (required)** `string` — Project name (alphanumeric, hyphens, underscores, dots; max 64 chars) - **`allowed_origins`** `string`, default: `"*"` — Allowed origins for CORS. Use \`\*\` for any origin. **Example:** ```json { "name": "my-new-site", "allowed_origins": "https://example.com" } ``` #### Responses ##### Status: 200 Project already exists (returned existing project) ###### Content-Type: application/json - **`existing`** `boolean` - **`id`** `string` - **`name`** `string` - **`project_token`** `string` **Example:** ```json { "id": "", "name": "", "project_token": "", "existing": true } ``` ##### Status: 201 Project created ###### Content-Type: application/json - **`allowed_origins`** `string` - **`api_example`** `string` — Example curl command for querying stats - **`id`** `string` - **`name`** `string` - **`project_token`** `string` - **`snippet`** `string` — Ready-to-use HTML tracking snippet **Example:** ```json { "id": "", "name": "", "project_token": "aat_...", "allowed_origins": "", "snippet": "", "api_example": "" } ``` ##### Status: 400 Missing name or invalid project name ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 409 Duplicate origin ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Get project details - **Method:** `GET` - **Path:** `/projects/{id}` - **Tags:** Projects Returns a single project with its configuration and today's usage stats. #### Responses ##### Status: 200 Project details ###### Content-Type: application/json - **`allowed_origins`** `string` - **`id`** `string` - **`name`** `string` - **`project_token`** `string` - **`usage_today`** `object` **Example:** ```json { "id": "", "name": "", "project_token": "", "allowed_origins": "", "usage_today": {} } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 403 Not your project ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 404 Project not found ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Update a project - **Method:** `PATCH` - **Path:** `/projects/{id}` - **Tags:** Projects Update a project's name and/or allowed origins. **Note:** Project name cannot be changed after events have been recorded. #### Request Body ##### Content-Type: application/json - **`allowed_origins`** `string` — New allowed origins - **`name`** `string` — New project name **Example:** ```json { "name": "", "allowed_origins": "" } ``` #### Responses ##### Status: 200 Updated project ###### Content-Type: application/json - **`allowed_origins`** `string` - **`id`** `string` - **`name`** `string` - **`project_token`** `string` **Example:** ```json { "id": "", "name": "", "project_token": "", "allowed_origins": "" } ``` ##### Status: 400 Invalid name or no fields to update ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 409 Name locked (events recorded) or duplicate name/origin ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Delete a project - **Method:** `DELETE` - **Path:** `/projects/{id}` - **Tags:** Projects Soft-deletes a project. Revokes the project token (no new events can be written). Event data is preserved in the database but no longer accessible via API. #### Responses ##### Status: 200 Project deleted ###### Content-Type: application/json - **`deleted`** `string` — Project ID - **`ok`** `boolean` **Example:** ```json { "ok": true, "deleted": "" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 403 Not your project ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 404 Project not found ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Get account info - **Method:** `GET` - **Path:** `/account` - **Tags:** Account Returns your account details, tier, limits, and project count. **CLI:** `npx @agent-analytics/cli whoami` #### Responses ##### Status: 200 Account info ###### Content-Type: application/json - **`created_at`** `string` - **`email`** `string` - **`github_login`** `string | null` - **`id`** `string` - **`monthly_spend_cap_dollars`** `number | null` - **`projects_count`** `integer` - **`tier`** `string`, possible values: `"free", "pro"` - **`tier_limits`** `object` - **`max_events_per_month`** `integer` - **`max_projects`** `integer` - **`max_reads_per_month`** `integer` - **`rate_limit_rpm`** `integer` - **`retention_days`** `integer` **Example:** ```json { "id": "acc_free123", "email": "free@example.com", "github_login": "freeuser", "tier": "free", "created_at": "2026-02-01T12:00:00.000Z", "projects_count": 2, "tier_limits": { "max_events_per_month": 100000, "max_projects": 2, "max_reads_per_month": 500, "retention_days": 90, "rate_limit_rpm": 10 }, "monthly_spend_cap_dollars": null } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Filtered automated traffic for one project - **Method:** `GET` - **Path:** `/bot-traffic` - **Tags:** Analytics Returns a project-scoped summary of automated traffic that reached Agent Analytics and was filtered out of normal analytics. This endpoint reports daily aggregates only: automated request count, dropped event count, category breakdown, top actors, and a zero-filled time series for the selected period. This is **not** full site bot visibility like CDN logs. It only includes automated requests that actually reached `POST /track` or `POST /track/batch`. **CLI:** `npx @agent-analytics/cli bot-traffic my-site --period 7d --limit 5` **MCP:** `bot_traffic_overview` #### Responses ##### Status: 200 Project bot traffic overview ###### Content-Type: application/json - **`actors`** `array` **Items:** - **`actor`** `string` - **`category`** `string` - **`dropped_events`** `integer` - **`last_seen_at`** `object` - **`requests`** `integer` - **`categories`** `array` **Items:** - **`category`** `string` - **`dropped_events`** `integer` - **`requests`** `integer` - **`share_pct`** `number` - **`period`** `object` - **`from`** `string`, format: `date` - **`label`** `string`, possible values: `"1d", "7d", "14d", "30d", "90d"` - **`previous_from`** `string`, format: `date` - **`previous_to`** `string`, format: `date` - **`to`** `string`, format: `date` - **`project`** `string` - **`scope`** `string`, possible values: `"project"` - **`summary`** `object` - **`automated_requests`** `object` - **`change`** `integer` - **`change_pct`** `object` - **`current`** `integer` - **`previous`** `integer` - **`dropped_events`** `object` - **`change`** `integer` - **`change_pct`** `object` - **`current`** `integer` - **`previous`** `integer` - **`last_seen_at`** `object` - **`time_series`** `array` **Items:** - **`date`** `string`, format: `date` - **`dropped_events`** `integer` - **`requests`** `integer` **Example:** ```json { "scope": "project", "project": "my-site", "period": { "label": "7d", "from": "2026-03-03", "to": "2026-03-09", "previous_from": "2026-02-24", "previous_to": "2026-03-02" }, "summary": { "automated_requests": { "current": 5, "previous": 1, "change": 4, "change_pct": 400 }, "dropped_events": { "current": 5, "previous": 1, "change": 4, "change_pct": 400 }, "last_seen_at": 1773459600000 }, "categories": [ { "category": "ai_agent", "requests": 3, "dropped_events": 5, "share_pct": 60 } ], "actors": [ { "actor": "ChatGPT-User", "category": "ai_agent", "requests": 3, "dropped_events": 5, "last_seen_at": 1773459600000 } ], "time_series": [ { "date": "2026-03-13", "requests": 5, "dropped_events": 7 } ] } ``` ##### Status: 400 Invalid period or missing project ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 404 Project not found or unauthorized ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Historical all-sites overview - **Method:** `GET` - **Path:** `/account/all-sites` - **Tags:** Account Returns a lightweight historical summary across all active projects in your account. This endpoint is optimized for a single account-level dashboard view: total event trend, active project count, daily event time series, and a top-project list. Use `/live` for real-time account-wide activity. Use project-scoped endpoints like `/stats` and `/insights` when you want deeper analysis for one project. **CLI:** `npx @agent-analytics/cli all-sites --period 7d` #### Responses ##### Status: 200 Account-wide historical overview ###### Content-Type: application/json - **`period`** `object` - **`from`** `string`, format: `date` - **`label`** `string`, possible values: `"1d", "7d", "14d", "30d", "90d"` - **`previous_from`** `string`, format: `date` - **`previous_to`** `string`, format: `date` - **`to`** `string`, format: `date` - **`projects`** `array` **Items:** - **`events`** `integer` - **`id`** `string` - **`last_active_date`** `object` - **`name`** `string` - **`share_pct`** `number` - **`remaining_projects`** `integer` - **`scope`** `string`, possible values: `"account"` - **`summary`** `object` - **`active_projects`** `integer` - **`total_events`** `object` - **`change`** `integer` - **`change_pct`** `object` - **`current`** `integer` - **`previous`** `integer` - **`total_projects`** `integer` - **`time_series`** `array` **Items:** - **`date`** `string`, format: `date` - **`events`** `integer` **Example:** ```json { "scope": "account", "period": { "label": "7d", "from": "2026-03-03", "to": "2026-03-09", "previous_from": "2026-02-24", "previous_to": "2026-03-02" }, "summary": { "total_projects": 3, "active_projects": 2, "total_events": { "current": 120, "previous": 90, "change": 30, "change_pct": 33 } }, "time_series": [ { "date": "2026-03-09", "events": 40 } ], "projects": [ { "id": "proj_123", "name": "my-site", "events": 80, "share_pct": 66.7, "last_active_date": "2026-03-09" } ], "remaining_projects": 1 } ``` ##### Status: 400 Invalid period ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Filtered automated traffic across all active projects - **Method:** `GET` - **Path:** `/account/bot-traffic` - **Tags:** Account Returns an account-scoped summary of automated traffic that reached Agent Analytics and was filtered out of normal analytics. This endpoint is optimized for a lightweight all-sites view: total filtered requests, dropped events, active-project count, top-project list, category breakdown, and a zero-filled daily time series. Deleted projects are excluded from the totals and rankings. **CLI:** `npx @agent-analytics/cli bot-traffic --all --period 7d --limit 10` **MCP:** `all_sites_bot_traffic` #### Responses ##### Status: 200 Account-wide bot traffic overview ###### Content-Type: application/json - **`categories`** `array` **Items:** - **`category`** `string` - **`dropped_events`** `integer` - **`requests`** `integer` - **`share_pct`** `number` - **`period`** `object` - **`from`** `string`, format: `date` - **`label`** `string`, possible values: `"1d", "7d", "14d", "30d", "90d"` - **`previous_from`** `string`, format: `date` - **`previous_to`** `string`, format: `date` - **`to`** `string`, format: `date` - **`projects`** `array` **Items:** - **`dropped_events`** `integer` - **`id`** `string` - **`last_seen_at`** `object` - **`name`** `string` - **`requests`** `integer` - **`share_pct`** `number` - **`remaining_projects`** `integer` - **`scope`** `string`, possible values: `"account"` - **`summary`** `object` - **`active_projects`** `integer` - **`automated_requests`** `object` - **`change`** `integer` - **`change_pct`** `object` - **`current`** `integer` - **`previous`** `integer` - **`dropped_events`** `object` - **`change`** `integer` - **`change_pct`** `object` - **`current`** `integer` - **`previous`** `integer` - **`last_seen_at`** `object` - **`total_projects`** `integer` - **`time_series`** `array` **Items:** - **`date`** `string`, format: `date` - **`dropped_events`** `integer` - **`requests`** `integer` **Example:** ```json { "scope": "account", "period": { "label": "7d", "from": "2026-03-03", "to": "2026-03-09", "previous_from": "2026-02-24", "previous_to": "2026-03-02" }, "summary": { "automated_requests": { "current": 5, "previous": 1, "change": 4, "change_pct": 400 }, "dropped_events": { "current": 5, "previous": 1, "change": 4, "change_pct": 400 }, "active_projects": 1, "total_projects": 4, "last_seen_at": 1773459600000 }, "categories": [ { "category": "ai_agent", "requests": 3, "dropped_events": 5, "share_pct": 60 } ], "projects": [ { "id": "proj_123", "name": "my-site", "requests": 3, "dropped_events": 4, "share_pct": 60, "last_seen_at": 1773459600000 } ], "remaining_projects": 3, "time_series": [ { "date": "2026-03-13", "requests": 5, "dropped_events": 7 } ] } ``` ##### Status: 400 Invalid period ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Revoke and regenerate API key - **Method:** `POST` - **Path:** `/account/revoke-key` - **Tags:** Account Revokes the current API key and generates a new one. The old key stops working immediately. The new key is returned in the response — this is the only time it's shown. #### Responses ##### Status: 200 New API key ###### Content-Type: application/json - **`api_key`** `string` — The new API key. Save this — it won't be shown again. - **`ok`** `boolean` **Example:** ```json { "ok": true, "api_key": "aak_..." } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Get experiment config for tracker.js - **Method:** `GET` - **Path:** `/experiments/config` - **Tags:** Experiments Returns active experiments for a project, used by `tracker.js` to assign variants client-side. This is a **public** endpoint — authenticated by project token (same as `/track`), not API key. You don't call this directly — the tracker fetches it automatically on page load. #### Responses ##### Status: 200 Active experiments for this project ###### Content-Type: application/json - **`experiments`** `array` **Items:** - **`key`** `string` - **`variants`** `array` **Items:** - **`key`** `string` - **`weight`** `integer` **Example:** ```json { "experiments": [ { "key": "signup_cta", "variants": [ { "key": "", "weight": 1 } ] } ] } ``` ##### Status: 400 Missing token ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Create an A/B experiment - **Method:** `POST` - **Path:** `/experiments` - **Tags:** Experiments Creates a new experiment for the specified project. **Pro tier only.** Variants are assigned client-side by `tracker.js` using a deterministic hash — same user always gets the same variant. Exposure events (`$experiment_exposure`) are tracked automatically. **CLI:** `npx @agent-analytics/cli experiments create my-site --name signup_cta --variants control,new_cta --goal signup` #### Request Body ##### Content-Type: application/json - **`goal_event` (required)** `string` — Event name to measure conversion (max 256 chars) - **`name` (required)** `string` — Experiment name (alphanumeric, hyphens, underscores; max 64 chars) - **`project` (required)** `string` — Project name - **`variants` (required)** `array` — 2-4 variant keys **Items:** `string` - **`weights`** `array` — Optional traffic weights per variant (must sum to 100). Defaults to equal split. **Items:** `integer` **Example:** ```json { "project": "my-site", "name": "signup_cta", "variants": [ "control", "new_cta" ], "goal_event": "signup", "weights": [ 50, 50 ] } ``` #### Responses ##### Status: 201 Experiment created ###### Content-Type: application/json - **`account_id`** `string` - **`completed_at`** `string | null` - **`created_at`** `string` - **`goal_event`** `string` - **`id`** `string` - **`name`** `string` - **`project_id`** `string` - **`status`** `string`, possible values: `"active", "paused", "completed"` - **`updated_at`** `string` - **`variants`** `array` **Items:** - **`key`** `string` - **`weight`** `integer` - **`winner`** `string | null` — Variant key, set when status is completed **Example:** ```json { "id": "exp_a1b2c3d4e5f6g7h8", "project_id": "", "account_id": "", "name": "signup_cta", "variants": [ { "key": "control", "weight": 50 } ], "goal_event": "signup", "status": "active", "winner": null, "created_at": "", "updated_at": "", "completed_at": null } ``` ##### Status: 400 Validation error (missing fields, invalid variants, duplicate name) ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 403 Pro tier required ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 404 Project not found ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 409 Experiment name already exists in this project ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### List experiments - **Method:** `GET` - **Path:** `/experiments` - **Tags:** Experiments Returns all experiments for a project (active, paused, and completed). **CLI:** `npx @agent-analytics/cli experiments list my-site` #### Responses ##### Status: 200 Experiment list ###### Content-Type: application/json - **`experiments`** `array` **Items:** - **`account_id`** `string` - **`completed_at`** `string | null` - **`created_at`** `string` - **`goal_event`** `string` - **`id`** `string` - **`name`** `string` - **`project_id`** `string` - **`status`** `string`, possible values: `"active", "paused", "completed"` - **`updated_at`** `string` - **`variants`** `array` **Items:** - **`key`** `string` - **`weight`** `integer` - **`winner`** `string | null` — Variant key, set when status is completed **Example:** ```json { "experiments": [ { "id": "exp_a1b2c3d4e5f6g7h8", "project_id": "", "account_id": "", "name": "signup_cta", "variants": [ { "key": "control", "weight": 50 } ], "goal_event": "signup", "status": "active", "winner": null, "created_at": "", "updated_at": "", "completed_at": null } ] } ``` ##### Status: 400 Missing project parameter ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 404 Project not found ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Get experiment with live results - **Method:** `GET` - **Path:** `/experiments/{id}` - **Tags:** Experiments Returns experiment config plus live Bayesian A/B test results. Results include `probability_best` (probability each variant is the winner), `lift` (relative improvement over baseline), `sufficient_data` (whether enough exposures exist), and a human-readable `recommendation`. The system needs \~100 exposures per variant before results are statistically significant. **CLI:** `npx @agent-analytics/cli experiments get exp_abc123` #### Responses ##### Status: 200 Experiment with results ###### Content-Type: application/json **All of:** - **`account_id`** `string` - **`completed_at`** `string | null` - **`created_at`** `string` - **`goal_event`** `string` - **`id`** `string` - **`name`** `string` - **`project_id`** `string` - **`status`** `string`, possible values: `"active", "paused", "completed"` - **`updated_at`** `string` - **`variants`** `array` **Items:** - **`key`** `string` - **`weight`** `integer` - **`winner`** `string | null` — Variant key, set when status is completed * **`results`** `object` - **`lift`** `object` - **`probability_best`** `object` - **`recommendation`** `string` - **`sufficient_data`** `boolean` - **`variants`** `array` **Items:** - **`conversion_rate`** `number` - **`conversions`** `integer` - **`exposures`** `integer` - **`key`** `string` - **`unique_users`** `integer` **Example:** ```json { "id": "exp_a1b2c3d4e5f6g7h8", "project_id": "", "account_id": "", "name": "signup_cta", "variants": [ { "key": "control", "weight": 50 } ], "goal_event": "signup", "status": "active", "winner": null, "created_at": "", "updated_at": "", "completed_at": null, "results": { "variants": [ { "key": "control", "exposures": 520, "unique_users": 480, "conversions": 48, "conversion_rate": 0.1 } ], "probability_best": { "control": 0.06, "new_cta": 0.94 }, "lift": { "control": -0.32, "new_cta": 0.47 }, "sufficient_data": true, "recommendation": "'new_cta' is the winner with 94% probability (47% lift over baseline)" } } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 403 Not your experiment ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 404 Experiment not found ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Update experiment status - **Method:** `PATCH` - **Path:** `/experiments/{id}` - **Tags:** Experiments Update an experiment's status. Valid transitions: - `active` → `paused` or `completed` - `paused` → `active` or `completed` - `completed` is terminal (no further changes) When completing, optionally set `winner` to a variant key. **CLI:** - `npx @agent-analytics/cli experiments pause exp_abc123` - `npx @agent-analytics/cli experiments resume exp_abc123` - `npx @agent-analytics/cli experiments complete exp_abc123 --winner new_cta` #### Request Body ##### Content-Type: application/json - **`status` (required)** `string`, possible values: `"active", "paused", "completed"` — New status - **`winner`** `string` — Variant key to declare as winner (only when completing) **Example:** ```json { "status": "active", "winner": "" } ``` #### Responses ##### Status: 200 Updated experiment ###### Content-Type: application/json - **`account_id`** `string` - **`completed_at`** `string | null` - **`created_at`** `string` - **`goal_event`** `string` - **`id`** `string` - **`name`** `string` - **`project_id`** `string` - **`status`** `string`, possible values: `"active", "paused", "completed"` - **`updated_at`** `string` - **`variants`** `array` **Items:** - **`key`** `string` - **`weight`** `integer` - **`winner`** `string | null` — Variant key, set when status is completed **Example:** ```json { "id": "exp_a1b2c3d4e5f6g7h8", "project_id": "", "account_id": "", "name": "signup_cta", "variants": [ { "key": "control", "weight": 50 } ], "goal_event": "signup", "status": "active", "winner": null, "created_at": "", "updated_at": "", "completed_at": null } ``` ##### Status: 400 Invalid status transition or missing status ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 403 Not your experiment ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 404 Experiment not found ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### Delete an experiment - **Method:** `DELETE` - **Path:** `/experiments/{id}` - **Tags:** Experiments Permanently deletes an experiment. Event data (exposures, conversions) is preserved in the events table — only the experiment config is removed. **CLI:** `npx @agent-analytics/cli experiments delete exp_abc123` #### Responses ##### Status: 200 Experiment deleted ###### Content-Type: application/json - **`deleted`** `string` — Experiment ID - **`ok`** `boolean` **Example:** ```json { "ok": true, "deleted": "" } ``` ##### Status: 401 Missing or invalid API key ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 403 Not your experiment ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ##### Status: 404 Experiment not found ###### Content-Type: application/json - **`error` (required)** `string` — Machine-readable error code - **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### JavaScript tracker script - **Method:** `GET` - **Path:** `/tracker.js` - **Tags:** Tracking Returns the lightweight JavaScript tracker used for browser-side analytics. Embed it in your HTML: ```html ``` Required attributes: - `data-project`: project name - `data-token`: project token (`aat_*`) The tracker auto-collects page URL, pathname, referrer, browser, OS, device, language, timezone, UTM parameters, session count, and first-touch attribution. SPA routing is detected automatically. No cookies are required. Full tracker documentation now lives in the docs site: #### Responses ##### Status: 200 JavaScript file ###### Content-Type: application/javascript `string` **Example:** ```json true ``` ##### Status: 404 Not found ## Schemas ### Error - **Type:**`object` * **`error` (required)** `string` — Machine-readable error code * **`message` (required)** `string` — Human-readable error message **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required" } ``` ### TrackRequest - **Type:**`object` * **`event` (required)** `string` — Event name (max 256 chars) * **`token` (required)** `string` — Project token (\`aat\_\*\`) * **`properties`** `object | null` — Arbitrary key-value properties (max 8KB JSON). The tracker auto-populates \`path\`, \`url\`, \`hostname\`, \`title\`, \`referrer\`, \`screen\`, \`browser\`, \`browser\_version\`, \`os\`, \`device\`, \`language\`, \`timezone\` (IANA), \`utm\_\*\` params, \`session\_count\`, \`days\_since\_first\_visit\`, and \`first\_utm\_\*\` (first-touch attribution). * **`session_id`** `string | null` — Session identifier (max 256 chars). The tracker auto-generates one via sessionStorage with 30-min inactivity timeout. * **`timestamp`** `number | null` — Unix timestamp in milliseconds. Defaults to \`Date.now()\`. Must be within 30 days ago to 5 minutes in the future. * **`user_id`** `string | null` — Anonymous user identifier (max 256 chars). The tracker auto-generates one via localStorage. **Example:** ```json { "token": "aat_51da22cdcab084ae1cdb50fd4841c642f0dafdb1d9adfa6b", "event": "page_view", "properties": { "path": "/pricing", "referrer": "https://google.com", "browser": "Chrome", "os": "macOS" }, "user_id": "usr_abc123", "session_id": "ses_xyz789", "timestamp": 1706745600000 } ``` ### TrackBatchRequest - **Type:**`object` * **`events` (required)** `array` — Array of events (max 100 per batch) **Items:** - **`event` (required)** `string` - **`token` (required)** `string` — Project token (\`aat\_\*\`). All events must use the same token. - **`properties`** `object | null` - **`session_id`** `string | null` - **`timestamp`** `number | null` - **`user_id`** `string | null` **Example:** ```json { "events": [ { "token": "aat_51da22cdcab084ae1cdb50fd4841c642f0dafdb1d9adfa6b", "event": "page_view", "properties": null, "user_id": null, "session_id": null, "timestamp": null } ] } ``` ### StatsResponse - **Type:**`object` * **`events`** `array` — Top event names by count (max 20) **Items:** - **`count`** `integer` - **`event`** `string` - **`unique_users`** `integer` * **`period`** `object` - **`from`** `string` - **`groupBy`** `string`, possible values: `"hour", "day", "week", "month"` - **`to`** `string` * **`project`** `string` * **`sessions`** `object` - **`avg_duration`** `integer` — Average session duration in milliseconds - **`bounce_rate`** `number` — Bounce rate computed from event names at query time. A session is a bounce when it has only non-interactive events: \`page\_view\`, \`$impression\`, \`$scroll\_depth\`, \`$error\`, \`$time\_on\_page\`, \`$performance\`, \`$web\_vitals\`. - **`pages_per_session`** `number` - **`sessions_per_user`** `number` - **`total_sessions`** `integer` * **`timeSeries`** `array` **Items:** - **`bucket`** `string` - **`total_events`** `integer` - **`unique_users`** `integer` * **`totals`** `object` - **`total_events`** `integer` - **`unique_users`** `integer` **Example:** ```json { "project": "my-site", "period": { "from": "2025-01-01", "to": "2025-01-07", "groupBy": "day" }, "totals": { "unique_users": 142, "total_events": 1247 }, "timeSeries": [ { "bucket": "2025-01-01", "unique_users": 1, "total_events": 1 } ], "events": [ { "event": "page_view", "count": 1, "unique_users": 1 } ], "sessions": { "total_sessions": 1, "bounce_rate": 0.45, "avg_duration": 1, "pages_per_session": 1, "sessions_per_user": 1 } } ``` ### EventRow - **Type:**`object` * **`country`** `string | null` — ISO 3166-1 alpha-2 country code derived from the request IP at ingestion time * **`date`** `string` * **`event`** `string` * **`id`** `string` * **`project_id`** `string` * **`properties`** `object | null` * **`session_id`** `string | null` * **`timestamp`** `number` * **`user_id`** `string | null` **Example:** ```json { "id": "", "project_id": "", "event": "", "properties": null, "user_id": null, "session_id": null, "timestamp": 1, "date": "", "country": "US" } ``` ### QueryRequest - **Type:**`object` * **`project` (required)** `string` — Project name * **`date_from`** `string` — Start date (ISO 8601 or \`Nd\` shorthand). Defaults to 7 days ago. * **`date_to`** `string` — End date (ISO 8601). Defaults to today. * **`filters`** `array` **Items:** - **`field`** `string` — Field to filter on. Built-in: \`event\`, \`user\_id\`, \`date\`, \`country\`, \`session\_id\`, \`timestamp\`. Property fields: \`properties.path\`, \`properties.browser\`, etc. - **`op`** `string`, possible values: `"eq", "neq", "gt", "lt", "gte", "lte", "contains"` - **`value`** `object` — Value to compare against * **`group_by`** `array`, default: `[]` — Fields to group results by **Items:** `string`, possible values: `"event", "date", "user_id", "session_id", "country"` * **`limit`** `integer`, default: `100` * **`metrics`** `array`, default: `["event_count"]` — Metrics to compute **Items:** `string`, possible values: `"event_count", "unique_users", "session_count", "bounce_rate", "avg_duration"` * **`order`** `string`, possible values: `"asc", "desc"`, default: `"desc"` * **`order_by`** `string`, possible values: `"event_count", "unique_users", "session_count", "date", "event"` — Field to sort by **Example:** ```json { "project": "my-site", "metrics": [ "event_count" ], "group_by": [], "filters": [ { "field": "event", "op": "eq", "value": "page_view" } ], "date_from": "2025-01-01", "date_to": "2025-01-07", "order_by": "event_count", "order": "desc", "limit": 100 } ``` ### QueryResponse - **Type:**`object` * **`count`** `integer` * **`group_by`** `array` **Items:** `string` * **`metrics`** `array` **Items:** `string` * **`period`** `object` - **`from`** `string` - **`to`** `string` * **`project`** `string` * **`rows`** `array` **Items:** **Example:** ```json { "project": "", "period": { "from": "", "to": "" }, "metrics": [ "" ], "group_by": [ "" ], "rows": [ { "additionalProperty": "anything" } ], "count": 1 } ``` ### SessionRow - **Type:**`object` * **`date`** `string` * **`duration`** `integer` — Duration in milliseconds * **`end_time`** `number` * **`entry_page`** `string | null` * **`event_count`** `integer` * **`exit_page`** `string | null` * **`is_bounce`** `integer`, possible values: `0, 1` — Computed from event names at query time. \`1\` means the session has only non-interactive events: \`page\_view\`, \`$impression\`, \`$scroll\_depth\`, \`$error\`, \`$time\_on\_page\`, \`$performance\`, \`$web\_vitals\`. * **`project_id`** `string` * **`session_id`** `string` * **`start_time`** `number` * **`user_id`** `string | null` **Example:** ```json { "session_id": "", "user_id": null, "project_id": "", "start_time": 1, "end_time": 1, "duration": 1, "entry_page": null, "exit_page": null, "event_count": 1, "is_bounce": 0, "date": "" } ``` ### BreakdownResponse - **Type:**`object` * **`event`** `string | null` * **`project`** `string` * **`property`** `string` * **`total_events`** `integer` * **`total_with_property`** `integer` * **`values`** `array` **Items:** - **`count`** `integer` - **`unique_users`** `integer` - **`value`** `string` **Example:** ```json { "project": "", "property": "path", "event": null, "values": [ { "value": "/pricing", "count": 342, "unique_users": 201 } ], "total_events": 1, "total_with_property": 1 } ``` ### InsightsResponse - **Type:**`object` * **`current_period`** `object` - **`from`** `string` - **`to`** `string` * **`metrics`** `object` - **`avg_duration`** `object` - **`change`** `number` - **`change_pct`** `integer | null` — Percentage change. Null when previous period was 0 but current is > 0. - **`current`** `number` - **`previous`** `number` - **`bounce_rate`** `object` - **`change`** `number` - **`change_pct`** `integer | null` — Percentage change. Null when previous period was 0 but current is > 0. - **`current`** `number` - **`previous`** `number` - **`total_events`** `object` - **`change`** `number` - **`change_pct`** `integer | null` — Percentage change. Null when previous period was 0 but current is > 0. - **`current`** `number` - **`previous`** `number` - **`total_sessions`** `object` - **`change`** `number` - **`change_pct`** `integer | null` — Percentage change. Null when previous period was 0 but current is > 0. - **`current`** `number` - **`previous`** `number` - **`unique_users`** `object` - **`change`** `number` - **`change_pct`** `integer | null` — Percentage change. Null when previous period was 0 but current is > 0. - **`current`** `number` - **`previous`** `number` * **`previous_period`** `object` - **`from`** `string` - **`to`** `string` * **`project`** `string` * **`trend`** `string`, possible values: `"growing", "stable", "declining"` **Example:** ```json { "project": "", "current_period": { "from": "", "to": "" }, "previous_period": { "from": "", "to": "" }, "metrics": { "total_events": { "current": 1, "previous": 1, "change": 1, "change_pct": null }, "unique_users": { "current": 1, "previous": 1, "change": 1, "change_pct": null }, "total_sessions": { "current": 1, "previous": 1, "change": 1, "change_pct": null }, "bounce_rate": { "current": 1, "previous": 1, "change": 1, "change_pct": null }, "avg_duration": { "current": 1, "previous": 1, "change": 1, "change_pct": null } }, "trend": "growing" } ``` ### DeltaMetric - **Type:**`object` * **`change`** `number` * **`change_pct`** `integer | null` — Percentage change. Null when previous period was 0 but current is > 0. * **`current`** `number` * **`previous`** `number` **Example:** ```json { "current": 1, "previous": 1, "change": 1, "change_pct": null } ``` ### PagesResponse - **Type:**`object` * **`entry_pages`** `array` **Items:** - **`avg_duration`** `number` — Average session duration in milliseconds - **`avg_events`** `number` - **`bounce_rate`** `number` — Bounce rate computed from event names at query time. A session is a bounce when it has only non-interactive events: \`page\_view\`, \`$impression\`, \`$scroll\_depth\`, \`$error\`, \`$time\_on\_page\`, \`$performance\`, \`$web\_vitals\`. - **`bounces`** `integer` - **`page`** `string` - **`sessions`** `integer` * **`exit_pages`** `array` **Items:** - **`avg_duration`** `number` — Average session duration in milliseconds - **`avg_events`** `number` - **`bounce_rate`** `number` — Bounce rate computed from event names at query time. A session is a bounce when it has only non-interactive events: \`page\_view\`, \`$impression\`, \`$scroll\_depth\`, \`$error\`, \`$time\_on\_page\`, \`$performance\`, \`$web\_vitals\`. - **`bounces`** `integer` - **`page`** `string` - **`sessions`** `integer` * **`project`** `string` **Example:** ```json { "project": "", "entry_pages": [ { "page": "/pricing", "sessions": 1, "bounces": 1, "bounce_rate": 0.45, "avg_duration": 1, "avg_events": 1 } ], "exit_pages": [ { "page": "/pricing", "sessions": 1, "bounces": 1, "bounce_rate": 0.45, "avg_duration": 1, "avg_events": 1 } ] } ``` ### PageRow - **Type:**`object` * **`avg_duration`** `number` — Average session duration in milliseconds * **`avg_events`** `number` * **`bounce_rate`** `number` — Bounce rate computed from event names at query time. A session is a bounce when it has only non-interactive events: \`page\_view\`, \`$impression\`, \`$scroll\_depth\`, \`$error\`, \`$time\_on\_page\`, \`$performance\`, \`$web\_vitals\`. * **`bounces`** `integer` * **`page`** `string` * **`sessions`** `integer` **Example:** ```json { "page": "/pricing", "sessions": 1, "bounces": 1, "bounce_rate": 0.45, "avg_duration": 1, "avg_events": 1 } ``` ### SessionDistributionResponse - **Type:**`object` * **`distribution`** `array` **Items:** - **`avg_events`** `number` - **`bounces`** `integer` — Number of bounce sessions in this duration bucket (computed from event names at query time). - **`bucket`** `string`, possible values: `"0s", "1-10s", "10-30s", "30-60s", "1-3m", "3-10m", "10m+"` - **`pct`** `number` — Percentage of total sessions - **`sessions`** `integer` * **`engaged_pct`** `number` — Percentage of sessions >= 30 seconds * **`median_bucket`** `string | null` * **`project`** `string` **Example:** ```json { "project": "", "distribution": [ { "bucket": "0s", "sessions": 1, "bounces": 1, "avg_events": 1, "pct": 1 } ], "median_bucket": null, "engaged_pct": 1 } ``` ### HeatmapResponse - **Type:**`object` * **`busiest_day`** `string | null` * **`busiest_hour`** `integer | null` * **`heatmap`** `array` **Items:** - **`day`** `integer` — Day of week (0=Sunday, 6=Saturday) - **`day_name`** `string` - **`events`** `integer` - **`hour`** `integer` — Hour of day (0-23) - **`users`** `integer` * **`peak`** `object | null` - **`day`** `integer` - **`day_name`** `string` - **`events`** `integer` - **`hour`** `integer` - **`users`** `integer` * **`project`** `string` **Example:** ```json { "project": "", "heatmap": [ { "day": 1, "day_name": "Monday", "hour": 1, "events": 1, "users": 1 } ], "peak": { "day": 1, "day_name": "", "hour": 1, "events": 1, "users": 1 }, "busiest_day": "Monday", "busiest_hour": 14 } ``` ### FunnelRequest - **Type:**`object` * **`project` (required)** `string` — Project name * **`steps` (required)** `array` — Funnel steps (2-8). Each step has an event name and optional property filters. **Items:** - **`event` (required)** `string` — Event name for this step - **`filters`** `array` — Optional property filters for this step **Items:** - **`op` (required)** `string`, possible values: `"eq", "neq", "contains"` — Filter operator - **`property` (required)** `string` — Property key (alphanumeric + underscores, max 128 chars) - **`value` (required)** `string` — Filter value * **`breakdown`** `string` — Optional property key to segment funnel by (e.g. 'variant', 'country'). Extracted from step 1 events only. * **`breakdown_limit`** `number`, default: `10` — Max breakdown groups, ordered by step 1 users descending (1-50, default 10) * **`conversion_window_hours`** `number`, default: `168` — Max hours from step 1 entry to final step (1-8760, default 168 = 7 days) * **`count_by`** `string`, possible values: `"user_id", "session_id"`, default: `"user_id"` — Count by unique users or sessions * **`since`** `string`, default: `"30d"` — Lookback period (ISO date or shorthand like '30d'). Default: 30d **Example:** ```json { "project": "my-site", "steps": [ { "event": "page_view", "filters": [ { "property": "path", "op": "eq", "value": "/pricing" } ] } ], "conversion_window_hours": 168, "since": "30d", "count_by": "user_id", "breakdown": "country", "breakdown_limit": 10 } ``` ### FunnelResponse - **Type:**`object` * **`breakdowns`** `array` — Per-group funnel results (only present when \`breakdown\` param is set). Ordered by step 1 users descending. **Items:** - **`overall_conversion_rate`** `number` — End-to-end conversion for this group - **`steps`** `array` — Same shape as top-level steps array **Items:** - **`avg_time_to_next_ms`** `number | null` - **`conversion_rate`** `number` - **`drop_off_rate`** `number` - **`event`** `string` - **`step`** `integer` - **`users`** `integer` - **`value`** `string | null` — Breakdown property value (null for events missing the property) * **`overall_conversion_rate`** `number` — Fraction of step 1 users that completed the final step * **`steps`** `array` **Items:** - **`avg_time_to_next_ms`** `number | null` — Average time in ms from this step to the next step (null for last step) - **`conversion_rate`** `number` — Fraction of users from previous step that reached this step (step 1 is always 1.0) - **`drop_off_rate`** `number` — Fraction of users lost from previous step (step 1 is always 0) - **`event`** `string` - **`step`** `integer` - **`users`** `integer` — Number of users (or sessions) that reached this step **Example:** ```json { "steps": [ { "step": 1, "event": "page_view", "users": 500, "conversion_rate": 0.6, "drop_off_rate": 0.4, "avg_time_to_next_ms": 3600000 } ], "overall_conversion_rate": 0.12, "breakdowns": [ { "value": "US", "steps": [ { "step": 1, "event": "", "users": 1, "conversion_rate": 1, "drop_off_rate": 1, "avg_time_to_next_ms": null } ], "overall_conversion_rate": 0.15 } ] } ``` ### RetentionResponse - **Type:**`object` * **`average_rates`** `array` — Weighted average retention rate per period offset (weighted by cohort size) **Items:** `number` * **`cohorts`** `array` **Items:** - **`date`** `string`, format: `date` — Start date of this cohort period - **`rates`** `array` — Retention rate per period (retained\[i] / users, rounded to 3 decimals) **Items:** `number` - **`retained`** `array` — Users active in each subsequent period (index 0 = full cohort) **Items:** `integer` - **`users`** `integer` — Number of users who first appeared in this period * **`period`** `string`, possible values: `"day", "week", "month"` * **`users_analyzed`** `integer` — Total users across all cohorts (capped at 10K per query) **Example:** ```json { "period": "week", "cohorts": [ { "date": "2026-01-27", "users": 142, "retained": [ 142, 64, 55, 46 ], "rates": [ 1, 0.451, 0.387, 0.324 ] } ], "average_rates": [ 1, 0.441, 0.373, 0.324 ], "users_analyzed": 560 } ``` ### Project - **Type:**`object` * **`allowed_origins`** `string` * **`id`** `string` * **`name`** `string` * **`project_token`** `string` * **`total_events`** `integer` * **`total_reads`** `integer` **Example:** ```json { "id": "", "name": "my-site", "project_token": "aat_...", "allowed_origins": "*", "total_events": 1, "total_reads": 1 } ``` ### LiveSnapshotResponse - **Type:**`object` * **`active_sessions`** `integer` — Unique session\_ids in the time window * **`active_visitors`** `integer` — Unique user\_ids in the time window * **`events_per_minute`** `integer` * **`project`** `string` * **`recent_events`** `array` — Last 10 events, newest first **Items:** - **`event`** `string` - **`properties`** `object | null` - **`timestamp`** `number` - **`user_id`** `string | null` * **`timestamp`** `number` * **`top_events`** `array` **Items:** - **`count`** `integer` - **`event`** `string` * **`top_pages`** `array` **Items:** - **`path`** `string` - **`visitors`** `integer` * **`window_seconds`** `integer` **Example:** ```json { "project": "my-site", "window_seconds": 60, "timestamp": 1708000060000, "active_visitors": 12, "active_sessions": 8, "events_per_minute": 47, "top_pages": [ { "path": "/", "visitors": 5 } ], "top_events": [ { "event": "page_view", "count": 32 } ], "recent_events": [ { "event": "", "properties": null, "user_id": null, "timestamp": 1 } ] } ``` ### AllSitesPeriod - **Type:**`object` * **`from`** `string`, format: `date` * **`label`** `string`, possible values: `"1d", "7d", "14d", "30d", "90d"` * **`previous_from`** `string`, format: `date` * **`previous_to`** `string`, format: `date` * **`to`** `string`, format: `date` **Example:** ```json { "label": "7d", "from": "2026-03-03", "to": "2026-03-09", "previous_from": "2026-02-24", "previous_to": "2026-03-02" } ``` ### AllSitesMetricDelta - **Type:**`object` * **`change`** `integer` * **`change_pct`** `object` * **`current`** `integer` * **`previous`** `integer` **Example:** ```json { "current": 120, "previous": 90, "change": 30, "change_pct": 33 } ``` ### AllSitesSummary - **Type:**`object` * **`active_projects`** `integer` * **`total_events`** `object` - **`change`** `integer` - **`change_pct`** `object` - **`current`** `integer` - **`previous`** `integer` * **`total_projects`** `integer` **Example:** ```json { "total_projects": 3, "active_projects": 2, "total_events": { "current": 120, "previous": 90, "change": 30, "change_pct": 33 } } ``` ### AllSitesTimeSeriesPoint - **Type:**`object` * **`date`** `string`, format: `date` * **`events`** `integer` **Example:** ```json { "date": "2026-03-09", "events": 40 } ``` ### AllSitesProjectSummary - **Type:**`object` * **`events`** `integer` * **`id`** `string` * **`last_active_date`** `object` * **`name`** `string` * **`share_pct`** `number` **Example:** ```json { "id": "proj_123", "name": "my-site", "events": 80, "share_pct": 66.7, "last_active_date": "2026-03-09" } ``` ### AllSitesOverviewResponse - **Type:**`object` * **`period`** `object` - **`from`** `string`, format: `date` - **`label`** `string`, possible values: `"1d", "7d", "14d", "30d", "90d"` - **`previous_from`** `string`, format: `date` - **`previous_to`** `string`, format: `date` - **`to`** `string`, format: `date` * **`projects`** `array` **Items:** - **`events`** `integer` - **`id`** `string` - **`last_active_date`** `object` - **`name`** `string` - **`share_pct`** `number` * **`remaining_projects`** `integer` * **`scope`** `string`, possible values: `"account"` * **`summary`** `object` - **`active_projects`** `integer` - **`total_events`** `object` - **`change`** `integer` - **`change_pct`** `object` - **`current`** `integer` - **`previous`** `integer` - **`total_projects`** `integer` * **`time_series`** `array` **Items:** - **`date`** `string`, format: `date` - **`events`** `integer` **Example:** ```json { "scope": "account", "period": { "label": "7d", "from": "2026-03-03", "to": "2026-03-09", "previous_from": "2026-02-24", "previous_to": "2026-03-02" }, "summary": { "total_projects": 3, "active_projects": 2, "total_events": { "current": 120, "previous": 90, "change": 30, "change_pct": 33 } }, "time_series": [ { "date": "2026-03-09", "events": 40 } ], "projects": [ { "id": "proj_123", "name": "my-site", "events": 80, "share_pct": 66.7, "last_active_date": "2026-03-09" } ], "remaining_projects": 1 } ``` ### BotTrafficMetricDelta - **Type:**`object` * **`change`** `integer` * **`change_pct`** `object` * **`current`** `integer` * **`previous`** `integer` **Example:** ```json { "current": 5, "previous": 1, "change": 4, "change_pct": 400 } ``` ### BotTrafficTimeSeriesPoint - **Type:**`object` * **`date`** `string`, format: `date` * **`dropped_events`** `integer` * **`requests`** `integer` **Example:** ```json { "date": "2026-03-13", "requests": 5, "dropped_events": 7 } ``` ### BotTrafficCategorySummary - **Type:**`object` * **`category`** `string` * **`dropped_events`** `integer` * **`requests`** `integer` * **`share_pct`** `number` **Example:** ```json { "category": "ai_agent", "requests": 3, "dropped_events": 5, "share_pct": 60 } ``` ### BotTrafficActorSummary - **Type:**`object` * **`actor`** `string` * **`category`** `string` * **`dropped_events`** `integer` * **`last_seen_at`** `object` * **`requests`** `integer` **Example:** ```json { "actor": "ChatGPT-User", "category": "ai_agent", "requests": 3, "dropped_events": 5, "last_seen_at": 1773459600000 } ``` ### BotTrafficProjectSummary - **Type:**`object` * **`dropped_events`** `integer` * **`id`** `string` * **`last_seen_at`** `object` * **`name`** `string` * **`requests`** `integer` * **`share_pct`** `number` **Example:** ```json { "id": "proj_123", "name": "my-site", "requests": 3, "dropped_events": 4, "share_pct": 60, "last_seen_at": 1773459600000 } ``` ### BotTrafficProjectSummaryMetrics - **Type:**`object` * **`automated_requests`** `object` - **`change`** `integer` - **`change_pct`** `object` - **`current`** `integer` - **`previous`** `integer` * **`dropped_events`** `object` - **`change`** `integer` - **`change_pct`** `object` - **`current`** `integer` - **`previous`** `integer` * **`last_seen_at`** `object` **Example:** ```json { "automated_requests": { "current": 5, "previous": 1, "change": 4, "change_pct": 400 }, "dropped_events": { "current": 5, "previous": 1, "change": 4, "change_pct": 400 }, "last_seen_at": 1773459600000 } ``` ### BotTrafficAccountSummary - **Type:**`object` * **`active_projects`** `integer` * **`automated_requests`** `object` - **`change`** `integer` - **`change_pct`** `object` - **`current`** `integer` - **`previous`** `integer` * **`dropped_events`** `object` - **`change`** `integer` - **`change_pct`** `object` - **`current`** `integer` - **`previous`** `integer` * **`last_seen_at`** `object` * **`total_projects`** `integer` **Example:** ```json { "automated_requests": { "current": 5, "previous": 1, "change": 4, "change_pct": 400 }, "dropped_events": { "current": 5, "previous": 1, "change": 4, "change_pct": 400 }, "active_projects": 1, "total_projects": 4, "last_seen_at": 1773459600000 } ``` ### BotTrafficOverviewResponse - **Type:**`object` * **`actors`** `array` **Items:** - **`actor`** `string` - **`category`** `string` - **`dropped_events`** `integer` - **`last_seen_at`** `object` - **`requests`** `integer` * **`categories`** `array` **Items:** - **`category`** `string` - **`dropped_events`** `integer` - **`requests`** `integer` - **`share_pct`** `number` * **`period`** `object` - **`from`** `string`, format: `date` - **`label`** `string`, possible values: `"1d", "7d", "14d", "30d", "90d"` - **`previous_from`** `string`, format: `date` - **`previous_to`** `string`, format: `date` - **`to`** `string`, format: `date` * **`project`** `string` * **`scope`** `string`, possible values: `"project"` * **`summary`** `object` - **`automated_requests`** `object` - **`change`** `integer` - **`change_pct`** `object` - **`current`** `integer` - **`previous`** `integer` - **`dropped_events`** `object` - **`change`** `integer` - **`change_pct`** `object` - **`current`** `integer` - **`previous`** `integer` - **`last_seen_at`** `object` * **`time_series`** `array` **Items:** - **`date`** `string`, format: `date` - **`dropped_events`** `integer` - **`requests`** `integer` **Example:** ```json { "scope": "project", "project": "my-site", "period": { "label": "7d", "from": "2026-03-03", "to": "2026-03-09", "previous_from": "2026-02-24", "previous_to": "2026-03-02" }, "summary": { "automated_requests": { "current": 5, "previous": 1, "change": 4, "change_pct": 400 }, "dropped_events": { "current": 5, "previous": 1, "change": 4, "change_pct": 400 }, "last_seen_at": 1773459600000 }, "categories": [ { "category": "ai_agent", "requests": 3, "dropped_events": 5, "share_pct": 60 } ], "actors": [ { "actor": "ChatGPT-User", "category": "ai_agent", "requests": 3, "dropped_events": 5, "last_seen_at": 1773459600000 } ], "time_series": [ { "date": "2026-03-13", "requests": 5, "dropped_events": 7 } ] } ``` ### AllSitesBotTrafficOverviewResponse - **Type:**`object` * **`categories`** `array` **Items:** - **`category`** `string` - **`dropped_events`** `integer` - **`requests`** `integer` - **`share_pct`** `number` * **`period`** `object` - **`from`** `string`, format: `date` - **`label`** `string`, possible values: `"1d", "7d", "14d", "30d", "90d"` - **`previous_from`** `string`, format: `date` - **`previous_to`** `string`, format: `date` - **`to`** `string`, format: `date` * **`projects`** `array` **Items:** - **`dropped_events`** `integer` - **`id`** `string` - **`last_seen_at`** `object` - **`name`** `string` - **`requests`** `integer` - **`share_pct`** `number` * **`remaining_projects`** `integer` * **`scope`** `string`, possible values: `"account"` * **`summary`** `object` - **`active_projects`** `integer` - **`automated_requests`** `object` - **`change`** `integer` - **`change_pct`** `object` - **`current`** `integer` - **`previous`** `integer` - **`dropped_events`** `object` - **`change`** `integer` - **`change_pct`** `object` - **`current`** `integer` - **`previous`** `integer` - **`last_seen_at`** `object` - **`total_projects`** `integer` * **`time_series`** `array` **Items:** - **`date`** `string`, format: `date` - **`dropped_events`** `integer` - **`requests`** `integer` **Example:** ```json { "scope": "account", "period": { "label": "7d", "from": "2026-03-03", "to": "2026-03-09", "previous_from": "2026-02-24", "previous_to": "2026-03-02" }, "summary": { "automated_requests": { "current": 5, "previous": 1, "change": 4, "change_pct": 400 }, "dropped_events": { "current": 5, "previous": 1, "change": 4, "change_pct": 400 }, "active_projects": 1, "total_projects": 4, "last_seen_at": 1773459600000 }, "categories": [ { "category": "ai_agent", "requests": 3, "dropped_events": 5, "share_pct": 60 } ], "projects": [ { "id": "proj_123", "name": "my-site", "requests": 3, "dropped_events": 4, "share_pct": 60, "last_seen_at": 1773459600000 } ], "remaining_projects": 3, "time_series": [ { "date": "2026-03-13", "requests": 5, "dropped_events": 7 } ] } ``` ### AccountResponse - **Type:**`object` * **`created_at`** `string` * **`email`** `string` * **`github_login`** `string | null` * **`id`** `string` * **`monthly_spend_cap_dollars`** `number | null` * **`projects_count`** `integer` * **`tier`** `string`, possible values: `"free", "pro"` * **`tier_limits`** `object` - **`max_events_per_month`** `integer` - **`max_projects`** `integer` - **`max_reads_per_month`** `integer` - **`rate_limit_rpm`** `integer` - **`retention_days`** `integer` **Example:** ```json { "id": "acc_free123", "email": "free@example.com", "github_login": "freeuser", "tier": "free", "created_at": "2026-02-01T12:00:00.000Z", "projects_count": 2, "tier_limits": { "max_events_per_month": 100000, "max_projects": 2, "max_reads_per_month": 500, "retention_days": 90, "rate_limit_rpm": 10 }, "monthly_spend_cap_dollars": null } ``` ### Experiment - **Type:**`object` * **`account_id`** `string` * **`completed_at`** `string | null` * **`created_at`** `string` * **`goal_event`** `string` * **`id`** `string` * **`name`** `string` * **`project_id`** `string` * **`status`** `string`, possible values: `"active", "paused", "completed"` * **`updated_at`** `string` * **`variants`** `array` **Items:** - **`key`** `string` - **`weight`** `integer` * **`winner`** `string | null` — Variant key, set when status is completed **Example:** ```json { "id": "exp_a1b2c3d4e5f6g7h8", "project_id": "", "account_id": "", "name": "signup_cta", "variants": [ { "key": "control", "weight": 50 } ], "goal_event": "signup", "status": "active", "winner": null, "created_at": "", "updated_at": "", "completed_at": null } ``` ### ExperimentResults - **Type:**`object` * **`lift`** `object` * **`probability_best`** `object` * **`recommendation`** `string` * **`sufficient_data`** `boolean` * **`variants`** `array` **Items:** - **`conversion_rate`** `number` - **`conversions`** `integer` - **`exposures`** `integer` - **`key`** `string` - **`unique_users`** `integer` **Example:** ```json { "variants": [ { "key": "control", "exposures": 520, "unique_users": 480, "conversions": 48, "conversion_rate": 0.1 } ], "probability_best": { "control": 0.06, "new_cta": 0.94 }, "lift": { "control": -0.32, "new_cta": 0.47 }, "sufficient_data": true, "recommendation": "'new_cta' is the winner with 94% probability (47% lift over baseline)" } ``` ## Additional Response Examples ### `GET /account` Status: `200` · Content-Type: `application/json` #### freeTier — Free tier account ```json { "id": "acc_free123", "email": "free@example.com", "github_login": "freeuser", "tier": "free", "created_at": "2026-02-01T12:00:00.000Z", "projects_count": 2, "tier_limits": { "max_events_per_month": 100000, "max_projects": 2, "max_reads_per_month": 500, "retention_days": 90, "rate_limit_rpm": 10 }, "monthly_spend_cap_dollars": null } ``` #### proTier — Pro tier account ```json { "id": "acc_pro123", "email": "pro@example.com", "github_login": "prouser", "tier": "pro", "created_at": "2026-02-01T12:00:00.000Z", "projects_count": 42, "tier_limits": { "max_events_per_month": -1, "max_projects": -1, "max_reads_per_month": -1, "retention_days": 365, "rate_limit_rpm": 1000 }, "monthly_spend_cap_dollars": 50 } ``` ### `GET /bot-traffic` Status: `200` · Content-Type: `application/json` #### empty — Empty state ```json { "scope": "project", "project": "my-site", "period": { "label": "7d", "from": "2026-03-07", "to": "2026-03-13", "previous_from": "2026-02-28", "previous_to": "2026-03-06" }, "summary": { "automated_requests": { "current": 0, "previous": 0, "change": 0, "change_pct": 0 }, "dropped_events": { "current": 0, "previous": 0, "change": 0, "change_pct": 0 }, "last_seen_at": null }, "categories": [], "actors": [], "time_series": [ { "date": "2026-03-13", "requests": 0, "dropped_events": 0 } ] } ``` #### active — Active bot traffic ```json { "scope": "project", "project": "my-site", "period": { "label": "7d", "from": "2026-03-07", "to": "2026-03-13", "previous_from": "2026-02-28", "previous_to": "2026-03-06" }, "summary": { "automated_requests": { "current": 5, "previous": 1, "change": 4, "change_pct": 400 }, "dropped_events": { "current": 7, "previous": 1, "change": 6, "change_pct": 600 }, "last_seen_at": 1773459600000 }, "categories": [ { "category": "ai_agent", "requests": 3, "dropped_events": 5, "share_pct": 60 }, { "category": "search_crawler", "requests": 2, "dropped_events": 2, "share_pct": 40 } ], "actors": [ { "actor": "ChatGPT-User", "category": "ai_agent", "requests": 3, "dropped_events": 5, "last_seen_at": 1773459600000 }, { "actor": "Googlebot", "category": "search_crawler", "requests": 2, "dropped_events": 2, "last_seen_at": 1773456000000 } ], "time_series": [ { "date": "2026-03-13", "requests": 5, "dropped_events": 7 } ] } ```