# 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 AI agents such as Claude Code, Cursor, Codex, and OpenClaw. Use these guides when you want to tell an AI agent such as Claude Code, Cursor, Codex, or OpenClaw what to do, not manually stitch together the low-level steps yourself. ## Start with the right guide If you are landing from a Paperclip company workflow, start with [Set up Agent Analytics for your Paperclip company](/guides/paperclip/) for onboarding and [Paperclip Live Plugin](/reference/paperclip-live-plugin/) for the thin live-monitor surface. If you need environment-specific setup outside Paperclip, 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 AI agent, and verify the first query. This is the shortest path from zero to a working Agent Analytics setup. The main flow is agent-first: choose `Connect as agent`, start in the AI agent you already use, let that agent open the browser for you or give you a login link, then have it create the project and wire tracking for you. Use `Connect as human` only when you need direct account access yourself, such as billing, cancellation, or manual review. Browser approval and finish-code handoff still belong to the normal agent-owned setup flow. If your AI agent, such as Claude Code, Cursor, Codex, or OpenClaw, is already installed and you want the clearest create-project, place-snippet, and verify-first-page-view walkthrough, use [First Project in 5 Minutes](/guides/first-project-in-5-minutes/). If you want your agent to install analytics with product judgment instead of a generic event list, start with [Product Growth Scanner](/guides/website-analysis/). It returns the growth questions, measurement blind spots, minimum viable instrumentation, and what not to measure yet. If you want to understand the two public skills first, start with [Agent Analytics Skill](/guides/agent-analytics-skill/) for setup and reporting, then [Autoresearch Growth Skill](/guides/autoresearch-growth-skill/) for data-informed variant generation. If you are here from a Paperclip workflow, start by creating a task for your CEO in Paperclip, then follow [Set up Agent Analytics for your Paperclip company](/guides/paperclip/) for the one-task delegation and finish-code handoff path. ## 1. Pick and complete an install path If you are using Paperclip, stop here first and create the CEO task from [Set up Agent Analytics for your Paperclip company](/guides/paperclip/). That guide covers the one-task delegation path, browser approval, and the finish-code reply flow. 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 into your own runtime. ## 2. Open your AI agent in the project directory Start from the codebase or site you actually want to track. Then ask your AI agent for the full setup flow: - `Set up Agent Analytics for this project. Run the website analysis first so you know what my agent should track first. Install only the high-priority recommended events. Open the browser for me or send me the login link, then wait. I will sign in with Google or GitHub, approve it, and paste back any finish code if you need it. Then create the project, add tracking, explain what each event enables, and verify the first useful event.` When the browser approval page opens, sign in with Google or GitHub, approve it, and let the agent continue. That approval creates or connects your account and gives the agent the session it needs. ## 3. Create your first project After your AI 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 AI agent has code write access to your site, ask it: - `Set up analytics for my-site.com. Analyze the public site first, then install only the first recommended events.` Your AI 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 AI 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 product has login or signup, keep `signup` for the moment the account is actually created, preferably server-side. After auth succeeds in the browser, call `window.aa.identify(account.id)` before other post-auth browser events so client and server activity land on the same user. 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 your app is a SPA, use [SPA and Virtual Page Tracking](/guides/spa-and-virtual-page-tracking/) to understand what Agent Analytics auto-tracks and when to send a manual virtual `page_view`. If you want your AI 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 AI 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 or opening a reporting UI. ## Advanced fallback: API key If you need direct HTTP access for your own runtime, you can still generate an API key from [app.agentanalytics.sh](https://app.agentanalytics.sh). That is the advanced/manual path. CLI, MCP, and agent onboarding should use browser approval and agent sessions instead. ## Next - Use [Installation](/installation/) for the fastest per-agent setup. - Use [First Project in 5 Minutes](/guides/first-project-in-5-minutes/) when you want the full activation walkthrough from installed agent to first live project. - Use [Product Growth Scanner](/guides/website-analysis/) when you want your agent to decide what to measure before it installs custom events. - Use [Agent Analytics Skill](/guides/agent-analytics-skill/) when you want the regular skill for setup, reporting, and experiments. - Use [Autoresearch Growth Skill](/guides/autoresearch-growth-skill/) when you want a loop that generates variants, tests approved experiments, and feeds the next run from measured behavior. - Use [Set up Agent Analytics for your Paperclip company](/guides/paperclip/) when your company runs on Paperclip and you need the direct CEO-task and finish-code path. - Use [SPA and Virtual Page Tracking](/guides/spa-and-virtual-page-tracking/) when your app changes screens client-side and you need accurate page tracking without double-counting. - Use [AI Agent Experiment Tracking](/guides/ai-agent-experiment-tracking/) when you want your AI 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. ## Set up Agent Analytics for your Paperclip company URL: https://docs.agentanalytics.sh/guides/paperclip/ Ask the user to create one CEO task, then review the blocked task when login approval is needed, and finally query the live project. [πŸ“ŽPaperclip](https://paperclip.ing/) is open-source orchestration for zero-human companies. The clean Agent Analytics path in πŸ“ŽPaperclip is now: 1. ask the user to create one task for the CEO 2. review the blocked task when login approval is needed 3. ask the analyst the first real traffic or signup question Use `Connect as agent` if you want the agent to own that blocked-login handoff from start to finish. Use `Connect as human` only when you need direct account access yourself, not because the setup flow has changed. For normal Paperclip setup, keep browser approval and finish-code handoff as the default path. Raw API keys are only there as an advanced fallback for custom runtimes. If you want the strategic context before you set this up, read [If You Use Paperclip, You Need End-User Analytics](https://blog.agentanalytics.sh/blog/paperclip-companies-need-agent-readable-analytics/). If the engineer already has access and you only need the activation loop, continue with [First Project in 5 Minutes](/guides/first-project-in-5-minutes/). ## Step 1: create one CEO task For most Paperclip companies, the setup should start with one explicit CEO task. Task title: ```text Hire an analyst to use AgentAnalytics ``` Task description: ```text Hire or assign the analytics analyst for our company. First install the Agent Analytics skill in Paperclip: npx skills add Agent-Analytics/agent-analytics-skill Make sure Agent Analytics is available to the analyst and the engineer working on the project. Ask the engineer on the relevant project to set up Agent Analytics tracking in the codebase. If approval is needed, send me the approval link, wait for me to sign in with Google or GitHub, accept the finish code I reply with, and then continue by creating the project, adding tracking and key events, and verifying the first event. ``` ![CEO task for hiring an analyst and setting up Agent Analytics in Paperclip](/paperclip-ceo-issue-creation.png) ## Step 2: review the blocked task and approve login At some point, the working agent will return that the task is blocked because it needs your help with login approval. When that happens: 1. open the login approval URL the agent posted in the Paperclip task 2. sign in with Google or GitHub 3. copy the finish code from the browser 4. paste that finish code back into the blocked Paperclip task ![Agent Analytics finish-code handoff screen](/finishcode.jpg) That lets the working setup agent continue: - finish login - create the project - add tracking - verify the first event If your Paperclip company wants a shared install primitive, the company skill key is still: ```text agent-analytics/agent-analytics-skill/agent-analytics ``` But that is implementation detail, not the main product story. The important path is still: create one CEO task, let it drive the setup in the real project, then ask real analytics questions. ## What approval looks like in practice In Paperclip, the detached login flow should look like this: 1. the working setup agent starts Agent Analytics detached login, gets an approval URL, and the start command exits 2. you open that URL in the browser 3. you sign in with Google or GitHub 4. the browser shows a finish code 5. you paste that finish code back into the Paperclip issue thread 6. the working setup agent uses that code to complete login and keep going Paperclip should not depend on a long-running polling command for this path. The finish-code reply is the handoff that lets the agent resume reliably inside the issue thread. The important product rule is simple: the user approves identity in the browser, but the working agent ends up holding the session and continues the setup. If you later want to review or revoke that Paperclip-owned session, open [app.agentanalytics.sh](https://app.agentanalytics.sh), go to `Account Settings`, and use the `Agent Sessions` section. That page now shows active Paperclip, CLI, macOS Live, and similar hosted agent logins. ## Step 3: ask the analyst real questions Once the engineer has the project live, ask the analyst real questions: - `Show me traffic, top pages, recent events, signups, purchases, and feature usage for this project.` - `Set up an A/B experiment for this project and help me decide what to change next.` - `Create a recurring 8:00 AM analyst job. Every morning, query Agent Analytics for the latest 24 hours across this company's projects and post a short brief with anomalies, winners, drop-offs, and one recommended action.` The success condition is not just login. The success condition is: - the setup agent can authenticate through browser approval and finish-code handoff - create the project - verify the first live event - the analyst can answer a real analytics question from the same issue flow ## Fast rollout order for Paperclip companies Use this order if you want the shortest path to first value: 1. create the CEO task 2. let that task cover the analyst and the engineer in one delegation path 3. review the blocked task when the working agent needs approval 4. sign in and reply with the finish code 5. let the working agent create the first project and verify the first event 6. ask the analyst one real traffic or signup question 7. add the recurring 8:00 AM analyst brief once the first traffic question works ## Advanced/manual fallback If you need direct HTTP access for a custom runtime later, you can still generate a raw API key from [app.agentanalytics.sh](https://app.agentanalytics.sh). Treat that as the advanced/manual fallback. For the normal Paperclip setup path, prefer browser approval plus finish code handoff. ## When to use a different page - Use [Getting Started](/getting-started/) if you want the shortest overview for the whole product. - Use [Installation](/installation/) if you need the raw per-environment install pages outside the Paperclip company flow. - Use [First Project in 5 Minutes](/guides/first-project-in-5-minutes/) if the engineer already has access and you want the clearest install-to-live walkthrough. - Use [Tracker.js](/reference/tracker-js/) if you need browser-side details such as SPA tracking, declarative events, consent, errors, or web vitals. - Use [CLI vs MCP vs API](/reference/cli-mcp-api/) if your team is deciding between access paths. ## Related - [If You Use Paperclip, You Need End-User Analytics](https://blog.agentanalytics.sh/blog/paperclip-companies-need-agent-readable-analytics/) - [Paperclip Live Plugin](/reference/paperclip-live-plugin/) - [Getting Started](/getting-started/) - [First Project in 5 Minutes](/guides/first-project-in-5-minutes/) - [Tracker.js](/reference/tracker-js/) - [CLI vs MCP vs API](/reference/cli-mcp-api/) - [API Reference](/api/) ## Agent Analytics Skill URL: https://docs.agentanalytics.sh/guides/agent-analytics-skill/ Install the regular Agent Analytics skill and let your coding agent create projects, add tracking, query analytics, and run experiments from the same workflow. Use the regular Agent Analytics skill when you want your AI agent to operate Agent Analytics end-to-end from the project it is already editing. It is the right first skill for setup, tracking, reporting, funnels, session paths, and normal experiment work. If you want the agent to generate and judge growth variants before an A/B test, use the [Autoresearch Growth Skill](/guides/autoresearch-growth-skill/) after the basic analytics loop works. ## Install Install from the public skill repo: ```bash npx skills add Agent-Analytics/agent-analytics-skill ``` If the installer asks which skill to install, choose `agent-analytics`. You can also install it explicitly: ```bash npx skills add Agent-Analytics/agent-analytics-skill --skill agent-analytics ``` The source is public: - [Agent Analytics skill repo](https://github.com/Agent-Analytics/agent-analytics-skill) - [Agent Analytics](https://agentanalytics.sh) ## What To Ask Your Agent Start in the codebase or site you want to measure, then ask: ```text Set up Agent Analytics for this project. Run the website analysis first so you know what my agent should track first. Install it here if needed. Open the browser for me or give me a login link, then wait. I will sign in with Google or GitHub, approve it, and paste back any finish code if you need it. Then create the project, install only the high-priority recommended events, explain what each event enables, and verify the first useful event. ``` After setup, ask normal analytics questions in plain English: ```text How did this site perform in the last 7 days? ``` ```text Show the funnel from page_view to signup_cta_click to signup. ``` ```text Create an experiment for the signup CTA with control and a clearer variant, then show me how to QA both versions before it gets traffic. ``` ## What The Skill Does The skill teaches the agent to use the official Agent Analytics CLI and API patterns without making you hand-write requests. Typical jobs: - analyze the public site before installing custom events - create or find the right project - install `tracker.js` - add only the first useful declarative events such as CTA clicks - verify the first useful recommended event - inspect pages, events, funnels, retention, and bot traffic - create, QA, measure, and complete experiments - explain gaps in the data before recommending action Browser approval is the normal login path. You do not need to create an API key first unless you are building a custom direct HTTP integration. For OpenClaw and similar managed runtimes, tell the agent to keep Agent Analytics CLI auth in a persistent workspace path instead of the default home config path: ```bash export AGENT_ANALYTICS_CONFIG_DIR="$PWD/.openclaw/agent-analytics" npx @agent-analytics/cli@0.5.16 auth status ``` If the runtime may not preserve exported variables between commands, prefix each Agent Analytics CLI command with that same `AGENT_ANALYTICS_CONFIG_DIR=...` value or pass `--config-dir "$PWD/.openclaw/agent-analytics"`. Do not commit `.openclaw/agent-analytics/config.json`. ## When To Use The Autoresearch Skill Instead Use [Autoresearch Growth Skill](/guides/autoresearch-growth-skill/) when the task is not just "read analytics" or "create an experiment," but: - generate landing-page, onboarding, pricing, or CTA variants - critique generic copy and product-truth drift - blind-rank multiple candidates - output two review-ready experiment variants - rerun the loop after experiment data comes back In practice, the regular skill gets the analytics foundation working. The autoresearch skill uses that foundation to run a structured growth loop. ## Related - [Autoresearch Growth Skill](/guides/autoresearch-growth-skill/) - [First Project in 5 Minutes](/guides/first-project-in-5-minutes/) - [AI Agent Experiment Tracking](/guides/ai-agent-experiment-tracking/) - [Session Paths](/guides/session-paths/) - [CLI](/reference/cli/) - [Plugin vs Skill vs MCP vs API](/reference/cli-mcp-api/) ## Autoresearch Growth Skill URL: https://docs.agentanalytics.sh/guides/autoresearch-growth-skill/ Use the Agent Analytics Autoresearch skill to run a loop on top of the loop: generate variants, judge them, ship approved experiments, measure real behavior, and feed the next run. Use the Autoresearch Growth skill when you want your coding agent to turn analytics data into review-ready growth experiments. The inner loop generates, critiques, synthesizes, and blind-ranks candidates. The outer loop waits for real behavior, pulls the experiment data back into the next brief, and runs again from evidence. This skill is based on [Andrei Karpathy's `autoresearch`](https://github.com/karpathy/autoresearch) pattern: keep the loop simple, let an external coding agent read the markdown instructions, write artifacts to disk, and iterate. Agent Analytics adapts that pattern for growth work in the public template repo: - [Agent Analytics Autoresearch Growth](https://github.com/Agent-Analytics/autoresearch-growth) - [Agent Analytics](https://agentanalytics.sh) The important change is the outer evidence loop. Karpathy's original loop is useful for generating and judging ideas. The growth version uses measured experiment behavior to seed the next run, so the loop does not stop at what LLM judges think sounds good. ## Install Install from the public skill repo: ```bash npx skills add Agent-Analytics/agent-analytics-skill ``` If the installer asks which skill to install, choose `agent-analytics-autoresearch`. You can also install it explicitly: ```bash npx skills add Agent-Analytics/agent-analytics-skill --skill agent-analytics-autoresearch ``` The regular `agent-analytics` skill can be installed from the same repo. Use it first when the site still needs project setup, tracking, or basic experiment wiring. ## What The Skill Needs The skill works with any analytics source, but works best with Agent Analytics because the agent can collect the evidence itself. Give the agent: - the target surface, such as homepage hero, pricing page, onboarding step, or signup CTA - the current control copy or screenshot - the product truth that must not drift - the audience - one primary event, such as `signup`, `checkout`, or `activation_completed` - one proxy event, such as `cta_click` - guardrails, such as bounce, scroll depth, time on page, errors, performance, source quality, or signup quality - recent analytics data or permission to fetch it The skill can also run on demo data from the template repo, so you can try the workflow before connecting an account. ## Run The Inner Loop Start with a review-only run: ```text Run the autoresearch growth loop for this landing page. Use signup as the primary event and cta_click as the proxy. Fetch the latest analytics data if you can; otherwise use the data I provide. Keep product truth intact, run 5 rounds, and produce two distinct variants for review. Do not wire the experiment yet. ``` The skill should create or update the normal run artifacts: - `brief.md` - `results.tsv` - `final_variants.md` - a dated data snapshot when live data is available `results.tsv` is the round log. The final decision artifact is `final_variants.md`. ## Run The Outer Loop Only move into production after human approval. Once you approve a variant, ask: ```text Implement the approved variants on the landing page, create an experiment with control plus the two candidates, verify signup and cta_click tracking, and show me the QA links before launch. ``` After the experiment has collected behavior, ask: ```text Pull the experiment results and guardrails into a new snapshot. Summarize what won, what lost, what is still too sparse to trust, and run the next autoresearch loop from that evidence. ``` That is the loop on top of the loop: 1. run several LLM judging rounds 2. pick two variants for human review 3. ship an approved experiment 4. wait for real behavior 5. collect the measured result 6. feed the next autoresearch run LLM judges are useful for pressure-testing options. They are not the final judge. User behavior is the final judge. ## Try It With Sample Data Clone or open the public template repo: ```bash git clone https://github.com/Agent-Analytics/autoresearch-growth.git cd autoresearch-growth ``` Then ask your agent: ```text Use the demo SaaS data in examples/demo-saas. Run the autoresearch growth loop and show me final_variants.md so I can judge the two winners. ``` No Agent Analytics account is required for the demo data. ## Related - [Agent Analytics Skill](/guides/agent-analytics-skill/) - [AI Agent Experiment Tracking](/guides/ai-agent-experiment-tracking/) - [Session Paths](/guides/session-paths/) - [CLI](/reference/cli/) - [Karpathy autoresearch](https://github.com/karpathy/autoresearch) - [Autoresearch Growth repo](https://github.com/Agent-Analytics/autoresearch-growth) - [Autoresearch Growth Loops Need Reality Checks](https://blog.agentanalytics.sh/blog/autoresearch-growth-loop-agent-analytics/) ## First Project in 5 Minutes URL: https://docs.agentanalytics.sh/guides/first-project-in-5-minutes/ Go from installed agent to a live Agent Analytics project with the tracker placed, the first page view verified, and one real analytics question answered. Use this guide when your AI agent install is already done and you want the clearest path to your first real success loop. If you still need to connect your AI agent first, go back to the [installation hub](/installation/) or the short [Getting Started](/getting-started/) overview. ## 1. Confirm your AI agent install is already working Ask your AI agent: ```text List my Agent Analytics projects and confirm you can reach my account. Open the browser for me or send me the login link, then wait for me. ``` When the browser approval page opens, sign in with Google or GitHub, approve it, and let the agent continue. If the agent still cannot see your account after that, stop here and finish the right [installation path](/installation/). ## 2. Create the first project and get the tracker snippet Ask your AI agent: ```text Create a project called my-site.com and give me the tracking snippet. ``` At this point you want one of two outcomes: - the agent creates the project and returns the tracker snippet for you to paste - the agent creates the project and can place the snippet itself ## 3. Choose the path that matches your setup ### Agent can edit code If your AI agent can change your site, ask it: ```text Create a project called my-site.com, add the Agent Analytics tracking snippet to the site, and tell me exactly which file you changed. ``` Then ask: ```text Show me where you placed the snippet and confirm it will load on the page I want to track first. ``` If your site uses Astro, tell the agent to add `is:inline` to the tracker ` ``` 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, use [First Project in 5 Minutes](/guides/first-project-in-5-minutes/) to create the project, get the snippet, and verify the first page view. Website analysis recommendations assume these automatic fields already exist. A good `implementation_hint` should use the smallest tracker capability that unlocks the decision: declarative attributes for simple intent, `window.aa.track(...)` for computed client state, and server-side tracking for durable outcomes like account creation. ## 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_cta_clicked` 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. Reserve `signup` for the durable moment when an account is actually created. If a button only starts the flow, use an intermediate event like `signup_started` or `signup_cta_clicked` instead of treating the click itself as the completed signup. ## 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 ## Authenticated apps: `signup`, `login`, and `identify` For apps with accounts, the cleanest post-auth browser step is: ```js window.aa?.identify(account.id); window.aa?.set({ plan: account.plan, team: account.team }); ``` Call `aa.identify(account.id)` immediately after auth succeeds and before `aa.set(...)` or any other post-auth browser events. That stitches the current browser activity onto the same canonical user ID that your server should use. Recommended event boundaries: - Fire `signup` exactly once when the account is created. Prefer the server-side account-creation path for this. - Fire `login` when an existing account finishes auth. Prefer the server-side auth callback or session-creation path for this too. - Use client-side events like `signup_started`, `signup_cta_clicked`, or `checkout_started` for earlier UI steps that happen before the account exists. That separation keeps funnels honest and makes browser events and server auth events land on the same `user_id`. ## SPA routing and virtual pages Agent Analytics automatically tracks `page_view` when the browser URL changes through `history.pushState()`, `history.replaceState()`, `popstate`, or `hashchange`. That covers most client-side routers, including hash-based SPAs. If your UI changes without changing the path, query string, or hash, the tracker does not try to guess that a new screen appeared. In that case, send a manual `page_view`. Preferred implementation order: 1. Real URL changes 2. Hash routing 3. Manual virtual `page_view` Use `aa.page(name)` when the current browser URL already matches the screen you want to label. It sends a `page_view` with a `page` property, but `path` and `url` still come from the current browser location. For true virtual pages with no URL change, send a manual `page_view` and override the route fields yourself: ```js window.aa?.track('page_view', { page: 'Checkout Step 2', path: '/checkout/step-2', url: `${location.origin}/checkout/step-2` }); ``` Do not combine manual page tracking with router-driven transitions that Agent Analytics already auto-tracks, or you will double-count the same screen change. If you want a prompt-first workflow for deciding between router tracking and manual virtual pages, use [SPA and Virtual Page Tracking](/guides/spa-and-virtual-page-tracking/). ## Common recipes ### Cross-domain identity ```html ``` ### Privacy and consent ```html ``` ```js window.aa?.grantConsent(); window.aa?.revokeConsent(); ``` When consent is granted, buffered events are flushed automatically. If the buffer contains more than 100 events, the tracker splits it into multiple `/track/batch` requests of 100 events or fewer. ### 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/) - [First Project in 5 Minutes](/guides/first-project-in-5-minutes/) - [SPA and Virtual Page Tracking](/guides/spa-and-virtual-page-tracking/) - [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 agent sessions, API keys, and project tokens before you wire an agent or a tracker. Agent Analytics uses three different credentials. They serve different jobs and should not be swapped. ## Agent session (`aas_*`) Use the agent session for: - official CLI login through browser approval - plugin and skill flows that authenticate through the hosted approval flow - agent-native setup where the agent should keep the connection after approval In the normal product flow, you do not paste this token around manually. The hosted approval flow creates it for the agent or CLI and stores it locally for later use. ## Managing active agent sessions If you want to inspect or revoke agent-owned logins later, open [app.agentanalytics.sh](https://app.agentanalytics.sh) and go to `Account Settings` β†’ `Agent Sessions`. That page shows active hosted agent sessions such as: - CLI logins - macOS Live app connections - Paperclip connections - MCP or other hosted agent-session clients Use `Disconnect` there when you want to revoke one specific session on the server. ## 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 three local auth convenience commands: - `npx @agent-analytics/cli login` starts browser approval and saves a local CLI session. - `npx @agent-analytics/cli login --detached` starts the same flow for headless or issue-based runtimes where the agent sends you an approval link and may ask for a finish code. - `npx @agent-analytics/cli login --token aak_...` saves an API key locally as the advanced/manual fallback. - `npx @agent-analytics/cli logout` clears the saved local CLI auth. `logout` is local-only for CLI state. If you want to revoke the hosted session itself, disconnect that session from the web app's `Agent Sessions` section. If you logged in with `--token`, `logout` does not revoke the API key on the server. Use `revoke-key` when you want to invalidate that saved raw API key and issue a new one. Scoped agent sessions cannot generate or rotate raw account API keys. 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/) ## Plugin vs Skill vs MCP vs API URL: https://docs.agentanalytics.sh/reference/cli-mcp-api/ Agent Analytics exposes one analytics surface through plugin, skill, MCP, CLI, and raw HTTP. Use this page to choose the native path that fits the environment your AI agent already uses. Agent Analytics exposes one analytics surface through five real access paths: - `Plugin` for Claude Code when you want the MCP connection and analytics workflow layer bundled together - `Skill` for agent environments that already support skills and command execution - `MCP` for chat-native and editor-native tool use - `CLI` for shell-oriented agent workflows - `API` for raw HTTP control The product model does not change between them. Projects, analytics reads, and experiment operations stay the same; only the native entrypoint changes. ## Recommended path by environment | Environment | Recommended path | Why | | --- | --- | --- | | Claude Code | Plugin first | Shortest hosted path with both MCP connectivity and Agent Analytics workflow guidance | | Claude Desktop / Cowork | Hosted MCP | Best fit for connector-style chat tools with native tool calls | | Cursor | Skill + CLI first | Usually lower overhead than MCP when the agent can already run commands | | OpenAI Codex | Skill first | Keeps the workflow agent-native without requiring MCP | | OpenClaw | Skill first | Cleanest path when OpenClaw owns the scheduled analytics job from chat | | Custom runtime or internal agent | API | Best fit when you own retries, parsing, and orchestration | ## When to use each path ### Plugin Use the plugin when your environment is Claude Code and you want one install that packages both: - the hosted MCP connection - the analytics-specific workflow layer This is the cleanest default when the plugin marketplace is available. ### Skill Use a skill when your agent already supports skills and can execute commands in the same environment. A skill is usually the best fit when: - you want a guided workflow layer around common analytics tasks - your agent already has terminal access - you want to stay in the agent's native loop instead of switching to tool-call-heavy MCP flows ### MCP Use MCP when your AI agent already runs inside a tool that supports connectors or MCP servers. 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 through structured tool responses - you want agent-readable reports such as `analytics_paths`, where the tool response includes both compact text and structured data 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 AI agent already has terminal access and is comfortable executing commands. CLI is usually the best fit when: - your AI 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 - you want shell-readable commands such as `paths` that summarize entry pages, exit pages, terminal labels, and next-step analysis For install, login flow, common commands, and CLI-to-API mapping, continue to the dedicated [CLI page](/reference/cli/). ### 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 ## Quick rule of thumb - Choose `plugin` first in Claude Code when the marketplace path is available. - Choose `skill + CLI` first in shell-capable environments like Cursor or Codex. - Choose `MCP` when the agent already lives in a connector-style chat environment and you want native tool calls. - Choose `API` when you need full control, custom integration, or lower-level debugging. ## Paths Across Access Paths Session paths are available through the same product surface: - CLI: `agent-analytics paths --goal ` - MCP: `analytics_paths` - API: `POST /paths` Use paths when the agent needs to connect entry pages and exit pages to goal behavior before deciding whether to run a funnel query, retention check, or experiment. The report is intentionally bounded and session-local. It is not a long-cycle identity-stitching report. ## Related - [CLI](/reference/cli/) - [Session Paths](/guides/session-paths/) - [Installation Overview](/installation/) - [Authentication](/reference/authentication/) - [Bot Traffic](/reference/bot-traffic/) - [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. `POST /track/batch` accepts up to 100 events per request. If you buffer events for consent or offline delivery, split larger buffers into multiple batch requests. Request-per-minute limits apply per HTTP request; monthly event limits and spend caps apply per accepted event. ## 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. Start an agent session with `POST /agent-sessions/start` or sign in on the dashboard. 2. Create a project with `POST /projects`. 3. Add the returned tracker snippet to your site. 4. Query analytics with `GET /stats`. **Authentication:** - `aas_*` agent sessions are the primary machine-auth mechanism for CLI, MCP, and managed runtimes. - `aak_*` API keys are an advanced/manual fallback for direct HTTP users. - `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 ### Start an agent-session signup/login flow - **Method:**Β `POST` - **Path:**Β `/agent-sessions/start` - **Tags:**Β Agent Sessions Creates a pending auth request for a CLI, MCP client, or managed runtime. Use `mode=interactive` when the runtime can receive a localhost/browser callback. Use `mode=detached` when the runtime must wait for approval by polling or manual exchange. #### Request Body ##### Content-Type: application/json - **`client_type` (required)** `string` β€” Runtime type such as \`cli\`, \`mcp\`, \`paperclip\`, or \`openclaw\`. - **`callback_url`** `string | null` β€” Required for interactive mode. Local loopback callback URL. - **`client_instance_id`** `string | null` - **`client_name`** `string | null` - **`code_challenge`** `string | null` β€” Optional PKCE-style challenge for interactive runtimes. - **`label`** `string | null` - **`metadata`** `object | null` - **`mode`** `string`, possible values: `"interactive", "detached"`, default: `"interactive"` - **`scopes`** `array` **Items:** `string`, possible values: `"account:read", "projects:write", "analytics:read", "experiments:write", "feedback:write", "live:read"` **Example:** ```json { "mode": "interactive", "client_type": "cli", "client_name": "Agent Analytics CLI", "client_instance_id": "pid:12345", "label": "Paperclip Workspace", "callback_url": "http://127.0.0.1:43123/callback", "scopes": [ "account:read" ], "code_challenge": null, "metadata": null } ``` #### Responses ##### Status: 201 Auth request created ###### Content-Type: application/json - **`approval_code`** `string` - **`auth_request_id`** `string` - **`authorize_url`** `string` - **`expires_at`** `integer` - **`mode`** `string`, possible values: `"interactive", "detached"` - **`ok`** `boolean` - **`poll_token`** `string` - **`scopes`** `array` **Items:** `string` **Example:** ```json { "ok": true, "auth_request_id": "req_agent123", "authorize_url": "https://api.agentanalytics.sh/agent-sessions/authorize/req_agent123", "approval_code": "ABCD2345", "poll_token": "aap_polltoken", "mode": "interactive", "expires_at": 1775079600000, "scopes": [ "" ] } ``` ##### Status: 400 Invalid request payload ###### 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" } ``` ### Browser approval page for an auth request - **Method:**Β `GET` - **Path:**Β `/agent-sessions/authorize/{id}` - **Tags:**Β Agent Sessions Human-facing browser entry for agent signup/login approval. The response is HTML and lets the user continue with Google or GitHub OAuth. This endpoint is usually opened from the `authorize_url` returned by `POST /agent-sessions/start`. #### Responses ##### Status: 200 HTML approval page ###### Content-Type: text/html `string` **Example:** ```json true ``` ##### Status: 404 Auth request 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: 410 Auth request expired ###### 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" } ``` ### Poll detached auth status - **Method:**Β `POST` - **Path:**Β `/agent-sessions/poll` - **Tags:**Β Agent Sessions Checks whether a detached auth request has been approved, exchanged, revoked, or expired. #### Request Body ##### Content-Type: application/json - **`auth_request_id` (required)** `string` - **`poll_token` (required)** `string` **Example:** ```json { "auth_request_id": "", "poll_token": "" } ``` #### Responses ##### Status: 200 Auth request status ###### Content-Type: application/json - **`account_id`** `object` - **`approved_email`** `object` - **`exchange_code`** `object` - **`status`** `string`, possible values: `"pending", "approved", "exchanged", "revoked", "expired"` **Example:** ```json { "status": "pending", "exchange_code": "", "account_id": "", "approved_email": "" } ``` ##### Status: 401 Invalid poll token or auth request ###### 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: 410 Auth request revoked or expired ###### Content-Type: application/json - **`account_id`** `object` - **`approved_email`** `object` - **`exchange_code`** `object` - **`status`** `string`, possible values: `"pending", "approved", "exchanged", "revoked", "expired"` **Example:** ```json { "status": "pending", "exchange_code": "", "account_id": "", "approved_email": "" } ``` ### Exchange an approved auth request for an agent session - **Method:**Β `POST` - **Path:**Β `/agent-sessions/exchange` - **Tags:**Β Agent Sessions Redeems an approved auth request into a renewable agent session. Interactive runtimes should send the matching `code_verifier` when the start request included a `code_challenge`. #### Request Body ##### Content-Type: application/json - **`auth_request_id` (required)** `string` - **`exchange_code` (required)** `string` - **`code_verifier`** `string | null` β€” Required when the auth request was started with a code challenge. **Example:** ```json { "auth_request_id": "", "exchange_code": "", "code_verifier": null } ``` #### Responses ##### Status: 200 Agent session issued ###### Content-Type: application/json - **`account`** `object` - **`email`** `string` - **`github_login`** `object` - **`google_name`** `object` - **`id`** `string` - **`tier`** `string`, possible values: `"free", "pro"` - **`agent_session`** `object` - **`access_expires_at`** `integer` - **`access_token`** `string` - **`id`** `string` - **`refresh_expires_at`** `integer` - **`refresh_token`** `string` - **`scopes`** `array` **Items:** `string` - **`ok`** `boolean` **Example:** ```json { "ok": true, "agent_session": { "id": "", "access_token": "aas_123", "refresh_token": "aar_123", "access_expires_at": 1, "refresh_expires_at": 1, "scopes": [ "" ] }, "account": { "id": "", "email": "", "github_login": "", "google_name": "", "tier": "free" } } ``` ##### Status: 400 Auth request is not ready ###### 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 exchange code or code verifier ###### 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 Auth request has already been exchanged ###### 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: 410 Auth request expired ###### 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" } ``` ### Refresh an agent session access token - **Method:**Β `POST` - **Path:**Β `/agent-sessions/refresh` - **Tags:**Β Agent Sessions Exchanges a valid refresh token for a new access token while keeping the same logical agent session. #### Request Body ##### Content-Type: application/json - **`refresh_token` (required)** `string` **Example:** ```json { "refresh_token": "" } ``` #### Responses ##### Status: 200 Session refreshed ###### Content-Type: application/json - **`agent_session`** `object` - **`access_expires_at`** `integer` - **`access_token`** `string` - **`account`** `object` - **`email`** `string` - **`github_login`** `object` - **`google_name`** `object` - **`id`** `string` - **`tier`** `string`, possible values: `"free", "pro"` - **`refresh_expires_at`** `integer` - **`refresh_token`** `string` - **`scopes`** `array` **Items:** `string` - **`session_id`** `string` - **`ok`** `boolean` **Example:** ```json { "ok": true, "agent_session": { "session_id": "", "access_token": "", "refresh_token": "", "access_expires_at": 1, "refresh_expires_at": 1, "account": { "id": "", "email": "", "github_login": "", "google_name": "", "tier": "free" }, "scopes": [ "" ] } } ``` ##### Status: 400 Missing refresh 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: 401 Invalid or expired refresh 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" } ``` ### Revoke an agent session - **Method:**Β `POST` - **Path:**Β `/agent-sessions/revoke` - **Tags:**Β Agent Sessions Revokes one active agent session owned by the authenticated account. #### Request Body ##### Content-Type: application/json - **`session_id` (required)** `string` **Example:** ```json { "session_id": "" } ``` #### Responses ##### Status: 200 Session revoked ###### Content-Type: application/json - **`ok`** `boolean` - **`session_id`** `string` **Example:** ```json { "ok": true, "session_id": "" } ``` ##### Status: 401 Missing or invalid account auth ###### 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 Session 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" } ``` ### 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. If a client buffers more than 100 events, such as while waiting for tracking consent, split the buffer into multiple `/track/batch` requests. 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` Property-based grouping such as `properties.hostname`, `properties.first_utm_source`, or `properties.cta` is not supported. **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`, `properties.first_utm_source`) Built-in fields are a closed list. Common analytics properties such as `referrer`, `utm_source`, `path`, `browser`, and `hostname` must be queried as `properties.referrer`, `properties.utm_source`, `properties.path`, `properties.browser`, and `properties.hostname`. Invalid filter fields fail the query with `400` and include `/properties`-style discovery guidance instead of being silently ignored. **Count modes:** `session_then_user` (default for `event_count`) or `raw` `/events` remains lossless and returns every ingested row. `/query` uses `session_then_user` as the default for `event_count`: session-backed rows count by session, no-session rows fall back to `user_id` only when that user has no session-backed row in the same filtered/grouped result set, and fully anonymous rows fall back to event `id`. `count_mode` is ignored for queries without `event_count`. Use `count_mode: raw` when the question is about ingestion volume or debugging raw writes. **CLI:** `npx @agent-analytics/cli query 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 - **`count_mode`** `string`, possible values: `"raw", "session_then_user"` β€” How \`event\_count\` is aggregated. Default for \`event\_count\`: \`session\_then\_user\`. Session-backed rows count by session, no-session rows fall back to user only when that user has no session-backed row in the same group, and fully anonymous rows fall back to event id. Ignored for queries without \`event\_count\`. Use \`raw\` to count ingested rows without dedupe. - **`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\`. You can also filter on any \`properties.\*\` field, including first-touch attribution fields like \`properties.first\_utm\_source\`. - **`op`** `string`, possible values: `"eq", "neq", "gt", "lt", "gte", "lte", "contains"` - **`value`** `object` β€” Value to compare against - **`group_by`** `array`, default: `[]` β€” Built-in fields to group results by. Property-based grouping such as \`properties.hostname\` is not supported. **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", "count_mode": "raw", "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 --days 7` #### 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" } ``` ### Bounded session path analysis - **Method:**Β `POST` - **Path:**Β `/paths` - **Tags:**Β Paths Analyze session-local journeys from entry pages through mixed page and event nodes, ending in `goal`, `drop_off`, or `truncated`. Each entry page also includes an `exit_pages` attribution table so agents can connect landing-page behavior to the pages where sessions actually ended. This endpoint is explicitly bounded: - fixed `since` values only: `7d`, `14d`, `30d`, `90d` - `max_steps` is capped at 5 - `entry_limit` is capped at 20 - `path_limit` is capped at 10 - `candidate_session_cap` is capped at 10,000 - implementation is limited to two D1 read queries, with Worker-side tree assembly Session attribution is local to the session: the `goal_event` only counts if it occurs in the same session as the entry page. Non-converting terminal nodes are split by `exit_page`, so a branch can show whether the drop-off happened on `/pricing`, `/checkout`, or another exit page. **CLI:** `npx @agent-analytics/cli paths my-site --goal signup --since 30d --max-steps 5` #### Request Body ##### Content-Type: application/json - **`goal_event` (required)** `string` β€” Goal event that must occur in the same session - **`project` (required)** `string` β€” Project name - **`candidate_session_cap`** `integer`, default: `5000` β€” Max sessions scanned before Worker-side path assembly - **`entry_limit`** `integer`, default: `10` β€” Max entry pages to include - **`max_steps`** `integer`, default: `5` β€” Max mixed page/event steps before the path is marked truncated - **`path_limit`** `integer`, default: `5` β€” Max children kept per tree node after aggregation - **`since`** `string`, possible values: `"7d", "14d", "30d", "90d"`, default: `"30d"` β€” Fixed lookback window for bounded path analysis **Example:** ```json { "project": "my-site", "goal_event": "signup", "since": "30d", "max_steps": 5, "entry_limit": 10, "path_limit": 5, "candidate_session_cap": 5000 } ``` #### Responses ##### Status: 200 Aggregated session path tree by entry page ###### Content-Type: application/json - **`bounds`** `object` - **`candidate_session_cap`** `integer` - **`entry_limit`** `integer` - **`max_steps`** `integer` - **`path_limit`** `integer` - **`entry_paths`** `array` **Items:** - **`conversion_rate`** `number` - **`conversions`** `integer` - **`entry_page`** `string` - **`exit_pages`** `array` β€” Exit pages reached by sessions that started on this entry page, with conversion and drop-off attribution. **Items:** - **`conversion_rate`** `number` - **`conversions`** `integer` - **`drop_off_rate`** `number` - **`drop_offs`** `integer` - **`exit_page`** `string` - **`sessions`** `integer` - **`sessions`** `integer` - **`tree`** `array` **Items:** - **`children`** `array` β€” Child path nodes. The runtime shape matches \`PathNode\`; the schema avoids a circular \`$ref\` so generated docs can render reliably. **Items:** - **`conversion_rate`** `number` - **`conversions`** `integer` - **`exit_page`** `string | null` β€” Present on \`drop\_off\` and \`truncated\` terminal nodes to tie path drop-off behavior to the actual session exit page. - **`sessions`** `integer` - **`type`** `string`, possible values: `"page", "event", "goal", "drop_off", "truncated"` - **`value`** `string` β€” For \`drop\_off\` and \`truncated\` terminal nodes, this is the exit page when known, otherwise \`unknown\`. - **`goal_event`** `string` - **`period`** `object` - **`from`** `string`, format: `date` - **`to`** `string`, format: `date` - **`project`** `string` **Example:** ```json { "project": "my-site", "goal_event": "signup", "period": { "from": "2026-03-11", "to": "2026-04-10" }, "bounds": { "max_steps": 5, "entry_limit": 10, "path_limit": 5, "candidate_session_cap": 5000 }, "entry_paths": [ { "entry_page": "/landing", "sessions": 120, "conversions": 32, "conversion_rate": 0.267, "exit_pages": [ { "exit_page": "/pricing", "sessions": 42, "conversions": 13, "conversion_rate": 0.31, "drop_offs": 29, "drop_off_rate": 0.69 } ], "tree": [ { "type": "page", "value": "/pricing", "exit_page": "/pricing", "sessions": 42, "conversions": 13, "conversion_rate": 0.31, "children": [ { "additionalProperty": "anything" } ] } ] } ] } ``` ##### Status: 400 Validation error for out-of-range bounds or missing goal\_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 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" } ``` ### 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. Supports agent-session bearer auth (`Authorization: Bearer aas_...`) or API key auth (`X-API-Key: aak_...`). **curl example:** ```bash curl -N "https://api.agentanalytics.sh/stream?project=my-site&events=page_view,signup" \ -H "Authorization: Bearer aas_..." ``` #### 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 account auth ###### 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). Supports agent-session bearer auth (`Authorization: Bearer aas_...`) or API key auth (`X-API-Key: aak_...`). **curl example:** ```bash curl "https://api.agentanalytics.sh/live?project=my-site&window=60" \ -H "Authorization: Bearer aas_..." ``` #### 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 account auth ###### 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" } ``` ### Analyze product growth measurement blind spots - **Method:**Β `POST` - **Path:**Β `/website-scans` - **Tags:**Β Website Analysis Creates a website analysis. Anonymous requests are root-domain only and return a preview plus a one-analysis `rst_*` resume token. They do not create `aas_*` agent sessions. Signed-in requests can request full analysis with `full: true` or `mode: full`, subject to monthly quota. Public product copy should frame this as the fastest path to useful analytics, not as a generic audit. Recommendations include implementation hints that map to supported tracker.js capabilities instead of duplicate custom events for automatic signals. #### Request Body ##### Content-Type: application/json - **`url` (required)** `string` β€” Website URL to analyze. Anonymous requests are normalized to the root domain. - **`full`** `boolean`, default: `false` β€” Request full analysis. Requires account auth. - **`mode`** `string`, possible values: `"preview", "full"` β€” Alternative way to request preview or full analysis. **Example:** ```json { "url": "https://example.com/pricing", "full": false, "mode": "preview" } ``` #### Responses ##### Status: 200 Preview or full website analysis ###### Content-Type: application/json - **`agent_handoff`** `object` - **`analysis_id`** `string` - **`label`** `string` - **`prompt`** `string` - **`resume_token`** `string | null` - **`analysis_id`** `string` - **`cached`** `boolean` - **`hostname`** `string` - **`mode`** `string` - **`normalized_url`** `string` - **`ok`** `boolean` - **`preview`** `object` β€” Decision-oriented instrumentation plan. Preview responses cap \`minimum\_viable\_instrumentation\` at 3-5 events; full results cap it at 5-8 events. Recommendations are tracker-aware and should not spend custom event slots on automatic page\_view, attribution, device, geography, session, or visitor fields. - **`advanced_tracking_later`** `array` β€” Agent Analytics tracker capabilities to consider after the first useful page event is installed. **Items:** - **`capability`** `string` - **`implementation_hint`** `string` - **`why`** `string` - **`after_install_agent_behavior`** `array` **Items:** `string` - **`analytics_detected`** `object` β€” Supporting context only; the recommendation does not lead with detected providers. - **`business_events_later`** `array` β€” Valuable inferred product, backend, or post-signup events that are not part of the first supplied-page install list. **Items:** - **`event`** `string` - **`where_to_instrument`** `string` - **`why`** `string` - **`current_blindspots`** `array` **Items:** `string` - **`framing`** `string` - **`goal_driven_funnels`** `array` **Items:** - **`bottleneck_detection`** `string` - **`decision`** `string` - **`name`** `string` - **`steps`** `array` **Items:** `string` - **`hostname`** `string` - **`minimum_viable_instrumentation`** `array` **Items:** - **`agent_capability_after_install` (required)** `string` - **`current_blindspot` (required)** `string` - **`event` (required)** `string` β€” Stable snake\_case custom event name. Recommendations should not duplicate automatic tracker signals such as page\_view. - **`implementation_hint` (required)** `string` β€” Tracker-aware implementation guidance, for example data-aa-event attributes, data-aa-impression attributes, window\.aa.track(...), server-side tracking for durable outcomes, aa.identify()/aa.set(...) after auth, or script opt-ins when they unlock a concrete decision. - **`priority` (required)** `integer` - **`properties` (required)** `array` β€” Practical property names to capture with the event. Automatic tracker properties such as path, referrer, UTMs, device fields, country, session IDs, and first-touch attribution are already present on every event. **Items:** `string` - **`unlocks_questions` (required)** `array` **Items:** `string` - **`why_this_matters_now` (required)** `string` - **`normalized_url`** `string` - **`not_needed_yet`** `array` **Items:** - **`event`** `string` - **`reason`** `string` - **`revisit_when`** `string` - **`suggested_ab_tests`** `array` β€” Compact experiment ideas tied to the recommended page UI events. **Items:** - **`hypothesis`** `string` - **`name`** `string` - **`success_event`** `string` - **`variants`** `array` **Items:** `string` - **`result`** `object` β€” Decision-oriented instrumentation plan. Preview responses cap \`minimum\_viable\_instrumentation\` at 3-5 events; full results cap it at 5-8 events. Recommendations are tracker-aware and should not spend custom event slots on automatic page\_view, attribution, device, geography, session, or visitor fields. - **`advanced_tracking_later`** `array` β€” Agent Analytics tracker capabilities to consider after the first useful page event is installed. **Items:** - **`capability`** `string` - **`implementation_hint`** `string` - **`why`** `string` - **`after_install_agent_behavior`** `array` **Items:** `string` - **`analytics_detected`** `object` β€” Supporting context only; the recommendation does not lead with detected providers. - **`business_events_later`** `array` β€” Valuable inferred product, backend, or post-signup events that are not part of the first supplied-page install list. **Items:** - **`event`** `string` - **`where_to_instrument`** `string` - **`why`** `string` - **`current_blindspots`** `array` **Items:** `string` - **`framing`** `string` - **`goal_driven_funnels`** `array` **Items:** - **`bottleneck_detection`** `string` - **`decision`** `string` - **`name`** `string` - **`steps`** `array` **Items:** `string` - **`hostname`** `string` - **`minimum_viable_instrumentation`** `array` **Items:** - **`agent_capability_after_install` (required)** `string` - **`current_blindspot` (required)** `string` - **`event` (required)** `string` β€” Stable snake\_case custom event name. Recommendations should not duplicate automatic tracker signals such as page\_view. - **`implementation_hint` (required)** `string` β€” Tracker-aware implementation guidance, for example data-aa-event attributes, data-aa-impression attributes, window\.aa.track(...), server-side tracking for durable outcomes, aa.identify()/aa.set(...) after auth, or script opt-ins when they unlock a concrete decision. - **`priority` (required)** `integer` - **`properties` (required)** `array` β€” Practical property names to capture with the event. Automatic tracker properties such as path, referrer, UTMs, device fields, country, session IDs, and first-touch attribution are already present on every event. **Items:** `string` - **`unlocks_questions` (required)** `array` **Items:** `string` - **`why_this_matters_now` (required)** `string` - **`normalized_url`** `string` - **`not_needed_yet`** `array` **Items:** - **`event`** `string` - **`reason`** `string` - **`revisit_when`** `string` - **`suggested_ab_tests`** `array` β€” Compact experiment ideas tied to the recommended page UI events. **Items:** - **`hypothesis`** `string` - **`name`** `string` - **`success_event`** `string` - **`variants`** `array` **Items:** `string` - **`resume_token`** `string` β€” Present only when creating a new anonymous preview. Store the token securely; the server stores only its hash. - **`status`** `string`, possible values: `"pending", "running", "succeeded", "failed"` **Example:** ```json { "ok": true, "analysis_id": "scan_...", "status": "succeeded", "mode": "anonymous_preview", "normalized_url": "https://example.com/", "hostname": "example.com", "cached": true, "resume_token": "rst_...", "preview": { "framing": "fastest_path_to_useful_analytics", "normalized_url": "https://example.com/", "hostname": "example.com", "current_blindspots": [ "" ], "minimum_viable_instrumentation": [ { "event": "signup_started", "priority": 1, "why_this_matters_now": "This marks the moment intent becomes commitment.", "current_blindspot": "You cannot see whether CTA clicks turn into signup attempts.", "unlocks_questions": [ "Which pages create signup starts?", "Where does signup intent drop?" ], "agent_capability_after_install": "Your agent can detect where intent drops before account creation.", "properties": [ "page_path", "entry_source", "auth_method" ], "implementation_hint": "" } ], "not_needed_yet": [ { "event": "", "reason": "", "revisit_when": "" } ], "goal_driven_funnels": [ { "name": "", "steps": [ "" ], "decision": "", "bottleneck_detection": "" } ], "after_install_agent_behavior": [ "" ], "business_events_later": [ { "event": "", "why": "", "where_to_instrument": "" } ], "advanced_tracking_later": [ { "capability": "data-aa-impression", "why": "", "implementation_hint": "" } ], "suggested_ab_tests": [ { "name": "", "hypothesis": "", "variants": [ "" ], "success_event": "" } ], "analytics_detected": {} }, "result": { "framing": "fastest_path_to_useful_analytics", "normalized_url": "https://example.com/", "hostname": "example.com", "current_blindspots": [ "" ], "minimum_viable_instrumentation": [ { "event": "signup_started", "priority": 1, "why_this_matters_now": "This marks the moment intent becomes commitment.", "current_blindspot": "You cannot see whether CTA clicks turn into signup attempts.", "unlocks_questions": [ "Which pages create signup starts?", "Where does signup intent drop?" ], "agent_capability_after_install": "Your agent can detect where intent drops before account creation.", "properties": [ "page_path", "entry_source", "auth_method" ], "implementation_hint": "" } ], "not_needed_yet": [ { "event": "", "reason": "", "revisit_when": "" } ], "goal_driven_funnels": [ { "name": "", "steps": [ "" ], "decision": "", "bottleneck_detection": "" } ], "after_install_agent_behavior": [ "" ], "business_events_later": [ { "event": "", "why": "", "where_to_instrument": "" } ], "advanced_tracking_later": [ { "capability": "data-aa-impression", "why": "", "implementation_hint": "" } ], "suggested_ab_tests": [ { "name": "", "hypothesis": "", "variants": [ "" ], "success_event": "" } ], "analytics_detected": {} }, "agent_handoff": { "label": "Give your agent analytics judgment", "analysis_id": "", "resume_token": null, "prompt": "" } } ``` ##### Status: 400 Invalid or unsafe website URL ###### 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 Login required for full analysis ###### 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 Anonymous analyzer busy, cooldown active, or quota reached ###### Content-Type: application/json **All of:** - **`error` (required)** `string` β€” Machine-readable error code - **`message` (required)** `string` β€” Human-readable error message * **`retry_after_seconds`** `integer` **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required", "retry_after_seconds": 30 } ``` ##### Status: 502 Downstream scanner failed safely ###### 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" } ``` ### Resume a website analysis - **Method:**Β `GET` - **Path:**Β `/website-scans/{id}` - **Tags:**Β Website Analysis Returns a previously created analysis. Account owners can read their own analyses. Anonymous previews require the matching `resume_token` query parameter. #### Responses ##### Status: 200 Website analysis ###### Content-Type: application/json - **`agent_handoff`** `object` - **`analysis_id`** `string` - **`label`** `string` - **`prompt`** `string` - **`resume_token`** `string | null` - **`analysis_id`** `string` - **`cached`** `boolean` - **`hostname`** `string` - **`mode`** `string` - **`normalized_url`** `string` - **`ok`** `boolean` - **`preview`** `object` β€” Decision-oriented instrumentation plan. Preview responses cap \`minimum\_viable\_instrumentation\` at 3-5 events; full results cap it at 5-8 events. Recommendations are tracker-aware and should not spend custom event slots on automatic page\_view, attribution, device, geography, session, or visitor fields. - **`advanced_tracking_later`** `array` β€” Agent Analytics tracker capabilities to consider after the first useful page event is installed. **Items:** - **`capability`** `string` - **`implementation_hint`** `string` - **`why`** `string` - **`after_install_agent_behavior`** `array` **Items:** `string` - **`analytics_detected`** `object` β€” Supporting context only; the recommendation does not lead with detected providers. - **`business_events_later`** `array` β€” Valuable inferred product, backend, or post-signup events that are not part of the first supplied-page install list. **Items:** - **`event`** `string` - **`where_to_instrument`** `string` - **`why`** `string` - **`current_blindspots`** `array` **Items:** `string` - **`framing`** `string` - **`goal_driven_funnels`** `array` **Items:** - **`bottleneck_detection`** `string` - **`decision`** `string` - **`name`** `string` - **`steps`** `array` **Items:** `string` - **`hostname`** `string` - **`minimum_viable_instrumentation`** `array` **Items:** - **`agent_capability_after_install` (required)** `string` - **`current_blindspot` (required)** `string` - **`event` (required)** `string` β€” Stable snake\_case custom event name. Recommendations should not duplicate automatic tracker signals such as page\_view. - **`implementation_hint` (required)** `string` β€” Tracker-aware implementation guidance, for example data-aa-event attributes, data-aa-impression attributes, window\.aa.track(...), server-side tracking for durable outcomes, aa.identify()/aa.set(...) after auth, or script opt-ins when they unlock a concrete decision. - **`priority` (required)** `integer` - **`properties` (required)** `array` β€” Practical property names to capture with the event. Automatic tracker properties such as path, referrer, UTMs, device fields, country, session IDs, and first-touch attribution are already present on every event. **Items:** `string` - **`unlocks_questions` (required)** `array` **Items:** `string` - **`why_this_matters_now` (required)** `string` - **`normalized_url`** `string` - **`not_needed_yet`** `array` **Items:** - **`event`** `string` - **`reason`** `string` - **`revisit_when`** `string` - **`suggested_ab_tests`** `array` β€” Compact experiment ideas tied to the recommended page UI events. **Items:** - **`hypothesis`** `string` - **`name`** `string` - **`success_event`** `string` - **`variants`** `array` **Items:** `string` - **`result`** `object` β€” Decision-oriented instrumentation plan. Preview responses cap \`minimum\_viable\_instrumentation\` at 3-5 events; full results cap it at 5-8 events. Recommendations are tracker-aware and should not spend custom event slots on automatic page\_view, attribution, device, geography, session, or visitor fields. - **`advanced_tracking_later`** `array` β€” Agent Analytics tracker capabilities to consider after the first useful page event is installed. **Items:** - **`capability`** `string` - **`implementation_hint`** `string` - **`why`** `string` - **`after_install_agent_behavior`** `array` **Items:** `string` - **`analytics_detected`** `object` β€” Supporting context only; the recommendation does not lead with detected providers. - **`business_events_later`** `array` β€” Valuable inferred product, backend, or post-signup events that are not part of the first supplied-page install list. **Items:** - **`event`** `string` - **`where_to_instrument`** `string` - **`why`** `string` - **`current_blindspots`** `array` **Items:** `string` - **`framing`** `string` - **`goal_driven_funnels`** `array` **Items:** - **`bottleneck_detection`** `string` - **`decision`** `string` - **`name`** `string` - **`steps`** `array` **Items:** `string` - **`hostname`** `string` - **`minimum_viable_instrumentation`** `array` **Items:** - **`agent_capability_after_install` (required)** `string` - **`current_blindspot` (required)** `string` - **`event` (required)** `string` β€” Stable snake\_case custom event name. Recommendations should not duplicate automatic tracker signals such as page\_view. - **`implementation_hint` (required)** `string` β€” Tracker-aware implementation guidance, for example data-aa-event attributes, data-aa-impression attributes, window\.aa.track(...), server-side tracking for durable outcomes, aa.identify()/aa.set(...) after auth, or script opt-ins when they unlock a concrete decision. - **`priority` (required)** `integer` - **`properties` (required)** `array` β€” Practical property names to capture with the event. Automatic tracker properties such as path, referrer, UTMs, device fields, country, session IDs, and first-touch attribution are already present on every event. **Items:** `string` - **`unlocks_questions` (required)** `array` **Items:** `string` - **`why_this_matters_now` (required)** `string` - **`normalized_url`** `string` - **`not_needed_yet`** `array` **Items:** - **`event`** `string` - **`reason`** `string` - **`revisit_when`** `string` - **`suggested_ab_tests`** `array` β€” Compact experiment ideas tied to the recommended page UI events. **Items:** - **`hypothesis`** `string` - **`name`** `string` - **`success_event`** `string` - **`variants`** `array` **Items:** `string` - **`resume_token`** `string` β€” Present only when creating a new anonymous preview. Store the token securely; the server stores only its hash. - **`status`** `string`, possible values: `"pending", "running", "succeeded", "failed"` **Example:** ```json { "ok": true, "analysis_id": "scan_...", "status": "succeeded", "mode": "anonymous_preview", "normalized_url": "https://example.com/", "hostname": "example.com", "cached": true, "resume_token": "rst_...", "preview": { "framing": "fastest_path_to_useful_analytics", "normalized_url": "https://example.com/", "hostname": "example.com", "current_blindspots": [ "" ], "minimum_viable_instrumentation": [ { "event": "signup_started", "priority": 1, "why_this_matters_now": "This marks the moment intent becomes commitment.", "current_blindspot": "You cannot see whether CTA clicks turn into signup attempts.", "unlocks_questions": [ "Which pages create signup starts?", "Where does signup intent drop?" ], "agent_capability_after_install": "Your agent can detect where intent drops before account creation.", "properties": [ "page_path", "entry_source", "auth_method" ], "implementation_hint": "" } ], "not_needed_yet": [ { "event": "", "reason": "", "revisit_when": "" } ], "goal_driven_funnels": [ { "name": "", "steps": [ "" ], "decision": "", "bottleneck_detection": "" } ], "after_install_agent_behavior": [ "" ], "business_events_later": [ { "event": "", "why": "", "where_to_instrument": "" } ], "advanced_tracking_later": [ { "capability": "data-aa-impression", "why": "", "implementation_hint": "" } ], "suggested_ab_tests": [ { "name": "", "hypothesis": "", "variants": [ "" ], "success_event": "" } ], "analytics_detected": {} }, "result": { "framing": "fastest_path_to_useful_analytics", "normalized_url": "https://example.com/", "hostname": "example.com", "current_blindspots": [ "" ], "minimum_viable_instrumentation": [ { "event": "signup_started", "priority": 1, "why_this_matters_now": "This marks the moment intent becomes commitment.", "current_blindspot": "You cannot see whether CTA clicks turn into signup attempts.", "unlocks_questions": [ "Which pages create signup starts?", "Where does signup intent drop?" ], "agent_capability_after_install": "Your agent can detect where intent drops before account creation.", "properties": [ "page_path", "entry_source", "auth_method" ], "implementation_hint": "" } ], "not_needed_yet": [ { "event": "", "reason": "", "revisit_when": "" } ], "goal_driven_funnels": [ { "name": "", "steps": [ "" ], "decision": "", "bottleneck_detection": "" } ], "after_install_agent_behavior": [ "" ], "business_events_later": [ { "event": "", "why": "", "where_to_instrument": "" } ], "advanced_tracking_later": [ { "capability": "data-aa-impression", "why": "", "implementation_hint": "" } ], "suggested_ab_tests": [ { "name": "", "hypothesis": "", "variants": [ "" ], "success_event": "" } ], "analytics_detected": {} }, "agent_handoff": { "label": "Give your agent analytics judgment", "analysis_id": "", "resume_token": null, "prompt": "" } } ``` ##### Status: 404 Analysis not found or token/account mismatch ###### 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" } ``` ### Claim and upgrade an analysis - **Method:**Β `POST` - **Path:**Β `/website-scans/{id}/upgrade` - **Tags:**Β Website Analysis Claims an anonymous preview for the authenticated account and returns the full instrumentation plan. If the analysis is already owned by the account, the resume token is not required. Project linking is done separately by creating a project with `source_scan_id` or by hosted project-linking helpers. A resume token alone cannot attach an analysis to a project. #### Request Body ##### Content-Type: application/json - **`project`** `string` β€” Optional project name used by CLI flows while upgrading. - **`resume_token`** `string` β€” One-analysis \`rst\_\*\` resume token from an anonymous preview. **Example:** ```json { "resume_token": "rst_...", "project": "my-site" } ``` #### Responses ##### Status: 200 Full website analysis ###### Content-Type: application/json - **`agent_handoff`** `object` - **`analysis_id`** `string` - **`label`** `string` - **`prompt`** `string` - **`resume_token`** `string | null` - **`analysis_id`** `string` - **`cached`** `boolean` - **`hostname`** `string` - **`mode`** `string` - **`normalized_url`** `string` - **`ok`** `boolean` - **`preview`** `object` β€” Decision-oriented instrumentation plan. Preview responses cap \`minimum\_viable\_instrumentation\` at 3-5 events; full results cap it at 5-8 events. Recommendations are tracker-aware and should not spend custom event slots on automatic page\_view, attribution, device, geography, session, or visitor fields. - **`advanced_tracking_later`** `array` β€” Agent Analytics tracker capabilities to consider after the first useful page event is installed. **Items:** - **`capability`** `string` - **`implementation_hint`** `string` - **`why`** `string` - **`after_install_agent_behavior`** `array` **Items:** `string` - **`analytics_detected`** `object` β€” Supporting context only; the recommendation does not lead with detected providers. - **`business_events_later`** `array` β€” Valuable inferred product, backend, or post-signup events that are not part of the first supplied-page install list. **Items:** - **`event`** `string` - **`where_to_instrument`** `string` - **`why`** `string` - **`current_blindspots`** `array` **Items:** `string` - **`framing`** `string` - **`goal_driven_funnels`** `array` **Items:** - **`bottleneck_detection`** `string` - **`decision`** `string` - **`name`** `string` - **`steps`** `array` **Items:** `string` - **`hostname`** `string` - **`minimum_viable_instrumentation`** `array` **Items:** - **`agent_capability_after_install` (required)** `string` - **`current_blindspot` (required)** `string` - **`event` (required)** `string` β€” Stable snake\_case custom event name. Recommendations should not duplicate automatic tracker signals such as page\_view. - **`implementation_hint` (required)** `string` β€” Tracker-aware implementation guidance, for example data-aa-event attributes, data-aa-impression attributes, window\.aa.track(...), server-side tracking for durable outcomes, aa.identify()/aa.set(...) after auth, or script opt-ins when they unlock a concrete decision. - **`priority` (required)** `integer` - **`properties` (required)** `array` β€” Practical property names to capture with the event. Automatic tracker properties such as path, referrer, UTMs, device fields, country, session IDs, and first-touch attribution are already present on every event. **Items:** `string` - **`unlocks_questions` (required)** `array` **Items:** `string` - **`why_this_matters_now` (required)** `string` - **`normalized_url`** `string` - **`not_needed_yet`** `array` **Items:** - **`event`** `string` - **`reason`** `string` - **`revisit_when`** `string` - **`suggested_ab_tests`** `array` β€” Compact experiment ideas tied to the recommended page UI events. **Items:** - **`hypothesis`** `string` - **`name`** `string` - **`success_event`** `string` - **`variants`** `array` **Items:** `string` - **`result`** `object` β€” Decision-oriented instrumentation plan. Preview responses cap \`minimum\_viable\_instrumentation\` at 3-5 events; full results cap it at 5-8 events. Recommendations are tracker-aware and should not spend custom event slots on automatic page\_view, attribution, device, geography, session, or visitor fields. - **`advanced_tracking_later`** `array` β€” Agent Analytics tracker capabilities to consider after the first useful page event is installed. **Items:** - **`capability`** `string` - **`implementation_hint`** `string` - **`why`** `string` - **`after_install_agent_behavior`** `array` **Items:** `string` - **`analytics_detected`** `object` β€” Supporting context only; the recommendation does not lead with detected providers. - **`business_events_later`** `array` β€” Valuable inferred product, backend, or post-signup events that are not part of the first supplied-page install list. **Items:** - **`event`** `string` - **`where_to_instrument`** `string` - **`why`** `string` - **`current_blindspots`** `array` **Items:** `string` - **`framing`** `string` - **`goal_driven_funnels`** `array` **Items:** - **`bottleneck_detection`** `string` - **`decision`** `string` - **`name`** `string` - **`steps`** `array` **Items:** `string` - **`hostname`** `string` - **`minimum_viable_instrumentation`** `array` **Items:** - **`agent_capability_after_install` (required)** `string` - **`current_blindspot` (required)** `string` - **`event` (required)** `string` β€” Stable snake\_case custom event name. Recommendations should not duplicate automatic tracker signals such as page\_view. - **`implementation_hint` (required)** `string` β€” Tracker-aware implementation guidance, for example data-aa-event attributes, data-aa-impression attributes, window\.aa.track(...), server-side tracking for durable outcomes, aa.identify()/aa.set(...) after auth, or script opt-ins when they unlock a concrete decision. - **`priority` (required)** `integer` - **`properties` (required)** `array` β€” Practical property names to capture with the event. Automatic tracker properties such as path, referrer, UTMs, device fields, country, session IDs, and first-touch attribution are already present on every event. **Items:** `string` - **`unlocks_questions` (required)** `array` **Items:** `string` - **`why_this_matters_now` (required)** `string` - **`normalized_url`** `string` - **`not_needed_yet`** `array` **Items:** - **`event`** `string` - **`reason`** `string` - **`revisit_when`** `string` - **`suggested_ab_tests`** `array` β€” Compact experiment ideas tied to the recommended page UI events. **Items:** - **`hypothesis`** `string` - **`name`** `string` - **`success_event`** `string` - **`variants`** `array` **Items:** `string` - **`resume_token`** `string` β€” Present only when creating a new anonymous preview. Store the token securely; the server stores only its hash. - **`status`** `string`, possible values: `"pending", "running", "succeeded", "failed"` **Example:** ```json { "ok": true, "analysis_id": "scan_...", "status": "succeeded", "mode": "anonymous_preview", "normalized_url": "https://example.com/", "hostname": "example.com", "cached": true, "resume_token": "rst_...", "preview": { "framing": "fastest_path_to_useful_analytics", "normalized_url": "https://example.com/", "hostname": "example.com", "current_blindspots": [ "" ], "minimum_viable_instrumentation": [ { "event": "signup_started", "priority": 1, "why_this_matters_now": "This marks the moment intent becomes commitment.", "current_blindspot": "You cannot see whether CTA clicks turn into signup attempts.", "unlocks_questions": [ "Which pages create signup starts?", "Where does signup intent drop?" ], "agent_capability_after_install": "Your agent can detect where intent drops before account creation.", "properties": [ "page_path", "entry_source", "auth_method" ], "implementation_hint": "" } ], "not_needed_yet": [ { "event": "", "reason": "", "revisit_when": "" } ], "goal_driven_funnels": [ { "name": "", "steps": [ "" ], "decision": "", "bottleneck_detection": "" } ], "after_install_agent_behavior": [ "" ], "business_events_later": [ { "event": "", "why": "", "where_to_instrument": "" } ], "advanced_tracking_later": [ { "capability": "data-aa-impression", "why": "", "implementation_hint": "" } ], "suggested_ab_tests": [ { "name": "", "hypothesis": "", "variants": [ "" ], "success_event": "" } ], "analytics_detected": {} }, "result": { "framing": "fastest_path_to_useful_analytics", "normalized_url": "https://example.com/", "hostname": "example.com", "current_blindspots": [ "" ], "minimum_viable_instrumentation": [ { "event": "signup_started", "priority": 1, "why_this_matters_now": "This marks the moment intent becomes commitment.", "current_blindspot": "You cannot see whether CTA clicks turn into signup attempts.", "unlocks_questions": [ "Which pages create signup starts?", "Where does signup intent drop?" ], "agent_capability_after_install": "Your agent can detect where intent drops before account creation.", "properties": [ "page_path", "entry_source", "auth_method" ], "implementation_hint": "" } ], "not_needed_yet": [ { "event": "", "reason": "", "revisit_when": "" } ], "goal_driven_funnels": [ { "name": "", "steps": [ "" ], "decision": "", "bottleneck_detection": "" } ], "after_install_agent_behavior": [ "" ], "business_events_later": [ { "event": "", "why": "", "where_to_instrument": "" } ], "advanced_tracking_later": [ { "capability": "data-aa-impression", "why": "", "implementation_hint": "" } ], "suggested_ab_tests": [ { "name": "", "hypothesis": "", "variants": [ "" ], "success_event": "" } ], "analytics_detected": {} }, "agent_handoff": { "label": "Give your agent analytics judgment", "analysis_id": "", "resume_token": null, "prompt": "" } } ``` ##### Status: 401 Login 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 Analysis not found or resume token mismatch ###### 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 Monthly full-analysis quota reached ###### Content-Type: application/json **All of:** - **`error` (required)** `string` β€” Machine-readable error code - **`message` (required)** `string` β€” Human-readable error message * **`retry_after_seconds`** `integer` **Example:** ```json { "error": "AUTH_REQUIRED", "message": "API key required", "retry_after_seconds": 30 } ``` ### 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` - **`source_scan_id`** `string | null` β€” Website analysis id linked during project creation, when present. - **`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, "source_scan_id": null } ], "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). Pass `source_scan_id` when creating the project from a website analysis so activation can be tied back to the recommended instrumentation plan. **CLI:** `npx @agent-analytics/cli create my-new-site --domain https://example.com --source-scan scan_...` #### 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. - **`source_scan_id`** `string` β€” Optional website analysis id to link to this project. Requires account ownership and hostname/origin match. **Example:** ```json { "name": "my-new-site", "allowed_origins": "https://example.com", "source_scan_id": "scan_..." } ``` #### 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 - **`source_scan_id`** `string | null` **Example:** ```json { "id": "", "name": "", "project_token": "aat_...", "allowed_origins": "", "source_scan_id": null, "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 a real-time snapshot of one project. 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" } ``` ### List active and historical agent sessions for the account - **Method:**Β `GET` - **Path:**Β `/account/agent-sessions` - **Tags:**Β Agent Sessions Returns agent sessions created for CLI, MCP, and managed runtimes under the authenticated account. #### Responses ##### Status: 200 Agent sessions for the account ###### Content-Type: application/json - **`sessions`** `array` **Items:** - **`access_expires_at`** `integer` - **`client_instance_id`** `object` - **`client_name`** `object` - **`client_type`** `string` - **`created_at`** `integer` - **`id`** `string` - **`label`** `object` - **`last_used_at`** `integer` - **`refresh_expires_at`** `integer` - **`revoked_at`** `object` - **`scopes`** `array` **Items:** `string` - **`status`** `string`, possible values: `"active", "revoked"` **Example:** ```json { "sessions": [ { "id": "", "client_type": "", "client_name": "", "client_instance_id": "", "label": "", "scopes": [ "" ], "status": "active", "created_at": 1, "last_used_at": 1, "access_expires_at": 1, "refresh_expires_at": 1, "revoked_at": 1 } ] } ``` ##### Status: 401 Missing or invalid account auth ###### 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. This route does not accept scoped agent-session bearer tokens. #### 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" } ``` ##### Status: 403 Agent-session bearer auth is not allowed for raw API-key rotation ###### 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" } ``` ### Submit product or workflow feedback - **Method:**Β `POST` - **Path:**Β `/account/feedback` - **Tags:**Β Account Sends feedback about Agent Analytics product gaps, confusing workflows, or missing capabilities to the internal review channel. This endpoint is meant for sanitized summaries of what was hard and what Agent Analytics should have done instead. Do not include private owner details, secrets, API keys, or raw customer data. **CLI:** `npx @agent-analytics/cli feedback --message "..."` #### Request Body ##### Content-Type: application/json - **`message` (required)** `string` β€” Sanitized summary of the friction, missing capability, or workflow problem. - **`command`** `string` β€” Optional CLI command or workflow step that led to the issue. - **`context`** `string` β€” Optional sanitized context. Do not include private owner details, secrets, or raw customer data. - **`project`** `string` β€” Optional project name for context. **Example:** ```json { "message": "The agent had to calculate funnel drop-off manually instead of using a built-in report.", "project": "my-site", "command": "agent-analytics funnel my-site --steps \"page_view,signup,purchase\"", "context": "The agent understood the goal but had to compute the percentages manually." } ``` #### Responses ##### Status: 200 Feedback accepted ###### Content-Type: application/json - **`ok`** `boolean` **Example:** ```json { "ok": true } ``` ##### Status: 400 Invalid JSON or validation error ###### 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" } ``` ### 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 * **`count_mode`** `string`, possible values: `"raw", "session_then_user"` β€” How \`event\_count\` is aggregated. Default for \`event\_count\`: \`session\_then\_user\`. Session-backed rows count by session, no-session rows fall back to user only when that user has no session-backed row in the same group, and fully anonymous rows fall back to event id. Ignored for queries without \`event\_count\`. Use \`raw\` to count ingested rows without dedupe. * **`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\`. You can also filter on any \`properties.\*\` field, including first-touch attribution fields like \`properties.first\_utm\_source\`. - **`op`** `string`, possible values: `"eq", "neq", "gt", "lt", "gte", "lte", "contains"` - **`value`** `object` β€” Value to compare against * **`group_by`** `array`, default: `[]` β€” Built-in fields to group results by. Property-based grouping such as \`properties.hostname\` is not supported. **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", "count_mode": "raw", "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 } ] } ``` ### PathsRequest - **Type:**`object` * **`goal_event` (required)** `string` β€” Goal event that must occur in the same session * **`project` (required)** `string` β€” Project name * **`candidate_session_cap`** `integer`, default: `5000` β€” Max sessions scanned before Worker-side path assembly * **`entry_limit`** `integer`, default: `10` β€” Max entry pages to include * **`max_steps`** `integer`, default: `5` β€” Max mixed page/event steps before the path is marked truncated * **`path_limit`** `integer`, default: `5` β€” Max children kept per tree node after aggregation * **`since`** `string`, possible values: `"7d", "14d", "30d", "90d"`, default: `"30d"` β€” Fixed lookback window for bounded path analysis **Example:** ```json { "project": "my-site", "goal_event": "signup", "since": "30d", "max_steps": 5, "entry_limit": 10, "path_limit": 5, "candidate_session_cap": 5000 } ``` ### PathsBounds - **Type:**`object` * **`candidate_session_cap`** `integer` * **`entry_limit`** `integer` * **`max_steps`** `integer` * **`path_limit`** `integer` **Example:** ```json { "max_steps": 5, "entry_limit": 10, "path_limit": 5, "candidate_session_cap": 5000 } ``` ### PathNode - **Type:**`object` * **`children`** `array` β€” Child path nodes. The runtime shape matches \`PathNode\`; the schema avoids a circular \`$ref\` so generated docs can render reliably. **Items:** * **`conversion_rate`** `number` * **`conversions`** `integer` * **`exit_page`** `string | null` β€” Present on \`drop\_off\` and \`truncated\` terminal nodes to tie path drop-off behavior to the actual session exit page. * **`sessions`** `integer` * **`type`** `string`, possible values: `"page", "event", "goal", "drop_off", "truncated"` * **`value`** `string` β€” For \`drop\_off\` and \`truncated\` terminal nodes, this is the exit page when known, otherwise \`unknown\`. **Example:** ```json { "type": "page", "value": "/pricing", "exit_page": "/pricing", "sessions": 42, "conversions": 13, "conversion_rate": 0.31, "children": [ { "additionalProperty": "anything" } ] } ``` ### PathExitPage - **Type:**`object` * **`conversion_rate`** `number` * **`conversions`** `integer` * **`drop_off_rate`** `number` * **`drop_offs`** `integer` * **`exit_page`** `string` * **`sessions`** `integer` **Example:** ```json { "exit_page": "/pricing", "sessions": 42, "conversions": 13, "conversion_rate": 0.31, "drop_offs": 29, "drop_off_rate": 0.69 } ``` ### PathEntry - **Type:**`object` * **`conversion_rate`** `number` * **`conversions`** `integer` * **`entry_page`** `string` * **`exit_pages`** `array` β€” Exit pages reached by sessions that started on this entry page, with conversion and drop-off attribution. **Items:** - **`conversion_rate`** `number` - **`conversions`** `integer` - **`drop_off_rate`** `number` - **`drop_offs`** `integer` - **`exit_page`** `string` - **`sessions`** `integer` * **`sessions`** `integer` * **`tree`** `array` **Items:** - **`children`** `array` β€” Child path nodes. The runtime shape matches \`PathNode\`; the schema avoids a circular \`$ref\` so generated docs can render reliably. **Items:** - **`conversion_rate`** `number` - **`conversions`** `integer` - **`exit_page`** `string | null` β€” Present on \`drop\_off\` and \`truncated\` terminal nodes to tie path drop-off behavior to the actual session exit page. - **`sessions`** `integer` - **`type`** `string`, possible values: `"page", "event", "goal", "drop_off", "truncated"` - **`value`** `string` β€” For \`drop\_off\` and \`truncated\` terminal nodes, this is the exit page when known, otherwise \`unknown\`. **Example:** ```json { "entry_page": "/landing", "sessions": 120, "conversions": 32, "conversion_rate": 0.267, "exit_pages": [ { "exit_page": "/pricing", "sessions": 42, "conversions": 13, "conversion_rate": 0.31, "drop_offs": 29, "drop_off_rate": 0.69 } ], "tree": [ { "type": "page", "value": "/pricing", "exit_page": "/pricing", "sessions": 42, "conversions": 13, "conversion_rate": 0.31, "children": [ { "additionalProperty": "anything" } ] } ] } ``` ### PathsResponse - **Type:**`object` * **`bounds`** `object` - **`candidate_session_cap`** `integer` - **`entry_limit`** `integer` - **`max_steps`** `integer` - **`path_limit`** `integer` * **`entry_paths`** `array` **Items:** - **`conversion_rate`** `number` - **`conversions`** `integer` - **`entry_page`** `string` - **`exit_pages`** `array` β€” Exit pages reached by sessions that started on this entry page, with conversion and drop-off attribution. **Items:** - **`conversion_rate`** `number` - **`conversions`** `integer` - **`drop_off_rate`** `number` - **`drop_offs`** `integer` - **`exit_page`** `string` - **`sessions`** `integer` - **`sessions`** `integer` - **`tree`** `array` **Items:** - **`children`** `array` β€” Child path nodes. The runtime shape matches \`PathNode\`; the schema avoids a circular \`$ref\` so generated docs can render reliably. **Items:** - **`conversion_rate`** `number` - **`conversions`** `integer` - **`exit_page`** `string | null` β€” Present on \`drop\_off\` and \`truncated\` terminal nodes to tie path drop-off behavior to the actual session exit page. - **`sessions`** `integer` - **`type`** `string`, possible values: `"page", "event", "goal", "drop_off", "truncated"` - **`value`** `string` β€” For \`drop\_off\` and \`truncated\` terminal nodes, this is the exit page when known, otherwise \`unknown\`. * **`goal_event`** `string` * **`period`** `object` - **`from`** `string`, format: `date` - **`to`** `string`, format: `date` * **`project`** `string` **Example:** ```json { "project": "my-site", "goal_event": "signup", "period": { "from": "2026-03-11", "to": "2026-04-10" }, "bounds": { "max_steps": 5, "entry_limit": 10, "path_limit": 5, "candidate_session_cap": 5000 }, "entry_paths": [ { "entry_page": "/landing", "sessions": 120, "conversions": 32, "conversion_rate": 0.267, "exit_pages": [ { "exit_page": "/pricing", "sessions": 42, "conversions": 13, "conversion_rate": 0.31, "drop_offs": 29, "drop_off_rate": 0.69 } ], "tree": [ { "type": "page", "value": "/pricing", "exit_page": "/pricing", "sessions": 42, "conversions": 13, "conversion_rate": 0.31, "children": [ { "additionalProperty": "anything" } ] } ] } ] } ``` ### 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` * **`source_scan_id`** `string | null` β€” Website analysis id linked during project creation, when present. * **`total_events`** `integer` * **`total_reads`** `integer` **Example:** ```json { "id": "", "name": "my-site", "project_token": "aat_...", "allowed_origins": "*", "total_events": 1, "total_reads": 1, "source_scan_id": null } ``` ### WebsiteScanCreateRequest - **Type:**`object` * **`url` (required)** `string` β€” Website URL to analyze. Anonymous requests are normalized to the root domain. * **`full`** `boolean`, default: `false` β€” Request full analysis. Requires account auth. * **`mode`** `string`, possible values: `"preview", "full"` β€” Alternative way to request preview or full analysis. **Example:** ```json { "url": "https://example.com/pricing", "full": false, "mode": "preview" } ``` ### WebsiteScanUpgradeRequest - **Type:**`object` * **`project`** `string` β€” Optional project name used by CLI flows while upgrading. * **`resume_token`** `string` β€” One-analysis \`rst\_\*\` resume token from an anonymous preview. **Example:** ```json { "resume_token": "rst_...", "project": "my-site" } ``` ### WebsiteScanEventRecommendation - **Type:**`object` * **`agent_capability_after_install` (required)** `string` * **`current_blindspot` (required)** `string` * **`event` (required)** `string` β€” Stable snake\_case custom event name. Recommendations should not duplicate automatic tracker signals such as page\_view. * **`implementation_hint` (required)** `string` β€” Tracker-aware implementation guidance, for example data-aa-event attributes, data-aa-impression attributes, window\.aa.track(...), server-side tracking for durable outcomes, aa.identify()/aa.set(...) after auth, or script opt-ins when they unlock a concrete decision. * **`priority` (required)** `integer` * **`properties` (required)** `array` β€” Practical property names to capture with the event. Automatic tracker properties such as path, referrer, UTMs, device fields, country, session IDs, and first-touch attribution are already present on every event. **Items:** `string` * **`unlocks_questions` (required)** `array` **Items:** `string` * **`why_this_matters_now` (required)** `string` **Example:** ```json { "event": "signup_started", "priority": 1, "why_this_matters_now": "This marks the moment intent becomes commitment.", "current_blindspot": "You cannot see whether CTA clicks turn into signup attempts.", "unlocks_questions": [ "Which pages create signup starts?", "Where does signup intent drop?" ], "agent_capability_after_install": "Your agent can detect where intent drops before account creation.", "properties": [ "page_path", "entry_source", "auth_method" ], "implementation_hint": "" } ``` ### WebsiteScanResult - **Type:**`object` Decision-oriented instrumentation plan. Preview responses cap `minimum_viable_instrumentation` at 3-5 events; full results cap it at 5-8 events. Recommendations are tracker-aware and should not spend custom event slots on automatic page\_view, attribution, device, geography, session, or visitor fields. - **`advanced_tracking_later`** `array` β€” Agent Analytics tracker capabilities to consider after the first useful page event is installed. **Items:** - **`capability`** `string` - **`implementation_hint`** `string` - **`why`** `string` - **`after_install_agent_behavior`** `array` **Items:** `string` - **`analytics_detected`** `object` β€” Supporting context only; the recommendation does not lead with detected providers. - **`business_events_later`** `array` β€” Valuable inferred product, backend, or post-signup events that are not part of the first supplied-page install list. **Items:** - **`event`** `string` - **`where_to_instrument`** `string` - **`why`** `string` - **`current_blindspots`** `array` **Items:** `string` - **`framing`** `string` - **`goal_driven_funnels`** `array` **Items:** - **`bottleneck_detection`** `string` - **`decision`** `string` - **`name`** `string` - **`steps`** `array` **Items:** `string` - **`hostname`** `string` - **`minimum_viable_instrumentation`** `array` **Items:** - **`agent_capability_after_install` (required)** `string` - **`current_blindspot` (required)** `string` - **`event` (required)** `string` β€” Stable snake\_case custom event name. Recommendations should not duplicate automatic tracker signals such as page\_view. - **`implementation_hint` (required)** `string` β€” Tracker-aware implementation guidance, for example data-aa-event attributes, data-aa-impression attributes, window\.aa.track(...), server-side tracking for durable outcomes, aa.identify()/aa.set(...) after auth, or script opt-ins when they unlock a concrete decision. - **`priority` (required)** `integer` - **`properties` (required)** `array` β€” Practical property names to capture with the event. Automatic tracker properties such as path, referrer, UTMs, device fields, country, session IDs, and first-touch attribution are already present on every event. **Items:** `string` - **`unlocks_questions` (required)** `array` **Items:** `string` - **`why_this_matters_now` (required)** `string` - **`normalized_url`** `string` - **`not_needed_yet`** `array` **Items:** - **`event`** `string` - **`reason`** `string` - **`revisit_when`** `string` - **`suggested_ab_tests`** `array` β€” Compact experiment ideas tied to the recommended page UI events. **Items:** - **`hypothesis`** `string` - **`name`** `string` - **`success_event`** `string` - **`variants`** `array` **Items:** `string` **Example:** ```json { "framing": "fastest_path_to_useful_analytics", "normalized_url": "https://example.com/", "hostname": "example.com", "current_blindspots": [ "" ], "minimum_viable_instrumentation": [ { "event": "signup_started", "priority": 1, "why_this_matters_now": "This marks the moment intent becomes commitment.", "current_blindspot": "You cannot see whether CTA clicks turn into signup attempts.", "unlocks_questions": [ "Which pages create signup starts?", "Where does signup intent drop?" ], "agent_capability_after_install": "Your agent can detect where intent drops before account creation.", "properties": [ "page_path", "entry_source", "auth_method" ], "implementation_hint": "" } ], "not_needed_yet": [ { "event": "", "reason": "", "revisit_when": "" } ], "goal_driven_funnels": [ { "name": "", "steps": [ "" ], "decision": "", "bottleneck_detection": "" } ], "after_install_agent_behavior": [ "" ], "business_events_later": [ { "event": "", "why": "", "where_to_instrument": "" } ], "advanced_tracking_later": [ { "capability": "data-aa-impression", "why": "", "implementation_hint": "" } ], "suggested_ab_tests": [ { "name": "", "hypothesis": "", "variants": [ "" ], "success_event": "" } ], "analytics_detected": {} } ``` ### WebsiteScanResponse - **Type:**`object` * **`agent_handoff`** `object` - **`analysis_id`** `string` - **`label`** `string` - **`prompt`** `string` - **`resume_token`** `string | null` * **`analysis_id`** `string` * **`cached`** `boolean` * **`hostname`** `string` * **`mode`** `string` * **`normalized_url`** `string` * **`ok`** `boolean` * **`preview`** `object` β€” Decision-oriented instrumentation plan. Preview responses cap \`minimum\_viable\_instrumentation\` at 3-5 events; full results cap it at 5-8 events. Recommendations are tracker-aware and should not spend custom event slots on automatic page\_view, attribution, device, geography, session, or visitor fields. - **`advanced_tracking_later`** `array` β€” Agent Analytics tracker capabilities to consider after the first useful page event is installed. **Items:** - **`capability`** `string` - **`implementation_hint`** `string` - **`why`** `string` - **`after_install_agent_behavior`** `array` **Items:** `string` - **`analytics_detected`** `object` β€” Supporting context only; the recommendation does not lead with detected providers. - **`business_events_later`** `array` β€” Valuable inferred product, backend, or post-signup events that are not part of the first supplied-page install list. **Items:** - **`event`** `string` - **`where_to_instrument`** `string` - **`why`** `string` - **`current_blindspots`** `array` **Items:** `string` - **`framing`** `string` - **`goal_driven_funnels`** `array` **Items:** - **`bottleneck_detection`** `string` - **`decision`** `string` - **`name`** `string` - **`steps`** `array` **Items:** `string` - **`hostname`** `string` - **`minimum_viable_instrumentation`** `array` **Items:** - **`agent_capability_after_install` (required)** `string` - **`current_blindspot` (required)** `string` - **`event` (required)** `string` β€” Stable snake\_case custom event name. Recommendations should not duplicate automatic tracker signals such as page\_view. - **`implementation_hint` (required)** `string` β€” Tracker-aware implementation guidance, for example data-aa-event attributes, data-aa-impression attributes, window\.aa.track(...), server-side tracking for durable outcomes, aa.identify()/aa.set(...) after auth, or script opt-ins when they unlock a concrete decision. - **`priority` (required)** `integer` - **`properties` (required)** `array` β€” Practical property names to capture with the event. Automatic tracker properties such as path, referrer, UTMs, device fields, country, session IDs, and first-touch attribution are already present on every event. **Items:** `string` - **`unlocks_questions` (required)** `array` **Items:** `string` - **`why_this_matters_now` (required)** `string` - **`normalized_url`** `string` - **`not_needed_yet`** `array` **Items:** - **`event`** `string` - **`reason`** `string` - **`revisit_when`** `string` - **`suggested_ab_tests`** `array` β€” Compact experiment ideas tied to the recommended page UI events. **Items:** - **`hypothesis`** `string` - **`name`** `string` - **`success_event`** `string` - **`variants`** `array` **Items:** `string` * **`result`** `object` β€” Decision-oriented instrumentation plan. Preview responses cap \`minimum\_viable\_instrumentation\` at 3-5 events; full results cap it at 5-8 events. Recommendations are tracker-aware and should not spend custom event slots on automatic page\_view, attribution, device, geography, session, or visitor fields. - **`advanced_tracking_later`** `array` β€” Agent Analytics tracker capabilities to consider after the first useful page event is installed. **Items:** - **`capability`** `string` - **`implementation_hint`** `string` - **`why`** `string` - **`after_install_agent_behavior`** `array` **Items:** `string` - **`analytics_detected`** `object` β€” Supporting context only; the recommendation does not lead with detected providers. - **`business_events_later`** `array` β€” Valuable inferred product, backend, or post-signup events that are not part of the first supplied-page install list. **Items:** - **`event`** `string` - **`where_to_instrument`** `string` - **`why`** `string` - **`current_blindspots`** `array` **Items:** `string` - **`framing`** `string` - **`goal_driven_funnels`** `array` **Items:** - **`bottleneck_detection`** `string` - **`decision`** `string` - **`name`** `string` - **`steps`** `array` **Items:** `string` - **`hostname`** `string` - **`minimum_viable_instrumentation`** `array` **Items:** - **`agent_capability_after_install` (required)** `string` - **`current_blindspot` (required)** `string` - **`event` (required)** `string` β€” Stable snake\_case custom event name. Recommendations should not duplicate automatic tracker signals such as page\_view. - **`implementation_hint` (required)** `string` β€” Tracker-aware implementation guidance, for example data-aa-event attributes, data-aa-impression attributes, window\.aa.track(...), server-side tracking for durable outcomes, aa.identify()/aa.set(...) after auth, or script opt-ins when they unlock a concrete decision. - **`priority` (required)** `integer` - **`properties` (required)** `array` β€” Practical property names to capture with the event. Automatic tracker properties such as path, referrer, UTMs, device fields, country, session IDs, and first-touch attribution are already present on every event. **Items:** `string` - **`unlocks_questions` (required)** `array` **Items:** `string` - **`why_this_matters_now` (required)** `string` - **`normalized_url`** `string` - **`not_needed_yet`** `array` **Items:** - **`event`** `string` - **`reason`** `string` - **`revisit_when`** `string` - **`suggested_ab_tests`** `array` β€” Compact experiment ideas tied to the recommended page UI events. **Items:** - **`hypothesis`** `string` - **`name`** `string` - **`success_event`** `string` - **`variants`** `array` **Items:** `string` * **`resume_token`** `string` β€” Present only when creating a new anonymous preview. Store the token securely; the server stores only its hash. * **`status`** `string`, possible values: `"pending", "running", "succeeded", "failed"` **Example:** ```json { "ok": true, "analysis_id": "scan_...", "status": "succeeded", "mode": "anonymous_preview", "normalized_url": "https://example.com/", "hostname": "example.com", "cached": true, "resume_token": "rst_...", "preview": { "framing": "fastest_path_to_useful_analytics", "normalized_url": "https://example.com/", "hostname": "example.com", "current_blindspots": [ "" ], "minimum_viable_instrumentation": [ { "event": "signup_started", "priority": 1, "why_this_matters_now": "This marks the moment intent becomes commitment.", "current_blindspot": "You cannot see whether CTA clicks turn into signup attempts.", "unlocks_questions": [ "Which pages create signup starts?", "Where does signup intent drop?" ], "agent_capability_after_install": "Your agent can detect where intent drops before account creation.", "properties": [ "page_path", "entry_source", "auth_method" ], "implementation_hint": "" } ], "not_needed_yet": [ { "event": "", "reason": "", "revisit_when": "" } ], "goal_driven_funnels": [ { "name": "", "steps": [ "" ], "decision": "", "bottleneck_detection": "" } ], "after_install_agent_behavior": [ "" ], "business_events_later": [ { "event": "", "why": "", "where_to_instrument": "" } ], "advanced_tracking_later": [ { "capability": "data-aa-impression", "why": "", "implementation_hint": "" } ], "suggested_ab_tests": [ { "name": "", "hypothesis": "", "variants": [ "" ], "success_event": "" } ], "analytics_detected": {} }, "result": { "framing": "fastest_path_to_useful_analytics", "normalized_url": "https://example.com/", "hostname": "example.com", "current_blindspots": [ "" ], "minimum_viable_instrumentation": [ { "event": "signup_started", "priority": 1, "why_this_matters_now": "This marks the moment intent becomes commitment.", "current_blindspot": "You cannot see whether CTA clicks turn into signup attempts.", "unlocks_questions": [ "Which pages create signup starts?", "Where does signup intent drop?" ], "agent_capability_after_install": "Your agent can detect where intent drops before account creation.", "properties": [ "page_path", "entry_source", "auth_method" ], "implementation_hint": "" } ], "not_needed_yet": [ { "event": "", "reason": "", "revisit_when": "" } ], "goal_driven_funnels": [ { "name": "", "steps": [ "" ], "decision": "", "bottleneck_detection": "" } ], "after_install_agent_behavior": [ "" ], "business_events_later": [ { "event": "", "why": "", "where_to_instrument": "" } ], "advanced_tracking_later": [ { "capability": "data-aa-impression", "why": "", "implementation_hint": "" } ], "suggested_ab_tests": [ { "name": "", "hypothesis": "", "variants": [ "" ], "success_event": "" } ], "analytics_detected": {} }, "agent_handoff": { "label": "Give your agent analytics judgment", "analysis_id": "", "resume_token": null, "prompt": "" } } ``` ### WebsiteScanBusyError - **Type:** **Example:** ### 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 } ``` ### AgentSessionStartRequest - **Type:**`object` * **`client_type` (required)** `string` β€” Runtime type such as \`cli\`, \`mcp\`, \`paperclip\`, or \`openclaw\`. * **`callback_url`** `string | null` β€” Required for interactive mode. Local loopback callback URL. * **`client_instance_id`** `string | null` * **`client_name`** `string | null` * **`code_challenge`** `string | null` β€” Optional PKCE-style challenge for interactive runtimes. * **`label`** `string | null` * **`metadata`** `object | null` * **`mode`** `string`, possible values: `"interactive", "detached"`, default: `"interactive"` * **`scopes`** `array` **Items:** `string`, possible values: `"account:read", "projects:write", "analytics:read", "experiments:write", "feedback:write", "live:read"` **Example:** ```json { "mode": "interactive", "client_type": "cli", "client_name": "Agent Analytics CLI", "client_instance_id": "pid:12345", "label": "Paperclip Workspace", "callback_url": "http://127.0.0.1:43123/callback", "scopes": [ "account:read" ], "code_challenge": null, "metadata": null } ``` ### AgentSessionStartResponse - **Type:**`object` * **`approval_code`** `string` * **`auth_request_id`** `string` * **`authorize_url`** `string` * **`expires_at`** `integer` * **`mode`** `string`, possible values: `"interactive", "detached"` * **`ok`** `boolean` * **`poll_token`** `string` * **`scopes`** `array` **Items:** `string` **Example:** ```json { "ok": true, "auth_request_id": "req_agent123", "authorize_url": "https://api.agentanalytics.sh/agent-sessions/authorize/req_agent123", "approval_code": "ABCD2345", "poll_token": "aap_polltoken", "mode": "interactive", "expires_at": 1775079600000, "scopes": [ "" ] } ``` ### AgentSessionPollRequest - **Type:**`object` * **`auth_request_id` (required)** `string` * **`poll_token` (required)** `string` **Example:** ```json { "auth_request_id": "", "poll_token": "" } ``` ### AgentSessionPollResponse - **Type:**`object` * **`account_id`** `object` * **`approved_email`** `object` * **`exchange_code`** `object` * **`status`** `string`, possible values: `"pending", "approved", "exchanged", "revoked", "expired"` **Example:** ```json { "status": "pending", "exchange_code": "", "account_id": "", "approved_email": "" } ``` ### AgentSessionExchangeRequest - **Type:**`object` * **`auth_request_id` (required)** `string` * **`exchange_code` (required)** `string` * **`code_verifier`** `string | null` β€” Required when the auth request was started with a code challenge. **Example:** ```json { "auth_request_id": "", "exchange_code": "", "code_verifier": null } ``` ### AgentSessionIssued - **Type:**`object` * **`access_expires_at`** `integer` * **`access_token`** `string` * **`id`** `string` * **`refresh_expires_at`** `integer` * **`refresh_token`** `string` * **`scopes`** `array` **Items:** `string` **Example:** ```json { "id": "", "access_token": "aas_123", "refresh_token": "aar_123", "access_expires_at": 1, "refresh_expires_at": 1, "scopes": [ "" ] } ``` ### AgentSessionExchangeResponse - **Type:**`object` * **`account`** `object` - **`email`** `string` - **`github_login`** `object` - **`google_name`** `object` - **`id`** `string` - **`tier`** `string`, possible values: `"free", "pro"` * **`agent_session`** `object` - **`access_expires_at`** `integer` - **`access_token`** `string` - **`id`** `string` - **`refresh_expires_at`** `integer` - **`refresh_token`** `string` - **`scopes`** `array` **Items:** `string` * **`ok`** `boolean` **Example:** ```json { "ok": true, "agent_session": { "id": "", "access_token": "aas_123", "refresh_token": "aar_123", "access_expires_at": 1, "refresh_expires_at": 1, "scopes": [ "" ] }, "account": { "id": "", "email": "", "github_login": "", "google_name": "", "tier": "free" } } ``` ### AgentSessionRefreshRequest - **Type:**`object` * **`refresh_token` (required)** `string` **Example:** ```json { "refresh_token": "" } ``` ### AgentSessionRefreshResponse - **Type:**`object` * **`agent_session`** `object` - **`access_expires_at`** `integer` - **`access_token`** `string` - **`account`** `object` - **`email`** `string` - **`github_login`** `object` - **`google_name`** `object` - **`id`** `string` - **`tier`** `string`, possible values: `"free", "pro"` - **`refresh_expires_at`** `integer` - **`refresh_token`** `string` - **`scopes`** `array` **Items:** `string` - **`session_id`** `string` * **`ok`** `boolean` **Example:** ```json { "ok": true, "agent_session": { "session_id": "", "access_token": "", "refresh_token": "", "access_expires_at": 1, "refresh_expires_at": 1, "account": { "id": "", "email": "", "github_login": "", "google_name": "", "tier": "free" }, "scopes": [ "" ] } } ``` ### AgentSessionSummary - **Type:**`object` * **`access_expires_at`** `integer` * **`client_instance_id`** `object` * **`client_name`** `object` * **`client_type`** `string` * **`created_at`** `integer` * **`id`** `string` * **`label`** `object` * **`last_used_at`** `integer` * **`refresh_expires_at`** `integer` * **`revoked_at`** `object` * **`scopes`** `array` **Items:** `string` * **`status`** `string`, possible values: `"active", "revoked"` **Example:** ```json { "id": "", "client_type": "", "client_name": "", "client_instance_id": "", "label": "", "scopes": [ "" ], "status": "active", "created_at": 1, "last_used_at": 1, "access_expires_at": 1, "refresh_expires_at": 1, "revoked_at": 1 } ``` ### FeedbackRequest - **Type:**`object` * **`message` (required)** `string` β€” Sanitized summary of the friction, missing capability, or workflow problem. * **`command`** `string` β€” Optional CLI command or workflow step that led to the issue. * **`context`** `string` β€” Optional sanitized context. Do not include private owner details, secrets, or raw customer data. * **`project`** `string` β€” Optional project name for context. **Example:** ```json { "message": "The agent had to calculate funnel drop-off manually instead of using a built-in report.", "project": "my-site", "command": "agent-analytics funnel my-site --steps \"page_view,signup,purchase\"", "context": "The agent understood the goal but had to compute the percentages manually." } ``` ### 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 } ] } ```