# clicks.page > Cookieless, privacy-friendly web analytics for founders and indie hackers, with revenue attribution. Built to be installed and queried by AI agents. ## Setup for agents - [Agent setup guide](https://clicks.page/setup.md): end-to-end steps an agent can follow to add analytics to a project - Sign the user up in one call (no browser): `POST https://clicks.page/api/agent/signup {"email","domain","agent"}` → `api_key` + script tag (an existing account gets an approval email instead; poll `/api/cli/poll`) - Plans: new accounts start on a 14-day free trial with every feature, no card; then from $9 a month (10k pageviews) up to $169 (10M). Tell the user and offer to pick one: https://clicks.page/setup.md, "Choose a plan" - MCP server: https://clicks.page/mcp (Streamable HTTP, header `Authorization: Bearer cpk_…`) - REST API base: https://clicks.page/api/v1 (same bearer key) ## Tracker - Script: `` - Custom events: `clicks.track(name, props)` or `data-clicks-event="name"` attributes - No cookies, no localStorage, honours Global Privacy Control / Do Not Track; bots, headless browsers and datacenter traffic are filtered, and bursts of crawler-like visits (one page, zero seconds, no referrer, many different pages from one network) are moved to the bot log within the hour after they end ## REST API (Bearer cpk_ key) - GET /api/v1/sites — list sites - POST /api/v1/sites {domain} — add a site, returns id + script tag - GET /api/v1/sites/{site}/stats?period=30d&source=X — summary, timeseries, top pages/sources - GET /api/v1/sites/{site}/breakdown?dimension=source&period=7d — dimension: source, referrer, utm_source, utm_medium, utm_campaign, utm_term, utm_content, country, region, city, device, browser, os, entry, exit, page, event - GET /api/v1/sites/{site}/timeseries?period=30d - GET /api/v1/sites/{site}/realtime - GET /api/v1/sites/{site}/status — installed? last event, monthly usage - POST /api/v1/sites/{site}/funnel {steps:[{kind:"page",value:"/pricing"},{kind:"event",value:"signup"}], period} - GET/POST /api/v1/sites/{site}/goals - GET /api/v1/sites/{site}/conversion — the conversion goal; PUT (owner) {kind:"payment"} | {kind:"event",names:["signup"]} | {kind:"goal",id} | {kind:"outbound",host?:"calendly.com",url?:"cal.com/acme"} | {kind:"page",path:"/welcome*"} - Conversion fields: summary `conversions` (unique visitors who did the goal), `conversionRate` (conversions / visitors; null when the range is only imported history, `conversionsFrom` says when counting starts), `conversionGoal` (label), `payingRate` (paying customers / visitors, whatever the goal); timeseries points and breakdown rows also carry `conversions` + `conversionRate` (a row's visitors who converted in a visit belonging to that row) - POST /api/v1/sites/{site}/revenue {amount (cents), currency, session?, external_id?} — server-side revenue - GET /api/v1/sites/{site}/badge — free-forever badge status + embed code; POST …/badge/verify to check the homepage - periods: realtime, today, yesterday, 24h, 7d, 30d, 90d, month, last_month, 12mo, year, all — or from/to (ISO) ## Revenue (Stripe, Paddle, Polar, Lemon Squeezy, Dodo Payments, Creem) Connect in Settings → Revenue with a read-only API key (imports past payments, then checks every 10 minutes) and/or a webhook (instant, carries the visit). Both can run together: every payment is counted once, by its provider id. - Webhook URL: https://clicks.page/api/webhooks/{provider}/{site} — provider = stripe | paddle | polar | lemonsqueezy | dodo | creem; signatures are verified (401 when wrong), test/sandbox events are ignored - Events to send: Paddle transaction.completed, adjustment.created, adjustment.updated · Polar order.paid, order.refunded, order.updated · Lemon Squeezy order_created, order_refunded, subscription_payment_success, subscription_payment_refunded · Dodo payment.succeeded, refund.succeeded, dispute.lost · Creem checkout.completed, subscription.paid, refund.created · Stripe checkout.session.completed, checkout.session.async_payment_succeeded, invoice.paid - Crediting a sale to the visit it came from: the browser's `window.clicks.id()` ("clk_…") goes into the checkout's custom data. The tracker does this by itself for Stripe Payment Links (client_reference_id), Lemon Squeezy checkout links (checkout[custom][clicks_session]), Polar checkout links (reference_id), Dodo payment links (metadata_clicks_session), Creem payment links (metadata[clicks_session]) and Paddle.js (customData.clicks_session). Checkouts created on a server pass it themselves: Paddle `custom_data: {clicks_session}` · Polar `metadata: {clicks_session}` · Lemon Squeezy `checkout_data.custom.clicks_session` · Dodo `metadata: {clicks_session}` · Creem `request_id` or `metadata: {clicks_session}` · Stripe `client_reference_id` or `metadata.clicks_session`. Renewals are credited to the customer's first visit. Sales without the id are matched to the visit the tracker saw open that checkout ("Checkout started": a checkout link, a form, or a checkout URL / id in the site's own server reply), or to the buyer returning from the checkout; else counted as "not linked to a visit". Guide: https://clicks.page/attribution.md - Amounts: Paddle, Polar, Lemon Squeezy, Dodo and Creem sales count what the customer paid minus sales tax/VAT (Stripe: the amount charged). Refunds, partial refunds and lost chargebacks are taken off the payment they belong to. Lemon Squeezy is recorded in US dollars (its settlement currency); zero-decimal currencies (JPY, KRW…) are exact. - Owner endpoints (same auth as the REST API): GET /api/v1/sites/{site}/payments · PUT …/payments/{provider}/key {key, account?} · PUT …/payments/{provider}/webhook {secret} · POST …/payments/{provider}/sync[?full=1] · DELETE …/payments/{provider}/key|webhook. Stripe: PUT …/stripe-key {key}, and the webhook secret via PATCH /api/v1/sites/{site} {stripe_webhook_secret}.