{"openapi":"3.1.0","info":{"title":"twelvebit API","version":"1.0.0","summary":"Web and revenue analytics for people and their coding agents.","description":"Read analytics, push the tracking plan (analytics.yaml), and send server-side events.\n\n**Authentication.** Send `Authorization: Bearer <key>`. A site key (`tbk_…`) reads and writes one site, and ignores `?site=`. An account token (`tbt_…`) reaches every site of its user; name the site with `?site=` (site id, public id `tb_…`, or domain). A Better Auth session (cookie, or the session token as a Bearer) works like an account token. Create keys in the site settings or on /account, or run `npx twelvebit init`.\n\n**Scopes.** Each key has scopes, and each operation names the one it needs in `x-required-scope`. A key without it gets 403 `insufficient_scope` with `required_scope` and `key_scopes`. Keys made before scopes existed have read, write:events and write:config. Sessions have every scope. The site role still applies: viewers never write, and only owners manage the team or delete a site.\n\n| Scope | Allows |\n|---|---|\n| `read` | Read analytics, people, funnels, goals, alerts, notes and the tracking plan. |\n| `write:events` | Send goals, identify calls, payments and crawler hits from a server. |\n| `write:config` | Change funnels, alerts, notes, the tracking plan, exclusions and basic site settings. |\n| `admin` | Manage the team and API keys, share the dashboard in public, and create or delete sites. |\n\n**Idempotency.** Create operations take an `Idempotency-Key` header (8–128 characters, a UUID works). A repeat with the same key and body within 24 hours returns the first answer with `Idempotent-Replayed: true` and creates nothing.\n\n**Responses.** Success: `{ \"data\": …, \"meta\": { … } }`. Errors: `{ \"error\": { \"code\": \"…\", \"message\": \"…\" }, \"hint\": \"…\", \"docs\": \"…\" }`; the hint says what to do next.\n\n**Rate limit.** 120 requests per minute per key (1,200 for POST /api/v1/bots). Over the limit: HTTP 429 with `Retry-After`.\n\n**Time ranges.** `period` = today, yesterday, 24h, 7d, 30d, 90d, 6m, 12m, wtd, mtd, ytd, all, or custom with `from` and `to` (YYYY-MM-DD, in the site's time zone, both inclusive). Default: 30d. `meta.range` echoes the exact range.\n\n**Filters.** `f_<dim>=value` narrows to visits that match, for example `f_channel=Organic Search` or `f_country=DE`. Operators: `f_` is, `fx_` is not, `fc_` contains, `fxc_` does not contain (contains is case-insensitive). Comma-separated values mean any of them: `f_country=PT,ES`, `fx_channel=Direct,Email`, `fc_page=/blog,/docs`; write a literal comma as `\\,`. `f_visitor=new` or `returning` splits first visits from return visits. Different filters stack (AND). Use `(none)` for empty values and `Direct` for f_referrer. Countries take ISO codes or English names.\n\n**Revenue attribution.** Revenue in breakdowns goes to the visit that gets the credit: last touch: the visit before the payment. Amounts are in the site currency, in major units."},"servers":[{"url":"https://twelvebit.app"}],"security":[{"bearer":[]}],"tags":[{"name":"Sites","description":"Sites this key can read."},{"name":"Analytics","description":"Traffic, revenue, goals, funnels and people."},{"name":"Tracking plan","description":"analytics.yaml and the install check (drift)."},{"name":"Server-side","description":"Events, identify, payments and crawler hits from your server."},{"name":"Configuration","description":"Saved funnels, alerts and chart notes. Needs write:config to change."},{"name":"Team and keys","description":"Members and site API keys. Needs the admin scope."},{"name":"Account","description":"Goal display settings, alert delivery history, integrations and usage."},{"name":"Billing","description":"The account's plan, a checkout link for a person to open (Dodo Payments) and the launch price. Live only when the server has its Dodo keys."},{"name":"Meta","description":"This description."}],"paths":{"/api/v1/sites":{"get":{"tags":["Sites"],"operationId":"listSites","summary":"List sites","description":"Every site the key can read. A site key returns only its own site.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Site"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"},"post":{"tags":["Sites"],"operationId":"createSite","summary":"Add a site","description":"Account tokens and sessions only (not site keys). If you already own a site with the domain, it comes back with 200 and meta.created false. Safe to retry with Idempotency-Key.","x-required-scope":"admin","parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["domain"],"additionalProperties":false,"properties":{"domain":{"type":"string"},"timezone":{"type":"string","default":"UTC"},"name":{"type":"string"},"currency":{"type":"string","default":"USD"}}},"example":{"domain":"example.com","timezone":"Europe/Berlin","currency":"EUR"}}}},"responses":{"200":{"description":"You already own a site with this domain"},"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Site"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/sites/{siteId}":{"get":{"tags":["Sites"],"operationId":"getSite","summary":"Get a site","description":"One site by id, public id or domain, with its script tag.","parameters":[{"name":"siteId","in":"path","required":true,"schema":{"type":"string"},"description":"Site id, public id (tb_…) or domain."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Site"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"},"patch":{"tags":["Sites"],"operationId":"updateSite","summary":"Change site settings","description":"Send only the fields to change. `is_public` and `public_slug` publish the dashboard at /share/<slug> and need the admin scope; the other fields need write:config. Owners and members only.","x-required-scope":"write:config","parameters":[{"name":"siteId","in":"path","required":true,"schema":{"type":"string"},"description":"Site id, public id (tb_…) or domain."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteSettingsPatch"},"example":{"timezone":"Europe/Berlin","currency":"EUR","kpi":"goal:signup"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Site"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Sites"],"operationId":"deleteSite","summary":"Delete a site","description":"Owner only. Deletes the site and all its data for good. The body must be `{\"confirm\": \"<the site's domain>\"}`; anything else is a 400 confirm_required.","x-required-scope":"admin","parameters":[{"name":"siteId","in":"path","required":true,"schema":{"type":"string"},"description":"Site id, public id (tb_…) or domain."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["confirm"],"additionalProperties":false,"properties":{"confirm":{"type":"string","description":"The site's domain, exactly."}}},"example":{"confirm":"example.com"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"domain":{"type":"string"},"deleted":{"const":true}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/overview":{"get":{"tags":["Analytics"],"operationId":"getOverview","summary":"KPIs for a range","description":"Visitors, visits, page views, bounce rate, time on site, revenue, customers and conversion, for the range and the previous range of the same length, plus the number of people online now (people who match the filters). When the previous range holds too little data (it starts before the site's first data, or it has fewer than 20 visitors or under 5% of the current visitors), every `change` is null and `change_note` says why.","parameters":[{"$ref":"#/components/parameters/site"},{"$ref":"#/components/parameters/period"},{"$ref":"#/components/parameters/from"},{"$ref":"#/components/parameters/to"},{"$ref":"#/components/parameters/f_channel"},{"$ref":"#/components/parameters/fx_channel"},{"$ref":"#/components/parameters/fc_channel"},{"$ref":"#/components/parameters/fxc_channel"},{"$ref":"#/components/parameters/f_referrer"},{"$ref":"#/components/parameters/fx_referrer"},{"$ref":"#/components/parameters/fc_referrer"},{"$ref":"#/components/parameters/fxc_referrer"},{"$ref":"#/components/parameters/f_campaign"},{"$ref":"#/components/parameters/fx_campaign"},{"$ref":"#/components/parameters/fc_campaign"},{"$ref":"#/components/parameters/fxc_campaign"},{"$ref":"#/components/parameters/f_keyword"},{"$ref":"#/components/parameters/fx_keyword"},{"$ref":"#/components/parameters/fc_keyword"},{"$ref":"#/components/parameters/fxc_keyword"},{"$ref":"#/components/parameters/f_source"},{"$ref":"#/components/parameters/fx_source"},{"$ref":"#/components/parameters/fc_source"},{"$ref":"#/components/parameters/fxc_source"},{"$ref":"#/components/parameters/f_hostname"},{"$ref":"#/components/parameters/fx_hostname"},{"$ref":"#/components/parameters/fc_hostname"},{"$ref":"#/components/parameters/fxc_hostname"},{"$ref":"#/components/parameters/f_entry"},{"$ref":"#/components/parameters/fx_entry"},{"$ref":"#/components/parameters/fc_entry"},{"$ref":"#/components/parameters/fxc_entry"},{"$ref":"#/components/parameters/f_exit"},{"$ref":"#/components/parameters/fx_exit"},{"$ref":"#/components/parameters/fc_exit"},{"$ref":"#/components/parameters/fxc_exit"},{"$ref":"#/components/parameters/f_country"},{"$ref":"#/components/parameters/fx_country"},{"$ref":"#/components/parameters/fc_country"},{"$ref":"#/components/parameters/fxc_country"},{"$ref":"#/components/parameters/f_region"},{"$ref":"#/components/parameters/fx_region"},{"$ref":"#/components/parameters/fc_region"},{"$ref":"#/components/parameters/fxc_region"},{"$ref":"#/components/parameters/f_city"},{"$ref":"#/components/parameters/fx_city"},{"$ref":"#/components/parameters/fc_city"},{"$ref":"#/components/parameters/fxc_city"},{"$ref":"#/components/parameters/f_browser"},{"$ref":"#/components/parameters/fx_browser"},{"$ref":"#/components/parameters/fc_browser"},{"$ref":"#/components/parameters/fxc_browser"},{"$ref":"#/components/parameters/f_os"},{"$ref":"#/components/parameters/fx_os"},{"$ref":"#/components/parameters/fc_os"},{"$ref":"#/components/parameters/fxc_os"},{"$ref":"#/components/parameters/f_device"},{"$ref":"#/components/parameters/fx_device"},{"$ref":"#/components/parameters/fc_device"},{"$ref":"#/components/parameters/fxc_device"},{"$ref":"#/components/parameters/f_page"},{"$ref":"#/components/parameters/fx_page"},{"$ref":"#/components/parameters/fc_page"},{"$ref":"#/components/parameters/fxc_page"},{"$ref":"#/components/parameters/f_goal"},{"$ref":"#/components/parameters/fx_goal"},{"$ref":"#/components/parameters/fc_goal"},{"$ref":"#/components/parameters/fxc_goal"},{"$ref":"#/components/parameters/f_visitor"},{"$ref":"#/components/parameters/fx_visitor"},{"$ref":"#/components/parameters/f_ad"},{"$ref":"#/components/parameters/fx_ad"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"current":{"$ref":"#/components/schemas/Kpis"},"previous":{"$ref":"#/components/schemas/Kpis"},"change":{"type":"object","additionalProperties":{"type":["number","null"]},"description":"Change as a fraction (0.12 = +12%). Null when the previous value is 0, or when the previous range has too little data (see change_note)."},"change_note":{"type":["string","null"],"description":"Why there is no change: \"Not enough data before this range: …\". Null when the changes are comparable."},"online_now":{"type":"integer","description":"People active in the last 5 minutes whose current visit matches the filters."},"currency":{"type":"string"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"}},"/api/v1/timeseries":{"get":{"tags":["Analytics"],"operationId":"getTimeseries","summary":"Visitors and revenue over time","description":"One point per hour, day, week or month in the site's time zone.","parameters":[{"$ref":"#/components/parameters/site"},{"$ref":"#/components/parameters/period"},{"$ref":"#/components/parameters/from"},{"$ref":"#/components/parameters/to"},{"$ref":"#/components/parameters/f_channel"},{"$ref":"#/components/parameters/fx_channel"},{"$ref":"#/components/parameters/fc_channel"},{"$ref":"#/components/parameters/fxc_channel"},{"$ref":"#/components/parameters/f_referrer"},{"$ref":"#/components/parameters/fx_referrer"},{"$ref":"#/components/parameters/fc_referrer"},{"$ref":"#/components/parameters/fxc_referrer"},{"$ref":"#/components/parameters/f_campaign"},{"$ref":"#/components/parameters/fx_campaign"},{"$ref":"#/components/parameters/fc_campaign"},{"$ref":"#/components/parameters/fxc_campaign"},{"$ref":"#/components/parameters/f_keyword"},{"$ref":"#/components/parameters/fx_keyword"},{"$ref":"#/components/parameters/fc_keyword"},{"$ref":"#/components/parameters/fxc_keyword"},{"$ref":"#/components/parameters/f_source"},{"$ref":"#/components/parameters/fx_source"},{"$ref":"#/components/parameters/fc_source"},{"$ref":"#/components/parameters/fxc_source"},{"$ref":"#/components/parameters/f_hostname"},{"$ref":"#/components/parameters/fx_hostname"},{"$ref":"#/components/parameters/fc_hostname"},{"$ref":"#/components/parameters/fxc_hostname"},{"$ref":"#/components/parameters/f_entry"},{"$ref":"#/components/parameters/fx_entry"},{"$ref":"#/components/parameters/fc_entry"},{"$ref":"#/components/parameters/fxc_entry"},{"$ref":"#/components/parameters/f_exit"},{"$ref":"#/components/parameters/fx_exit"},{"$ref":"#/components/parameters/fc_exit"},{"$ref":"#/components/parameters/fxc_exit"},{"$ref":"#/components/parameters/f_country"},{"$ref":"#/components/parameters/fx_country"},{"$ref":"#/components/parameters/fc_country"},{"$ref":"#/components/parameters/fxc_country"},{"$ref":"#/components/parameters/f_region"},{"$ref":"#/components/parameters/fx_region"},{"$ref":"#/components/parameters/fc_region"},{"$ref":"#/components/parameters/fxc_region"},{"$ref":"#/components/parameters/f_city"},{"$ref":"#/components/parameters/fx_city"},{"$ref":"#/components/parameters/fc_city"},{"$ref":"#/components/parameters/fxc_city"},{"$ref":"#/components/parameters/f_browser"},{"$ref":"#/components/parameters/fx_browser"},{"$ref":"#/components/parameters/fc_browser"},{"$ref":"#/components/parameters/fxc_browser"},{"$ref":"#/components/parameters/f_os"},{"$ref":"#/components/parameters/fx_os"},{"$ref":"#/components/parameters/fc_os"},{"$ref":"#/components/parameters/fxc_os"},{"$ref":"#/components/parameters/f_device"},{"$ref":"#/components/parameters/fx_device"},{"$ref":"#/components/parameters/fc_device"},{"$ref":"#/components/parameters/fxc_device"},{"$ref":"#/components/parameters/f_page"},{"$ref":"#/components/parameters/fx_page"},{"$ref":"#/components/parameters/fc_page"},{"$ref":"#/components/parameters/fxc_page"},{"$ref":"#/components/parameters/f_goal"},{"$ref":"#/components/parameters/fx_goal"},{"$ref":"#/components/parameters/fc_goal"},{"$ref":"#/components/parameters/fxc_goal"},{"$ref":"#/components/parameters/f_visitor"},{"$ref":"#/components/parameters/fx_visitor"},{"$ref":"#/components/parameters/f_ad"},{"$ref":"#/components/parameters/fx_ad"},{"$ref":"#/components/parameters/g"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"granularity":{"type":"string","enum":["hour","day","week","month"]},"points":{"type":"array","items":{"type":"object","properties":{"t":{"type":"string","description":"Bucket start, local time (YYYY-MM-DDTHH:MM)"},"visitors":{"type":"integer"},"pageviews":{"type":"integer"},"revenue":{"type":"number"}}}}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"}},"/api/v1/breakdown":{"get":{"tags":["Analytics"],"operationId":"getBreakdown","summary":"Top values of one dimension","description":"Visitors and attributed revenue (last touch: the visit before the payment) per value, sorted by visitors. dim=goal adds completions; dim=referrer adds the channel; dim=region and dim=city add the country.","parameters":[{"$ref":"#/components/parameters/site"},{"$ref":"#/components/parameters/period"},{"$ref":"#/components/parameters/from"},{"$ref":"#/components/parameters/to"},{"$ref":"#/components/parameters/f_channel"},{"$ref":"#/components/parameters/fx_channel"},{"$ref":"#/components/parameters/fc_channel"},{"$ref":"#/components/parameters/fxc_channel"},{"$ref":"#/components/parameters/f_referrer"},{"$ref":"#/components/parameters/fx_referrer"},{"$ref":"#/components/parameters/fc_referrer"},{"$ref":"#/components/parameters/fxc_referrer"},{"$ref":"#/components/parameters/f_campaign"},{"$ref":"#/components/parameters/fx_campaign"},{"$ref":"#/components/parameters/fc_campaign"},{"$ref":"#/components/parameters/fxc_campaign"},{"$ref":"#/components/parameters/f_keyword"},{"$ref":"#/components/parameters/fx_keyword"},{"$ref":"#/components/parameters/fc_keyword"},{"$ref":"#/components/parameters/fxc_keyword"},{"$ref":"#/components/parameters/f_source"},{"$ref":"#/components/parameters/fx_source"},{"$ref":"#/components/parameters/fc_source"},{"$ref":"#/components/parameters/fxc_source"},{"$ref":"#/components/parameters/f_hostname"},{"$ref":"#/components/parameters/fx_hostname"},{"$ref":"#/components/parameters/fc_hostname"},{"$ref":"#/components/parameters/fxc_hostname"},{"$ref":"#/components/parameters/f_entry"},{"$ref":"#/components/parameters/fx_entry"},{"$ref":"#/components/parameters/fc_entry"},{"$ref":"#/components/parameters/fxc_entry"},{"$ref":"#/components/parameters/f_exit"},{"$ref":"#/components/parameters/fx_exit"},{"$ref":"#/components/parameters/fc_exit"},{"$ref":"#/components/parameters/fxc_exit"},{"$ref":"#/components/parameters/f_country"},{"$ref":"#/components/parameters/fx_country"},{"$ref":"#/components/parameters/fc_country"},{"$ref":"#/components/parameters/fxc_country"},{"$ref":"#/components/parameters/f_region"},{"$ref":"#/components/parameters/fx_region"},{"$ref":"#/components/parameters/fc_region"},{"$ref":"#/components/parameters/fxc_region"},{"$ref":"#/components/parameters/f_city"},{"$ref":"#/components/parameters/fx_city"},{"$ref":"#/components/parameters/fc_city"},{"$ref":"#/components/parameters/fxc_city"},{"$ref":"#/components/parameters/f_browser"},{"$ref":"#/components/parameters/fx_browser"},{"$ref":"#/components/parameters/fc_browser"},{"$ref":"#/components/parameters/fxc_browser"},{"$ref":"#/components/parameters/f_os"},{"$ref":"#/components/parameters/fx_os"},{"$ref":"#/components/parameters/fc_os"},{"$ref":"#/components/parameters/fxc_os"},{"$ref":"#/components/parameters/f_device"},{"$ref":"#/components/parameters/fx_device"},{"$ref":"#/components/parameters/fc_device"},{"$ref":"#/components/parameters/fxc_device"},{"$ref":"#/components/parameters/f_page"},{"$ref":"#/components/parameters/fx_page"},{"$ref":"#/components/parameters/fc_page"},{"$ref":"#/components/parameters/fxc_page"},{"$ref":"#/components/parameters/f_goal"},{"$ref":"#/components/parameters/fx_goal"},{"$ref":"#/components/parameters/fc_goal"},{"$ref":"#/components/parameters/fxc_goal"},{"$ref":"#/components/parameters/f_visitor"},{"$ref":"#/components/parameters/fx_visitor"},{"$ref":"#/components/parameters/f_ad"},{"$ref":"#/components/parameters/fx_ad"},{"$ref":"#/components/parameters/dim"},{"$ref":"#/components/parameters/limit"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"dim":{"type":"string"},"label":{"type":"string"},"rows":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"visitors":{"type":"integer"},"revenue":{"type":"number"}},"additionalProperties":true}}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"}},"/api/v1/realtime":{"get":{"tags":["Analytics"],"operationId":"getRealtime","summary":"Who is on the site now","description":"People active in the last 5 minutes (with page, source, place and goals), visitors per minute for the last 30 minutes (oldest first), and the last 30 minutes of events and payments.","parameters":[{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"online_now":{"type":"integer"},"visitors_per_minute":{"type":"array","items":{"type":"integer"}},"visitors":{"type":"array","items":{"type":"object"}},"recent_events":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"recent_payments":{"type":"array","items":{"type":"object"}}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"}},"/api/v1/goals":{"get":{"tags":["Analytics"],"operationId":"listGoals","summary":"Goals and their completions","description":"Visitors, completions and revenue of those visitors per goal. `declared` says whether the goal is in the tracking plan; `undeclared` and `not_seen` show drift.","parameters":[{"$ref":"#/components/parameters/site"},{"$ref":"#/components/parameters/period"},{"$ref":"#/components/parameters/from"},{"$ref":"#/components/parameters/to"},{"$ref":"#/components/parameters/f_channel"},{"$ref":"#/components/parameters/fx_channel"},{"$ref":"#/components/parameters/fc_channel"},{"$ref":"#/components/parameters/fxc_channel"},{"$ref":"#/components/parameters/f_referrer"},{"$ref":"#/components/parameters/fx_referrer"},{"$ref":"#/components/parameters/fc_referrer"},{"$ref":"#/components/parameters/fxc_referrer"},{"$ref":"#/components/parameters/f_campaign"},{"$ref":"#/components/parameters/fx_campaign"},{"$ref":"#/components/parameters/fc_campaign"},{"$ref":"#/components/parameters/fxc_campaign"},{"$ref":"#/components/parameters/f_keyword"},{"$ref":"#/components/parameters/fx_keyword"},{"$ref":"#/components/parameters/fc_keyword"},{"$ref":"#/components/parameters/fxc_keyword"},{"$ref":"#/components/parameters/f_source"},{"$ref":"#/components/parameters/fx_source"},{"$ref":"#/components/parameters/fc_source"},{"$ref":"#/components/parameters/fxc_source"},{"$ref":"#/components/parameters/f_hostname"},{"$ref":"#/components/parameters/fx_hostname"},{"$ref":"#/components/parameters/fc_hostname"},{"$ref":"#/components/parameters/fxc_hostname"},{"$ref":"#/components/parameters/f_entry"},{"$ref":"#/components/parameters/fx_entry"},{"$ref":"#/components/parameters/fc_entry"},{"$ref":"#/components/parameters/fxc_entry"},{"$ref":"#/components/parameters/f_exit"},{"$ref":"#/components/parameters/fx_exit"},{"$ref":"#/components/parameters/fc_exit"},{"$ref":"#/components/parameters/fxc_exit"},{"$ref":"#/components/parameters/f_country"},{"$ref":"#/components/parameters/fx_country"},{"$ref":"#/components/parameters/fc_country"},{"$ref":"#/components/parameters/fxc_country"},{"$ref":"#/components/parameters/f_region"},{"$ref":"#/components/parameters/fx_region"},{"$ref":"#/components/parameters/fc_region"},{"$ref":"#/components/parameters/fxc_region"},{"$ref":"#/components/parameters/f_city"},{"$ref":"#/components/parameters/fx_city"},{"$ref":"#/components/parameters/fc_city"},{"$ref":"#/components/parameters/fxc_city"},{"$ref":"#/components/parameters/f_browser"},{"$ref":"#/components/parameters/fx_browser"},{"$ref":"#/components/parameters/fc_browser"},{"$ref":"#/components/parameters/fxc_browser"},{"$ref":"#/components/parameters/f_os"},{"$ref":"#/components/parameters/fx_os"},{"$ref":"#/components/parameters/fc_os"},{"$ref":"#/components/parameters/fxc_os"},{"$ref":"#/components/parameters/f_device"},{"$ref":"#/components/parameters/fx_device"},{"$ref":"#/components/parameters/fc_device"},{"$ref":"#/components/parameters/fxc_device"},{"$ref":"#/components/parameters/f_page"},{"$ref":"#/components/parameters/fx_page"},{"$ref":"#/components/parameters/fc_page"},{"$ref":"#/components/parameters/fxc_page"},{"$ref":"#/components/parameters/f_goal"},{"$ref":"#/components/parameters/fx_goal"},{"$ref":"#/components/parameters/fc_goal"},{"$ref":"#/components/parameters/fxc_goal"},{"$ref":"#/components/parameters/f_visitor"},{"$ref":"#/components/parameters/fx_visitor"},{"$ref":"#/components/parameters/f_ad"},{"$ref":"#/components/parameters/fx_ad"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"goals":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":["string","null"],"description":"From the tracking plan, the source of declared goals."},"visitors":{"type":"integer"},"completions":{"type":"integer"},"revenue":{"type":"number"},"declared":{"type":"boolean"},"builtin":{"type":"boolean"},"display_name":{"type":["string","null"],"description":"Dashboard name (PATCH /api/v1/goals/{name})."},"color":{"type":["string","null"]},"hidden":{"type":"boolean"},"is_kpi":{"type":"boolean","description":"The goal is the #1 KPI on the overview."}}}},"undeclared":{"type":"array","items":{"type":"string"}},"not_seen":{"type":"array","items":{"type":"string"}}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"}},"/api/v1/funnels":{"get":{"tags":["Analytics"],"operationId":"getFunnels","summary":"List or compute funnels","description":"With `steps`, computes that funnel: each step counts visitors who reached it after the step before. Without `steps`, lists every funnel saved in the dashboard (with its `id`) and every funnel in the tracking plan (`id` null), each with its result (`name` picks one).","parameters":[{"$ref":"#/components/parameters/site"},{"$ref":"#/components/parameters/period"},{"$ref":"#/components/parameters/from"},{"$ref":"#/components/parameters/to"},{"$ref":"#/components/parameters/f_channel"},{"$ref":"#/components/parameters/fx_channel"},{"$ref":"#/components/parameters/fc_channel"},{"$ref":"#/components/parameters/fxc_channel"},{"$ref":"#/components/parameters/f_referrer"},{"$ref":"#/components/parameters/fx_referrer"},{"$ref":"#/components/parameters/fc_referrer"},{"$ref":"#/components/parameters/fxc_referrer"},{"$ref":"#/components/parameters/f_campaign"},{"$ref":"#/components/parameters/fx_campaign"},{"$ref":"#/components/parameters/fc_campaign"},{"$ref":"#/components/parameters/fxc_campaign"},{"$ref":"#/components/parameters/f_keyword"},{"$ref":"#/components/parameters/fx_keyword"},{"$ref":"#/components/parameters/fc_keyword"},{"$ref":"#/components/parameters/fxc_keyword"},{"$ref":"#/components/parameters/f_source"},{"$ref":"#/components/parameters/fx_source"},{"$ref":"#/components/parameters/fc_source"},{"$ref":"#/components/parameters/fxc_source"},{"$ref":"#/components/parameters/f_hostname"},{"$ref":"#/components/parameters/fx_hostname"},{"$ref":"#/components/parameters/fc_hostname"},{"$ref":"#/components/parameters/fxc_hostname"},{"$ref":"#/components/parameters/f_entry"},{"$ref":"#/components/parameters/fx_entry"},{"$ref":"#/components/parameters/fc_entry"},{"$ref":"#/components/parameters/fxc_entry"},{"$ref":"#/components/parameters/f_exit"},{"$ref":"#/components/parameters/fx_exit"},{"$ref":"#/components/parameters/fc_exit"},{"$ref":"#/components/parameters/fxc_exit"},{"$ref":"#/components/parameters/f_country"},{"$ref":"#/components/parameters/fx_country"},{"$ref":"#/components/parameters/fc_country"},{"$ref":"#/components/parameters/fxc_country"},{"$ref":"#/components/parameters/f_region"},{"$ref":"#/components/parameters/fx_region"},{"$ref":"#/components/parameters/fc_region"},{"$ref":"#/components/parameters/fxc_region"},{"$ref":"#/components/parameters/f_city"},{"$ref":"#/components/parameters/fx_city"},{"$ref":"#/components/parameters/fc_city"},{"$ref":"#/components/parameters/fxc_city"},{"$ref":"#/components/parameters/f_browser"},{"$ref":"#/components/parameters/fx_browser"},{"$ref":"#/components/parameters/fc_browser"},{"$ref":"#/components/parameters/fxc_browser"},{"$ref":"#/components/parameters/f_os"},{"$ref":"#/components/parameters/fx_os"},{"$ref":"#/components/parameters/fc_os"},{"$ref":"#/components/parameters/fxc_os"},{"$ref":"#/components/parameters/f_device"},{"$ref":"#/components/parameters/fx_device"},{"$ref":"#/components/parameters/fc_device"},{"$ref":"#/components/parameters/fxc_device"},{"$ref":"#/components/parameters/f_page"},{"$ref":"#/components/parameters/fx_page"},{"$ref":"#/components/parameters/fc_page"},{"$ref":"#/components/parameters/fxc_page"},{"$ref":"#/components/parameters/f_goal"},{"$ref":"#/components/parameters/fx_goal"},{"$ref":"#/components/parameters/fc_goal"},{"$ref":"#/components/parameters/fxc_goal"},{"$ref":"#/components/parameters/f_visitor"},{"$ref":"#/components/parameters/fx_visitor"},{"$ref":"#/components/parameters/f_ad"},{"$ref":"#/components/parameters/fx_ad"},{"name":"steps","in":"query","schema":{"type":"string"},"example":"page:/pricing,goal:signup,goal:purchase","description":"2 to 8 comma-separated steps: `page:/path` (use * as a wildcard), `goal:name` (a bare name is a goal) or `not:goal_name` (did not complete: people who did not do the goal before the next step; never the first step)."},{"name":"name","in":"query","schema":{"type":"string"},"description":"Name of a saved or planned funnel."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"oneOf":[{"$ref":"#/components/schemas/Funnel"},{"type":"array","items":{"$ref":"#/components/schemas/Funnel"}}]},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"},"post":{"tags":["Configuration"],"operationId":"createFunnel","summary":"Save a funnel","description":"Saves a funnel in the dashboard. Names are unique per site (409 funnel_exists). At most 50 per site. Safe to retry with Idempotency-Key.","x-required-scope":"write:config","parameters":[{"$ref":"#/components/parameters/site"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","steps"],"additionalProperties":false,"properties":{"name":{"type":"string","maxLength":80},"steps":{"type":"array","minItems":2,"maxItems":8,"items":{"oneOf":[{"type":"string","description":"page:/path (exact page), page:*text* (contains), page:/prefix* (starts with), goal:name, a bare goal name, or not:goal_name (did not complete: people who did not do the goal before the next step; never the first step)."},{"$ref":"#/components/schemas/FunnelStep"}]},"description":"page:/path, page:*text*, page:/prefix*, goal:name, not:goal_name, or { kind, value, match, label, not }."}}},"example":{"name":"Blog to purchase","steps":[{"kind":"page","value":"blog","match":"contains"},"page:/pricing*","goal:signup","goal:purchase"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/SavedFunnel"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/people":{"get":{"tags":["Analytics"],"operationId":"listPeople","summary":"List visitors","description":"Visitors seen in the range, newest first or by revenue. Anonymous visitors get a stable friendly name like amber-fox-4821. With f_* filters, a person counts when one of their visits in the range matches every filter (the same rule as the dashboard).","parameters":[{"$ref":"#/components/parameters/site"},{"$ref":"#/components/parameters/period"},{"$ref":"#/components/parameters/from"},{"$ref":"#/components/parameters/to"},{"$ref":"#/components/parameters/f_channel"},{"$ref":"#/components/parameters/fx_channel"},{"$ref":"#/components/parameters/fc_channel"},{"$ref":"#/components/parameters/fxc_channel"},{"$ref":"#/components/parameters/f_referrer"},{"$ref":"#/components/parameters/fx_referrer"},{"$ref":"#/components/parameters/fc_referrer"},{"$ref":"#/components/parameters/fxc_referrer"},{"$ref":"#/components/parameters/f_campaign"},{"$ref":"#/components/parameters/fx_campaign"},{"$ref":"#/components/parameters/fc_campaign"},{"$ref":"#/components/parameters/fxc_campaign"},{"$ref":"#/components/parameters/f_keyword"},{"$ref":"#/components/parameters/fx_keyword"},{"$ref":"#/components/parameters/fc_keyword"},{"$ref":"#/components/parameters/fxc_keyword"},{"$ref":"#/components/parameters/f_source"},{"$ref":"#/components/parameters/fx_source"},{"$ref":"#/components/parameters/fc_source"},{"$ref":"#/components/parameters/fxc_source"},{"$ref":"#/components/parameters/f_hostname"},{"$ref":"#/components/parameters/fx_hostname"},{"$ref":"#/components/parameters/fc_hostname"},{"$ref":"#/components/parameters/fxc_hostname"},{"$ref":"#/components/parameters/f_entry"},{"$ref":"#/components/parameters/fx_entry"},{"$ref":"#/components/parameters/fc_entry"},{"$ref":"#/components/parameters/fxc_entry"},{"$ref":"#/components/parameters/f_exit"},{"$ref":"#/components/parameters/fx_exit"},{"$ref":"#/components/parameters/fc_exit"},{"$ref":"#/components/parameters/fxc_exit"},{"$ref":"#/components/parameters/f_country"},{"$ref":"#/components/parameters/fx_country"},{"$ref":"#/components/parameters/fc_country"},{"$ref":"#/components/parameters/fxc_country"},{"$ref":"#/components/parameters/f_region"},{"$ref":"#/components/parameters/fx_region"},{"$ref":"#/components/parameters/fc_region"},{"$ref":"#/components/parameters/fxc_region"},{"$ref":"#/components/parameters/f_city"},{"$ref":"#/components/parameters/fx_city"},{"$ref":"#/components/parameters/fc_city"},{"$ref":"#/components/parameters/fxc_city"},{"$ref":"#/components/parameters/f_browser"},{"$ref":"#/components/parameters/fx_browser"},{"$ref":"#/components/parameters/fc_browser"},{"$ref":"#/components/parameters/fxc_browser"},{"$ref":"#/components/parameters/f_os"},{"$ref":"#/components/parameters/fx_os"},{"$ref":"#/components/parameters/fc_os"},{"$ref":"#/components/parameters/fxc_os"},{"$ref":"#/components/parameters/f_device"},{"$ref":"#/components/parameters/fx_device"},{"$ref":"#/components/parameters/fc_device"},{"$ref":"#/components/parameters/fxc_device"},{"$ref":"#/components/parameters/f_page"},{"$ref":"#/components/parameters/fx_page"},{"$ref":"#/components/parameters/fc_page"},{"$ref":"#/components/parameters/fxc_page"},{"$ref":"#/components/parameters/f_goal"},{"$ref":"#/components/parameters/fx_goal"},{"$ref":"#/components/parameters/fc_goal"},{"$ref":"#/components/parameters/fxc_goal"},{"$ref":"#/components/parameters/f_visitor"},{"$ref":"#/components/parameters/fx_visitor"},{"$ref":"#/components/parameters/f_ad"},{"$ref":"#/components/parameters/fx_ad"},{"name":"filter","in":"query","schema":{"type":"string","enum":["all","customers","identified"],"default":"all"}},{"name":"q","in":"query","schema":{"type":"string"},"description":"Search email or name (contains), or an exact visitor id or user id."},{"name":"sort","in":"query","schema":{"type":"string","enum":["recent","revenue"],"default":"recent"}},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/offset"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Person"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"}},"/api/v1/people/{visitorId}":{"get":{"tags":["Analytics"],"operationId":"getJourney","summary":"One visitor's journey","description":"Profile, attribution (first touch and the credited visit: last touch: the visit before the payment), every visit with its events, and payments.","parameters":[{"name":"visitorId","in":"path","required":true,"schema":{"type":"string"},"description":"Visitor id (_tb_vid) or the user_id from identify."},{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"person":{"$ref":"#/components/schemas/Person"},"attribution":{"type":"object"},"visits":{"type":"array","items":{"type":"object"}},"payments":{"type":"array","items":{"type":"object"}}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"}},"/api/v1/tracking-plan":{"get":{"tags":["Tracking plan"],"operationId":"getTrackingPlan","summary":"Get the tracking plan","description":"The stored plan as JSON and YAML. Send `Accept: text/yaml` (or ?format=yaml) to get only the YAML text. The YAML is rebuilt from the stored plan, so comments are not kept.","parameters":[{"$ref":"#/components/parameters/site"},{"name":"format","in":"query","schema":{"type":"string","enum":["json","yaml"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"plan":{"oneOf":[{"$ref":"#/components/schemas/TrackingPlan"},{"type":"null"}]},"yaml":{"type":["string","null"]},"updated_at":{"type":["string","null"],"format":"date-time"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}},"text/yaml":{"schema":{"type":"string"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"},"put":{"tags":["Tracking plan"],"operationId":"putTrackingPlan","summary":"Replace the tracking plan","description":"Send analytics.yaml as the body (`Content-Type: text/yaml`), or the same plan as JSON. The schema is strict: unknown keys are errors. The response lists added, removed and changed events. `?dry_run=1` validates without saving.","parameters":[{"$ref":"#/components/parameters/site"},{"name":"dry_run","in":"query","schema":{"type":"boolean"}}],"requestBody":{"required":true,"content":{"text/yaml":{"schema":{"type":"string"},"example":"version: 1\nproject: example.com\nevents:\n  signup:\n    description: A visitor creates an account\n    props:\n      plan: { type: string, enum: [free, pro] }\n"},"application/json":{"schema":{"$ref":"#/components/schemas/TrackingPlan"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"saved":{"type":"boolean"},"summary":{"type":"string","example":"Plan saved: added signup, purchase."},"events":{"type":"integer"},"diff":{"type":"object","properties":{"added":{"type":"array","items":{"type":"string"}},"removed":{"type":"array","items":{"type":"string"}},"changed":{"type":"array","items":{"type":"string"}},"unchanged":{"type":"integer"},"funnelsChanged":{"type":"boolean"}}}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid tracking plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"write:config"}},"/api/v1/install-check":{"get":{"tags":["Tracking plan"],"operationId":"getInstallCheck","summary":"Install check and drift","description":"What the site has sent: first and last event, the last 10 events, counts by type in the last 24 hours, host names seen, and for each event in the tracking plan whether it has been seen in the last 30 days. `undeclared` lists goals that were sent but are not in the plan. `next_steps` says what to fix.","parameters":[{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"installed":{"type":"boolean"},"first_event_at":{"type":["string","null"],"format":"date-time"},"last_event_at":{"type":["string","null"],"format":"date-time"},"recent":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"counts_24h":{"type":"object","additionalProperties":{"type":"integer"}},"hosts":{"type":"array","items":{"type":"string"}},"tracking_plan":{"type":"object","properties":{"exists":{"type":"boolean"},"updated_at":{"type":["string","null"]},"events":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"seen":{"type":"boolean"},"last_seen_at":{"type":["string","null"]},"count_24h":{"type":"integer"},"count_30d":{"type":"integer"}}}}}},"undeclared":{"type":"array","items":{"type":"string"}},"next_steps":{"type":"array","items":{"type":"string"}}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"}},"/api/v1/annotations":{"get":{"tags":["Analytics"],"operationId":"listAnnotations","summary":"List annotations","description":"Notes shown on the time charts in the range.","parameters":[{"$ref":"#/components/parameters/site"},{"$ref":"#/components/parameters/period"},{"$ref":"#/components/parameters/from"},{"$ref":"#/components/parameters/to"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Annotation"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"},"post":{"tags":["Analytics"],"operationId":"addAnnotation","summary":"Add an annotation","description":"Mark a launch, a deploy or a price change on the charts. `ts` defaults to now.","parameters":[{"$ref":"#/components/parameters/site"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["label"],"additionalProperties":false,"properties":{"label":{"type":"string","maxLength":200},"ts":{"type":"string","format":"date-time"},"url":{"type":"string","format":"uri"}}},"example":{"label":"Launched on Product Hunt","ts":"2026-10-01T09:00:00Z","url":"https://www.producthunt.com/posts/example"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Annotation"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"write:config"}},"/api/v1/segments":{"get":{"tags":["Analytics"],"operationId":"listSegments","summary":"List saved segments","description":"Named sets of filters saved in the dashboard or through the API. `query` is the same filters as URL parameters: append it to any analytics endpoint.","parameters":[{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Segment"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"},"post":{"tags":["Analytics"],"operationId":"saveSegment","summary":"Save a segment","description":"Save filters under a name. A segment with the same name (case-insensitive) is replaced (200); a new one is 201. A filter value is a string (is), a list (is any of) or an object with `is`, `is_not`, `contains` or `not_contains`. Members and owners only; at most 50 per site.","parameters":[{"$ref":"#/components/parameters/site"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","filters"],"additionalProperties":false,"properties":{"name":{"type":"string","maxLength":60},"filters":{"type":"object","description":"Keys: channel, referrer, campaign, keyword, source, hostname, entry, exit, country, region, city, browser, os, device, page, goal, visitor, ad.","additionalProperties":true}}},"example":{"name":"Paid traffic outside the US","filters":{"channel":["Paid Search","Paid Social"],"country":{"is_not":"US"},"page":{"contains":"/pricing"}}}}}},"responses":{"200":{"description":"Replaced","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Segment"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Segment"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"write:config"},"delete":{"tags":["Analytics"],"operationId":"deleteSegment","summary":"Delete a segment","description":"Pass the segment `id` or its `name`. Members and owners only.","parameters":[{"$ref":"#/components/parameters/site"},{"name":"id","in":"query","schema":{"type":"string"}},{"name":"name","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"write:config"}},"/api/v1/events":{"post":{"tags":["Server-side"],"operationId":"trackEvent","summary":"Send a goal from your server","description":"Records a goal for a visitor that the browser script has already seen. Pass `visitor_id` (the `_tb_vid` cookie) or `user_id` (sent earlier with identify). The goal joins the visitor's latest visit. Names are lowercased; use snake_case.","parameters":[{"$ref":"#/components/parameters/site"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":64},"visitor_id":{"type":"string"},"user_id":{"type":"string"},"props":{"type":"object","additionalProperties":{"type":["string","number","boolean"]},"maxProperties":30},"url":{"type":"string","format":"uri","description":"Page the goal belongs to. Defaults to the visit's last page."}}},"example":{"name":"subscription_started","user_id":"user_123","props":{"plan":"pro"}}}}},"responses":{"202":{"description":"Recorded","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"recorded":{"type":"boolean"},"name":{"type":"string"},"visitor_id":{"type":"string"},"visit_id":{"type":"string"},"ts":{"type":"string","format":"date-time"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"write:events"}},"/api/v1/identify":{"post":{"tags":["Server-side"],"operationId":"identify","summary":"Identify a visitor from your server","description":"Attaches your user id, email, name, avatar and traits to a visitor. Later server-side events can then use `user_id`.","parameters":[{"$ref":"#/components/parameters/site"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["visitor_id"],"properties":{"visitor_id":{"type":"string"},"user_id":{"type":"string"},"email":{"type":"string","format":"email"},"name":{"type":"string"},"image":{"type":"string","format":"uri"},"traits":{"type":"object","additionalProperties":{"type":["string","number","boolean"]}}}},"example":{"visitor_id":"k3j5h2g7f9d1s8a4q6w0e2r5","user_id":"user_123","email":"ada@example.com","name":"Ada","traits":{"plan":"pro"}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"identified":{"type":"boolean"},"visitor_id":{"type":"string"},"user_id":{"type":["string","null"]}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"write:events"}},"/api/v1/payments":{"post":{"tags":["Server-side"],"operationId":"recordPayment","summary":"Record a payment","description":"For payment providers without a built-in connection. Safe to repeat: `transaction_id` makes it idempotent. The payment is credited to a visit (last touch: the visit before the payment) through `visitor_id`/`session_id`, or through the email or customer id of an identified visitor. An amount of 0 is a free trial and is not counted. Account tokens pass `site_id` in the body.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount","currency","transaction_id"],"properties":{"amount":{"type":"number","minimum":0,"description":"Major units, like 49.99."},"currency":{"type":"string","pattern":"^[A-Z]{3}$"},"transaction_id":{"type":"string","maxLength":255},"visitor_id":{"type":"string","description":"The _tb_vid cookie at checkout."},"session_id":{"type":"string","description":"The _tb_sid cookie at checkout."},"email":{"type":"string"},"customer_id":{"type":"string"},"plan":{"type":"string"},"renewal":{"type":"boolean"},"refunded":{"type":"boolean"},"timestamp":{"type":["string","number"],"description":"ISO 8601 or Unix time. Defaults to now."},"site_id":{"type":"string","description":"Only for account tokens."}}},"example":{"amount":49,"currency":"USD","transaction_id":"inv_1042","visitor_id":"k3j5h2g7f9d1s8a4q6w0e2r5","email":"ada@example.com","plan":"Pro"}}}},"responses":{"200":{"description":"Already recorded (duplicate transaction_id), or a free trial"},"201":{"description":"Recorded","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"transaction_id":{"type":"string"},"duplicate":{"type":"boolean"},"attributed":{"type":"boolean"},"visitor_id":{"type":["string","null"]}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"write:events"},"get":{"tags":["Server-side"],"operationId":"listPayments","summary":"List payments","description":"Payments in a time window, newest first, with the channel of the credited visit. Account tokens pass `site_id`.","x-required-scope":"read","parameters":[{"name":"site_id","in":"query","schema":{"type":"string"},"description":"Only for account tokens."},{"name":"from","in":"query","schema":{"type":"string","format":"date-time"},"description":"Default: 30 days ago."},{"name":"to","in":"query","schema":{"type":"string","format":"date-time"},"description":"Default: now."},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"transaction_id":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"kind":{"type":"string","enum":["new","renewal","refund"]},"amount":{"type":"number"},"currency":{"type":"string"},"amount_site":{"type":"number"},"plan":{"type":["string","null"]},"customer_id":{"type":["string","null"]},"email":{"type":["string","null"]},"visitor_id":{"type":["string","null"]},"channel":{"type":["string","null"]}}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/bots":{"get":{"tags":["Server-side"],"operationId":"getBotTraffic","summary":"Crawler traffic","description":"Hits from AI and search crawlers in the range, by kind (ai_answer, indexing, training) and by bot, and browser visits from cloud and hosting networks (AWS, Google Cloud, Azure and others; likely bots). `cloud_networks.left_out` visits are in no report and no usage count; `counted` visits came while the site's `count_cloud_visits` was on.","parameters":[{"$ref":"#/components/parameters/site"},{"$ref":"#/components/parameters/period"},{"$ref":"#/components/parameters/from"},{"$ref":"#/components/parameters/to"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"by_kind":{"type":"object","additionalProperties":{"type":"integer"}},"bots":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"bot":{"type":"string"},"n":{"type":"integer"}}}},"cloud_networks":{"type":"object","properties":{"left_out":{"type":"integer","description":"Visits from cloud networks that no report counts (likely bots)."},"counted":{"type":"integer","description":"Visits from cloud networks that count as people (count_cloud_visits was on)."},"counting":{"type":"boolean","description":"The site's count_cloud_visits setting now."},"by_provider":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","enum":["aws","gcp","azure","oracle","digitalocean","linode","hetzner","ovh","github"]},"label":{"type":"string"},"visits":{"type":"integer"}}}}}}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"},"post":{"tags":["Server-side"],"operationId":"trackBotHit","summary":"Record a crawler hit","description":"Crawlers do not run JavaScript, so your server reports them. Send the user agent and path of a request; known crawlers are stored as ai_answer (an assistant fetches the page for a person now), indexing (search engines) or training (model training). Unknown user agents are ignored. Fire and forget: do not make the page wait.","parameters":[{"$ref":"#/components/parameters/site"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user_agent"],"properties":{"user_agent":{"type":"string"},"path":{"type":"string"}}},"example":{"user_agent":"Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; ChatGPT-User/1.0; +https://openai.com/bot","path":"/pricing"}}}},"responses":{"202":{"description":"Accepted","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"tracked":{"type":"boolean"},"bot":{"type":"string"},"kind":{"type":"string","enum":["ai_answer","indexing","training"]},"company":{"type":"string"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"write:events"}},"/api/v1/cli/bootstrap":{"post":{"tags":["Sites"],"operationId":"cliBootstrap","summary":"Find or create a site and mint a key (CLI)","description":"Used by `twelvebit init` after the device sign-in. Needs a session (the device flow's access token as a Bearer). Finds the user's site for the domain, or creates it, and returns a new site key named after the machine.","security":[{"session":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["domain"],"properties":{"domain":{"type":"string"},"timezone":{"type":"string"},"hostname":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["read","write:events","write:config","admin"]},"description":"Scopes of the new key. Default: read, write:events, write:config."}}},"example":{"domain":"example.com","timezone":"Europe/Berlin","hostname":"ada-mbp"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/extension/device":{"post":{"tags":["Sites"],"operationId":"extensionDeviceCode","summary":"Start the browser extension's sign-in","description":"Used by the twelvebit browser extension. Starts a device sign-in (RFC 8628) for the extension: the person approves user_code on verification_uri_complete, then the extension calls POST /api/v1/extension/token. No authentication.","security":[],"responses":{"200":{"description":"A device code","content":{"application/json":{"schema":{"type":"object","properties":{"device_code":{"type":"string"},"user_code":{"type":"string"},"verification_uri_complete":{"type":"string"},"expires_in":{"type":"integer"},"interval":{"type":"integer"}}}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/extension/token":{"post":{"tags":["Sites"],"operationId":"extensionToken","summary":"Finish the browser extension's sign-in","description":"Poll with the device_code. Before the approval: 400 with error authorization_pending (or slow_down, expired_token, access_denied). After it: a read-only account token (tbt_…, scope read, named Browser extension). The session of the device flow ends at once.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["device_code"],"properties":{"device_code":{"type":"string"}}},"example":{"device_code":"…"}}}},"responses":{"200":{"description":"The read-only token","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"user":{"type":"object"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Sites"],"operationId":"extensionSignOut","summary":"Sign the browser extension out","description":"Deletes the account token that the request sends as a Bearer. Only an account token can delete itself here.","responses":{"204":{"description":"Deleted"},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/sales":{"get":{"tags":["Analytics"],"operationId":"getPaidSales","summary":"Paid sales by customer type and order source","description":"Paid orders and refunds, including sales without a website visit. Customer type and order source are independent payment filters. Historical classification uses current Shopify tags; it does not reconstruct past tag values. Website visit filters are not accepted.","parameters":[{"$ref":"#/components/parameters/site"},{"$ref":"#/components/parameters/period"},{"$ref":"#/components/parameters/from"},{"$ref":"#/components/parameters/to"},{"$ref":"#/components/parameters/g"},{"name":"customer_type","in":"query","schema":{"type":"string","enum":["wholesale","distributor","d2c","unknown"]}},{"name":"order_source","in":"query","schema":{"type":"string","enum":["online_checkout","draft_order","other","unknown"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"currency":{"type":"string"},"filters":{"type":"object","properties":{"customer_type":{"type":"string","enum":["wholesale","distributor","d2c","unknown"]},"order_source":{"type":"string","enum":["online_checkout","draft_order","other","unknown"]}}},"totals":{"type":"object","properties":{"gross":{"type":"number"},"refunds":{"type":"number"},"net":{"type":"number"},"orders":{"type":"integer"},"customers":{"type":"integer","description":"Known distinct paying customers; sales without a customer identity are still counted as orders."}}},"previous":{"type":"object","properties":{"gross":{"type":"number"},"refunds":{"type":"number"},"net":{"type":"number"},"orders":{"type":"integer"},"customers":{"type":"integer","description":"Known distinct paying customers; sales without a customer identity are still counted as orders."}}},"by_customer_type":{"type":"array","items":{"type":"object","properties":{"gross":{"type":"number"},"refunds":{"type":"number"},"net":{"type":"number"},"orders":{"type":"integer"},"customers":{"type":"integer","description":"Known distinct paying customers; sales without a customer identity are still counted as orders."},"key":{"type":"string","enum":["wholesale","distributor","d2c","unknown"]}}}},"by_order_source":{"type":"array","items":{"type":"object","properties":{"gross":{"type":"number"},"refunds":{"type":"number"},"net":{"type":"number"},"orders":{"type":"integer"},"customers":{"type":"integer","description":"Known distinct paying customers; sales without a customer identity are still counted as orders."},"key":{"type":"string","enum":["online_checkout","draft_order","other","unknown"]}}}},"trend":{"type":"array","items":{"type":"object","properties":{"t":{"type":"string"},"revenue":{"type":"number"},"orders":{"type":"integer"}}}},"recent_payments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ts":{"type":"string","format":"date-time"},"amount":{"type":"number"},"kind":{"type":"string"},"provider":{"type":"string"},"order":{"type":["string","null"]},"customer_id":{"type":["string","null"]},"customer_type":{"type":"string","enum":["wholesale","distributor","d2c","unknown"]},"order_source":{"type":"string","enum":["online_checkout","draft_order","other","unknown"]},"classification_basis":{"enum":["unknown","tags","historical_current_tags"]},"attributed":{"type":"boolean"}}}},"coverage":{"type":"object","properties":{"historical":{"type":"integer"},"unknown":{"type":"integer"},"unattributed":{"type":"integer"}}},"classification_note":{"type":"string"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"}},"/api/v1/sales/segments":{"get":{"tags":["Analytics"],"operationId":"listSalesSegments","summary":"Saved paid sales segments","parameters":[{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"filters":{"type":"object","properties":{"customer_type":{"type":"string","enum":["wholesale","distributor","d2c","unknown"]},"order_source":{"type":"string","enum":["online_checkout","draft_order","other","unknown"]}},"additionalProperties":false}}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"read"},"post":{"tags":["Analytics"],"operationId":"saveSalesSegment","summary":"Save a paid sales segment","parameters":[{"$ref":"#/components/parameters/site"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","filters"],"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":60},"filters":{"type":"object","properties":{"customer_type":{"type":"string","enum":["wholesale","distributor","d2c","unknown"]},"order_source":{"type":"string","enum":["online_checkout","draft_order","other","unknown"]}},"additionalProperties":false}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"filters":{"type":"object","properties":{"customer_type":{"type":"string","enum":["wholesale","distributor","d2c","unknown"]},"order_source":{"type":"string","enum":["online_checkout","draft_order","other","unknown"]}},"additionalProperties":false}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"write:config"},"delete":{"tags":["Analytics"],"operationId":"deleteSalesSegment","summary":"Delete a paid sales segment","parameters":[{"$ref":"#/components/parameters/site"},{"name":"id","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-required-scope":"write:config"}},"/api/v1/sites/{siteId}/exclusions":{"get":{"tags":["Sites"],"operationId":"getExclusions","summary":"Get the exclusion lists","description":"The IP addresses and ranges, paths, hostnames, excluded countries and hidden countries of a site (Settings → Exclusions).","x-required-scope":"read","parameters":[{"name":"siteId","in":"path","required":true,"schema":{"type":"string"},"description":"Site id, public id (tb_…) or domain."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Exclusions"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Sites"],"operationId":"changeExclusions","summary":"Add and remove exclusions","description":"Adds and removes single entries, with the checks of Settings → Exclusions: IP addresses and CIDR ranges are checked and stored in one form (203.0.113.9/24 is stored as 203.0.113.0/24; IPv4-mapped IPv6 becomes IPv4), countries are ISO codes or English names. A country in `hidden_countries` is also excluded, and its past data is left out of every report (nothing is deleted); removing it from `hidden_countries` shows the past data again, and removing it from `countries` also stops the exclusion. Adding an entry that is there, or removing one that is not, changes nothing, so a repeat is safe; with Idempotency-Key a retry returns the first answer. `data.change_note` says in one sentence what changed (null when nothing changed). Owners and members only. To replace a whole list, use PATCH /api/v1/sites/{siteId}.","x-required-scope":"write:config","parameters":[{"name":"siteId","in":"path","required":true,"schema":{"type":"string"},"description":"Site id, public id (tb_…) or domain."},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"add":{"$ref":"#/components/schemas/ExclusionEntries"},"remove":{"$ref":"#/components/schemas/ExclusionEntries"}}},"example":{"add":{"ips":["203.0.113.0/24"],"paths":["/admin/**"],"hidden_countries":["RU"]},"remove":{"hostnames":["staging.example.com"]}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"exclusions":{"$ref":"#/components/schemas/Exclusions"},"changed":{"type":"boolean","description":"false when every entry was already there (add) or missing (remove)."},"change_note":{"type":["string","null"],"description":"What changed and what it does to new visits and the reports, in plain English. Null when nothing changed."}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/funnels/{funnelId}":{"get":{"tags":["Configuration"],"operationId":"getSavedFunnel","summary":"Get a saved funnel","description":"The definition of one saved funnel and its result for the range (`result`).","x-required-scope":"read","parameters":[{"name":"funnelId","in":"path","required":true,"schema":{"type":"string"},"description":"Saved funnel id (from GET /api/v1/funnels or /api/v1/funnels/{funnelId})."},{"$ref":"#/components/parameters/site"},{"$ref":"#/components/parameters/period"},{"$ref":"#/components/parameters/from"},{"$ref":"#/components/parameters/to"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/SavedFunnel"},{"type":"object","properties":{"result":{"$ref":"#/components/schemas/Funnel"}}}]},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Configuration"],"operationId":"updateFunnel","summary":"Change a saved funnel","description":"Rename the funnel or replace all its steps. Send only what changes.","x-required-scope":"write:config","parameters":[{"name":"funnelId","in":"path","required":true,"schema":{"type":"string"},"description":"Saved funnel id (from GET /api/v1/funnels or /api/v1/funnels/{funnelId})."},{"$ref":"#/components/parameters/site"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"name":{"type":"string","maxLength":80},"steps":{"type":"array","minItems":2,"maxItems":8,"items":{"oneOf":[{"type":"string","description":"page:/path (exact page), page:*text* (contains), page:/prefix* (starts with), goal:name, a bare goal name, or not:goal_name (did not complete: people who did not do the goal before the next step; never the first step)."},{"$ref":"#/components/schemas/FunnelStep"}]}}}},"example":{"name":"Pricing to purchase","steps":["page:/pricing","goal:signup","goal:purchase"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/SavedFunnel"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Configuration"],"operationId":"deleteFunnel","summary":"Delete a saved funnel","description":"Removes the funnel from the dashboard. Visit data stays.","x-required-scope":"write:config","parameters":[{"name":"funnelId","in":"path","required":true,"schema":{"type":"string"},"description":"Saved funnel id (from GET /api/v1/funnels or /api/v1/funnels/{funnelId})."},{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"deleted":{"const":true},"funnel":{"$ref":"#/components/schemas/SavedFunnel"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/alerts":{"get":{"tags":["Configuration"],"operationId":"listAlerts","summary":"List alerts","description":"Notifications when a goal completes or a payment arrives. Webhook URLs are secrets, so only their start is shown.","x-required-scope":"read","parameters":[{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Alert"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Configuration"],"operationId":"createAlert","summary":"Create an alert","description":"trigger is a goal name, or \"payment\" for every new payment. channel email takes an email address as target; webhook, slack and discord take an https:// URL. At most 20 per site. Safe to retry with Idempotency-Key.","x-required-scope":"write:config","parameters":[{"$ref":"#/components/parameters/site"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["trigger","channel","target"],"additionalProperties":false,"properties":{"trigger":{"type":"string","maxLength":64},"channel":{"type":"string","enum":["email","webhook","slack","discord"]},"target":{"type":"string","maxLength":500},"enabled":{"type":"boolean","default":true},"subject":{"type":["string","null"],"maxLength":200,"description":"Custom subject (email). Variables: {{goal}}, {{amount}}, {{country}}, {{source}}, {{path}}, {{site}}. null or \"\" = the default text."},"message":{"type":["string","null"],"maxLength":2000,"description":"Custom message. The same variables. null or \"\" = the default text."}}},"example":{"trigger":"signup","channel":"email","target":"founder@example.com"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Alert"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/alerts/{alertId}":{"get":{"tags":["Configuration"],"operationId":"getAlert","summary":"Get an alert","x-required-scope":"read","parameters":[{"name":"alertId","in":"path","required":true,"schema":{"type":"string"},"description":"Alert id (from GET /api/v1/alerts)."},{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Alert"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Configuration"],"operationId":"updateAlert","summary":"Change an alert","description":"Change trigger, channel or target, or pause it with enabled: false. A new channel needs a new target.","x-required-scope":"write:config","parameters":[{"name":"alertId","in":"path","required":true,"schema":{"type":"string"},"description":"Alert id (from GET /api/v1/alerts)."},{"$ref":"#/components/parameters/site"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"trigger":{"type":"string"},"channel":{"type":"string","enum":["email","webhook","slack","discord"]},"target":{"type":"string"},"enabled":{"type":"boolean"},"subject":{"type":["string","null"],"maxLength":200,"description":"Custom subject (email). Variables: {{goal}}, {{amount}}, {{country}}, {{source}}, {{path}}, {{site}}. null or \"\" = the default text."},"message":{"type":["string","null"],"maxLength":2000,"description":"Custom message. The same variables. null or \"\" = the default text."}}},"example":{"enabled":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Alert"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Configuration"],"operationId":"deleteAlert","summary":"Delete an alert","x-required-scope":"write:config","parameters":[{"name":"alertId","in":"path","required":true,"schema":{"type":"string"},"description":"Alert id (from GET /api/v1/alerts)."},{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"deleted":{"const":true},"alert":{"$ref":"#/components/schemas/Alert"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/annotations/{annotationId}":{"get":{"tags":["Analytics"],"operationId":"getAnnotation","summary":"Get an annotation","x-required-scope":"read","parameters":[{"name":"annotationId","in":"path","required":true,"schema":{"type":"string"},"description":"Annotation id (from GET /api/v1/annotations)."},{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Annotation"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Analytics"],"operationId":"updateAnnotation","summary":"Change an annotation","description":"Change the label, time or link. `url: null` removes the link.","x-required-scope":"write:config","parameters":[{"name":"annotationId","in":"path","required":true,"schema":{"type":"string"},"description":"Annotation id (from GET /api/v1/annotations)."},{"$ref":"#/components/parameters/site"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"label":{"type":"string","maxLength":200},"ts":{"type":"string","format":"date-time"},"url":{"type":["string","null"],"format":"uri"}}},"example":{"label":"Launched v2 on Product Hunt"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Annotation"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Analytics"],"operationId":"deleteAnnotation","summary":"Delete an annotation","x-required-scope":"write:config","parameters":[{"name":"annotationId","in":"path","required":true,"schema":{"type":"string"},"description":"Annotation id (from GET /api/v1/annotations)."},{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"deleted":{"const":true},"annotation":{"$ref":"#/components/schemas/Annotation"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/team":{"get":{"tags":["Team and keys"],"operationId":"listTeam","summary":"List the team","description":"The owner and every invited person, with role and status (active, invited, or expired).","x-required-scope":"admin","parameters":[{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Member"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Team and keys"],"operationId":"inviteMember","summary":"Invite a person","description":"Owner only. Sends the invite email with a link to the invite page (/invite/…), where the person creates an account or signs in with that address and joins in one step. The response has invite_url, the same page for you to share elsewhere. Invites expire after 14 days. Inviting an existing member changes only the role (200, no email). viewer = read only; member = read and change the setup. Safe to retry with Idempotency-Key.","x-required-scope":"admin","parameters":[{"$ref":"#/components/parameters/site"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"additionalProperties":false,"properties":{"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["viewer","member"],"default":"viewer"}}},"example":{"email":"ada@example.com","role":"viewer"}}}},"responses":{"200":{"description":"Already a member: the role was updated"},"201":{"description":"Invited","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Member"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/team/{email}":{"patch":{"tags":["Team and keys"],"operationId":"updateMemberRole","summary":"Change a member's role","description":"Owner only.","x-required-scope":"admin","parameters":[{"name":"email","in":"path","required":true,"schema":{"type":"string"},"description":"The member's email, URL-encoded (ada%40example.com).","example":"ada%40example.com"},{"$ref":"#/components/parameters/site"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["role"],"additionalProperties":false,"properties":{"role":{"type":"string","enum":["viewer","member"]}}},"example":{"role":"member"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Member"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Team and keys"],"operationId":"removeMember","summary":"Remove a member","description":"Owner only. The site keys that the person made stop working.","x-required-scope":"admin","parameters":[{"name":"email","in":"path","required":true,"schema":{"type":"string"},"description":"The member's email, URL-encoded (ada%40example.com).","example":"ada%40example.com"},{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"removed":{"const":true},"member":{"$ref":"#/components/schemas/Member"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/keys":{"get":{"tags":["Team and keys"],"operationId":"listKeys","summary":"List site keys","description":"Name, first characters, scopes, creator and last use. Never the secret. Owners and members only.","x-required-scope":"admin","parameters":[{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Team and keys"],"operationId":"createKey","summary":"Create a site key","description":"The secret is in `data.key` in this response only; store it at once. A key cannot create a key with scopes it does not have. Default scopes: read, write:events, write:config. With Idempotency-Key, a replay returns the key's details with `key: null`.","x-required-scope":"admin","parameters":[{"$ref":"#/components/parameters/site"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"additionalProperties":false,"properties":{"name":{"type":"string","maxLength":80},"scopes":{"type":"array","items":{"type":"string","enum":["read","write:events","write:config","admin"]},"minItems":1}}},"example":{"name":"Read-only dashboard export","scopes":["read"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/ApiKey"},{"type":"object","properties":{"key":{"type":["string","null"],"description":"The secret. Shown once."}}}]},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/keys/{keyId}":{"delete":{"tags":["Team and keys"],"operationId":"revokeKey","summary":"Revoke a site key","description":"Requests with the key fail at once. A key can revoke itself.","x-required-scope":"admin","parameters":[{"name":"keyId","in":"path","required":true,"schema":{"type":"string"},"description":"Key id (from GET /api/v1/keys). Not the secret."},{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"revoked":{"const":true},"key":{"$ref":"#/components/schemas/ApiKey"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/goals/{name}":{"get":{"tags":["Account"],"operationId":"getGoalSettings","summary":"Get a goal's display settings","description":"Display name, colour, hidden, and whether the goal is the #1 KPI.","x-required-scope":"read","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"},"description":"The goal (event) name, URL-encoded. GET /api/v1/goals lists them.","example":"signup"},{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/GoalSettings"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Account"],"operationId":"updateGoal","summary":"Customise a goal","description":"Rename a goal for the dashboard, give it a colour, hide it, or make it the #1 KPI. The event name never changes, so code, funnels and alerts keep working. Send only what changes; `null` resets `display_name` or `color`. `kpi: false` sets the KPI back to revenue when this goal is the KPI. `meta.warnings` names a goal that was never seen.","x-required-scope":"write:config","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"},"description":"The goal (event) name, URL-encoded. GET /api/v1/goals lists them.","example":"signup"},{"$ref":"#/components/parameters/site"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"display_name":{"type":["string","null"],"maxLength":80},"color":{"type":["string","null"],"enum":["#8FE3C8","#6FB8FF","#CFE8FF","#FFC46B","#FF7E6B","#B9A3FF","#FF9EC7","#C8E86B",null]},"hidden":{"type":"boolean"},"kpi":{"type":"boolean","description":"true = the #1 KPI on the overview."}}},"example":{"display_name":"Signed up","color":"#6FB8FF","kpi":true}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/GoalSettings"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/keys/{keyId}/roll":{"post":{"tags":["Team and keys"],"operationId":"rollKey","summary":"Roll a site key","description":"Makes a new secret with the same name and scopes and returns it in `data.key`, once. The old key stops at once, or after `grace_seconds` (at most 86400, 24 hours) so a deploy can switch over; `data.old_key.expires_at` says when. Owners can roll any key, members their own. A key in its grace period cannot be rolled again (409 key_expiring). With Idempotency-Key, a replay returns `key: null`. The MCP tool roll_api_key never returns the secret: the person copies it in the dashboard.","x-required-scope":"admin","parameters":[{"name":"keyId","in":"path","required":true,"schema":{"type":"string"},"description":"Key id (from GET /api/v1/keys). Not the secret."},{"$ref":"#/components/parameters/site"},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"grace_seconds":{"type":"integer","minimum":0,"maximum":86400,"default":0},"name":{"type":"string","maxLength":80}}},"example":{"grace_seconds":3600}}}},"responses":{"201":{"description":"Rolled","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/ApiKey"},{"type":"object","properties":{"key":{"type":["string","null"],"description":"The new secret. Shown once."},"old_key":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"revoked":{"type":"boolean"},"expires_at":{"type":["string","null"],"format":"date-time"}}}}}]},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/alerts/deliveries":{"get":{"tags":["Configuration"],"operationId":"listAlertDeliveries","summary":"Alert delivery history","description":"Every alert, test and report message that twelvebit tried to send, newest first. Webhook URLs are shortened because they are secrets. Page with `before` = `meta.next_before`.","x-required-scope":"read","parameters":[{"$ref":"#/components/parameters/site"},{"name":"alert_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this alert."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["sent","failed","skipped"]},"description":"Only this status."},{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["alert","test","weekly","spike"]},"description":"Only this kind."},{"name":"before","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only deliveries before this time (meta.next_before)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"Rows per page."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AlertDelivery"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/integrations":{"get":{"tags":["Account"],"operationId":"listIntegrations","summary":"Integration status","description":"For each integration: connected, status, last sync, last event, last error and non-secret details. Never a secret or a token. A person connects an integration in the dashboard (`connect_url`): the API never takes a secret.","x-required-scope":"read","parameters":[{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Integration"}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/integrations/{provider}":{"get":{"tags":["Account"],"operationId":"getIntegration","summary":"One integration's status","x-required-scope":"read","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","enum":["stripe","polar","lemonsqueezy","paddle","dodo","shopify","woocommerce","gsc","meta_ads","google_ads","github"]},"description":"Integration id."},{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Integration"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Account"],"operationId":"disconnectIntegration","summary":"Disconnect an integration","description":"Deletes the stored secret or token (Search Console also revokes the Google grant; Shopify removes the script and webhooks). Imported payments and orders stay. Connecting again needs a person in the dashboard, so the body must name the provider: `{\"confirm\": \"stripe\"}`. Owners and members only.","x-required-scope":"write:config","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","enum":["stripe","polar","lemonsqueezy","paddle","dodo","shopify","woocommerce","gsc","meta_ads","google_ads","github"]},"description":"Integration id."},{"$ref":"#/components/parameters/site"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["confirm"],"additionalProperties":false,"properties":{"confirm":{"type":"string","description":"The provider id, exactly."}}},"example":{"confirm":"stripe"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"provider":{"type":"string"},"name":{"type":"string"},"disconnected":{"const":true},"kept":{"type":"string"},"notes":{"type":"array","items":{"type":"string"}},"connect_url":{"type":"string"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/billing":{"get":{"tags":["Billing"],"operationId":"getBilling","summary":"The account's plan, usage and limits","description":"Status (beta, trialing, active, past_due, canceled, expired, exempt), trial end, events since the start of the monthly cycle, site and people limits, whether the dashboard asks for a plan (dashboard_locked, lock_reason; tracking never stops), the launch price (launch_price: the open offer, or the account's own place), and meta.catalog with the plans and prices. With a site key or ?site=, the account that owns the site; only the owner gets the details (others get dashboard_locked and owner_name).","x-required-scope":"read","parameters":[{"$ref":"#/components/parameters/site"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Billing"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/billing/checkout":{"post":{"tags":["Billing"],"operationId":"createCheckout","summary":"Make a checkout link (or change the plan)","description":"Owner only. Returns checkout_url, a Dodo Payments page for the person to open and pay; no card data passes through the API. A 10k or 100k checkout gets the next free launch price place (launch: the place, its discount and the full price; price_usd is then the discounted price). With an active plan, changes the plan instead (kind: plan_change; Dodo bills the difference; a plan change ends the launch price). The plan applies when Dodo confirms the payment. Takes Idempotency-Key. 409 billing_off when billing is not live.","x-required-scope":"admin","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":8,"maxLength":128},"description":"Repeat the same request safely."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["plan","tier"],"additionalProperties":false,"properties":{"plan":{"type":"string","enum":["starter","growth"]},"tier":{"type":"string","enum":["10k","100k","200k","500k","1m","2m","5m","10m"],"description":"Monthly events."},"interval":{"type":"string","enum":["month","year"],"default":"month","description":"A year costs 10 months."}}},"example":{"plan":"growth","tier":"100k","interval":"year"}}}},"responses":{"200":{"description":"The plan of the active subscription is changing.","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"kind":{"const":"plan_change"},"plan":{"type":"string"},"tier":{"type":"string"},"interval":{"type":"string"},"price_usd":{"type":"number"},"note":{"type":"string"}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"201":{"description":"A checkout link for a person to open.","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"object","properties":{"kind":{"const":"checkout"},"checkout_url":{"type":"string","format":"uri"},"session_id":{"type":"string"},"plan":{"type":"string"},"tier":{"type":"string"},"interval":{"type":"string"},"price_usd":{"type":"number","description":"The price for one period; the discounted price when launch is set."},"trial_days":{"type":"integer"},"launch":{"type":["object","null"],"description":"The launch price place this checkout holds (null: the standard price).","properties":{"place":{"type":"integer"},"off_pct":{"type":"integer"},"full_price_usd":{"type":"number"}}}}},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/usage":{"get":{"tags":["Account"],"operationId":"getUsage","summary":"Usage this month","description":"Events in a calendar month (UTC) by type (page views, goals, pings, bot hits, other), the billable count (page views + goals) with a projection for the month and the plan tier it fits, the top 10 paths and hostnames by event count, and the usage controls in place. Change the controls with PATCH /api/v1/sites/{siteId} (excluded_paths, excluded_hostnames, drop_pings, pageview_sample_rate). The plan counts per monthly cycle from the trial or plan start, not per calendar month: plan.cycle has the count of the current cycle (owner), and GET /api/v1/billing has the full plan.","x-required-scope":"read","parameters":[{"$ref":"#/components/parameters/site"},{"name":"month","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"description":"YYYY-MM (UTC). Default: this month."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Usage"},"meta":{"$ref":"#/components/schemas/Meta"}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/openapi.json":{"get":{"tags":["Meta"],"operationId":"getOpenApi","summary":"This document","security":[],"responses":{"200":{"description":"OpenAPI 3.1 document","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/openapi.yaml":{"get":{"tags":["Meta"],"operationId":"getOpenApiYaml","summary":"This document as YAML","security":[],"responses":{"200":{"description":"OpenAPI 3.1 document","content":{"application/yaml":{"schema":{"type":"string"}}}}}}}},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"Site key (tbk_…), account token (tbt_…) or a session token. Keys carry scopes: read, write:events, write:config, admin."},"session":{"type":"http","scheme":"bearer","description":"Better Auth session token (from the device sign-in) or the session cookie."}},"parameters":{"site":{"name":"site","in":"query","schema":{"type":"string"},"description":"Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it."},"period":{"name":"period","in":"query","schema":{"type":"string","enum":["today","yesterday","24h","7d","30d","90d","6m","12m","wtd","mtd","ytd","all","custom"],"default":"30d"}},"from":{"name":"from","in":"query","schema":{"type":"string","format":"date"},"description":"First day (YYYY-MM-DD, site time zone). Use with `to`."},"to":{"name":"to","in":"query","schema":{"type":"string","format":"date"},"description":"Last day, inclusive."},"g":{"name":"g","in":"query","schema":{"type":"string","enum":["hour","day","week","month"]},"description":"Bucket size. Default depends on the range length."},"dim":{"name":"dim","in":"query","schema":{"type":"string","enum":["channel","referrer","campaign","keyword","source","hostname","entry","exit","country","region","city","browser","os","device","page","goal","outbound"],"default":"channel"}},"limit":{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200}},"offset":{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}},"idempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":8,"maxLength":128,"pattern":"^[A-Za-z0-9_\\-:.]+$"},"description":"Makes a create safe to retry for 24 hours: a repeat with the same key and body returns the first answer (header Idempotent-Replayed: true) and creates nothing. The same key with another body is a 409."},"f_channel":{"name":"f_channel","in":"query","schema":{"type":"string"},"description":"Only visits where channel is this value (comma-separated for any of several)."},"fx_channel":{"name":"fx_channel","in":"query","schema":{"type":"string"},"description":"Only visits where channel is not this value (comma-separated for any of several)."},"fc_channel":{"name":"fc_channel","in":"query","schema":{"type":"string"},"description":"Only visits where channel contains this value (comma-separated for any of several)."},"fxc_channel":{"name":"fxc_channel","in":"query","schema":{"type":"string"},"description":"Only visits where channel does not contain this value (comma-separated for any of several)."},"f_referrer":{"name":"f_referrer","in":"query","schema":{"type":"string"},"description":"Only visits where referrer is this value (comma-separated for any of several)."},"fx_referrer":{"name":"fx_referrer","in":"query","schema":{"type":"string"},"description":"Only visits where referrer is not this value (comma-separated for any of several)."},"fc_referrer":{"name":"fc_referrer","in":"query","schema":{"type":"string"},"description":"Only visits where referrer contains this value (comma-separated for any of several)."},"fxc_referrer":{"name":"fxc_referrer","in":"query","schema":{"type":"string"},"description":"Only visits where referrer does not contain this value (comma-separated for any of several)."},"f_campaign":{"name":"f_campaign","in":"query","schema":{"type":"string"},"description":"Only visits where campaign is this value (comma-separated for any of several)."},"fx_campaign":{"name":"fx_campaign","in":"query","schema":{"type":"string"},"description":"Only visits where campaign is not this value (comma-separated for any of several)."},"fc_campaign":{"name":"fc_campaign","in":"query","schema":{"type":"string"},"description":"Only visits where campaign contains this value (comma-separated for any of several)."},"fxc_campaign":{"name":"fxc_campaign","in":"query","schema":{"type":"string"},"description":"Only visits where campaign does not contain this value (comma-separated for any of several)."},"f_keyword":{"name":"f_keyword","in":"query","schema":{"type":"string"},"description":"Only visits where keyword is this value (comma-separated for any of several)."},"fx_keyword":{"name":"fx_keyword","in":"query","schema":{"type":"string"},"description":"Only visits where keyword is not this value (comma-separated for any of several)."},"fc_keyword":{"name":"fc_keyword","in":"query","schema":{"type":"string"},"description":"Only visits where keyword contains this value (comma-separated for any of several)."},"fxc_keyword":{"name":"fxc_keyword","in":"query","schema":{"type":"string"},"description":"Only visits where keyword does not contain this value (comma-separated for any of several)."},"f_source":{"name":"f_source","in":"query","schema":{"type":"string"},"description":"Only visits where source is this value (comma-separated for any of several)."},"fx_source":{"name":"fx_source","in":"query","schema":{"type":"string"},"description":"Only visits where source is not this value (comma-separated for any of several)."},"fc_source":{"name":"fc_source","in":"query","schema":{"type":"string"},"description":"Only visits where source contains this value (comma-separated for any of several)."},"fxc_source":{"name":"fxc_source","in":"query","schema":{"type":"string"},"description":"Only visits where source does not contain this value (comma-separated for any of several)."},"f_hostname":{"name":"f_hostname","in":"query","schema":{"type":"string"},"description":"Only visits where hostname is this value (comma-separated for any of several)."},"fx_hostname":{"name":"fx_hostname","in":"query","schema":{"type":"string"},"description":"Only visits where hostname is not this value (comma-separated for any of several)."},"fc_hostname":{"name":"fc_hostname","in":"query","schema":{"type":"string"},"description":"Only visits where hostname contains this value (comma-separated for any of several)."},"fxc_hostname":{"name":"fxc_hostname","in":"query","schema":{"type":"string"},"description":"Only visits where hostname does not contain this value (comma-separated for any of several)."},"f_entry":{"name":"f_entry","in":"query","schema":{"type":"string"},"description":"Only visits where entry page is this value (comma-separated for any of several)."},"fx_entry":{"name":"fx_entry","in":"query","schema":{"type":"string"},"description":"Only visits where entry page is not this value (comma-separated for any of several)."},"fc_entry":{"name":"fc_entry","in":"query","schema":{"type":"string"},"description":"Only visits where entry page contains this value (comma-separated for any of several)."},"fxc_entry":{"name":"fxc_entry","in":"query","schema":{"type":"string"},"description":"Only visits where entry page does not contain this value (comma-separated for any of several)."},"f_exit":{"name":"f_exit","in":"query","schema":{"type":"string"},"description":"Only visits where exit page is this value (comma-separated for any of several)."},"fx_exit":{"name":"fx_exit","in":"query","schema":{"type":"string"},"description":"Only visits where exit page is not this value (comma-separated for any of several)."},"fc_exit":{"name":"fc_exit","in":"query","schema":{"type":"string"},"description":"Only visits where exit page contains this value (comma-separated for any of several)."},"fxc_exit":{"name":"fxc_exit","in":"query","schema":{"type":"string"},"description":"Only visits where exit page does not contain this value (comma-separated for any of several)."},"f_country":{"name":"f_country","in":"query","schema":{"type":"string"},"description":"Only visits where country is this value (comma-separated for any of several)."},"fx_country":{"name":"fx_country","in":"query","schema":{"type":"string"},"description":"Only visits where country is not this value (comma-separated for any of several)."},"fc_country":{"name":"fc_country","in":"query","schema":{"type":"string"},"description":"Only visits where country contains this value (comma-separated for any of several)."},"fxc_country":{"name":"fxc_country","in":"query","schema":{"type":"string"},"description":"Only visits where country does not contain this value (comma-separated for any of several)."},"f_region":{"name":"f_region","in":"query","schema":{"type":"string"},"description":"Only visits where region is this value (comma-separated for any of several)."},"fx_region":{"name":"fx_region","in":"query","schema":{"type":"string"},"description":"Only visits where region is not this value (comma-separated for any of several)."},"fc_region":{"name":"fc_region","in":"query","schema":{"type":"string"},"description":"Only visits where region contains this value (comma-separated for any of several)."},"fxc_region":{"name":"fxc_region","in":"query","schema":{"type":"string"},"description":"Only visits where region does not contain this value (comma-separated for any of several)."},"f_city":{"name":"f_city","in":"query","schema":{"type":"string"},"description":"Only visits where city is this value (comma-separated for any of several)."},"fx_city":{"name":"fx_city","in":"query","schema":{"type":"string"},"description":"Only visits where city is not this value (comma-separated for any of several)."},"fc_city":{"name":"fc_city","in":"query","schema":{"type":"string"},"description":"Only visits where city contains this value (comma-separated for any of several)."},"fxc_city":{"name":"fxc_city","in":"query","schema":{"type":"string"},"description":"Only visits where city does not contain this value (comma-separated for any of several)."},"f_browser":{"name":"f_browser","in":"query","schema":{"type":"string"},"description":"Only visits where browser is this value (comma-separated for any of several)."},"fx_browser":{"name":"fx_browser","in":"query","schema":{"type":"string"},"description":"Only visits where browser is not this value (comma-separated for any of several)."},"fc_browser":{"name":"fc_browser","in":"query","schema":{"type":"string"},"description":"Only visits where browser contains this value (comma-separated for any of several)."},"fxc_browser":{"name":"fxc_browser","in":"query","schema":{"type":"string"},"description":"Only visits where browser does not contain this value (comma-separated for any of several)."},"f_os":{"name":"f_os","in":"query","schema":{"type":"string"},"description":"Only visits where os is this value (comma-separated for any of several)."},"fx_os":{"name":"fx_os","in":"query","schema":{"type":"string"},"description":"Only visits where os is not this value (comma-separated for any of several)."},"fc_os":{"name":"fc_os","in":"query","schema":{"type":"string"},"description":"Only visits where os contains this value (comma-separated for any of several)."},"fxc_os":{"name":"fxc_os","in":"query","schema":{"type":"string"},"description":"Only visits where os does not contain this value (comma-separated for any of several)."},"f_device":{"name":"f_device","in":"query","schema":{"type":"string"},"description":"Only visits where device is this value (comma-separated for any of several)."},"fx_device":{"name":"fx_device","in":"query","schema":{"type":"string"},"description":"Only visits where device is not this value (comma-separated for any of several)."},"fc_device":{"name":"fc_device","in":"query","schema":{"type":"string"},"description":"Only visits where device contains this value (comma-separated for any of several)."},"fxc_device":{"name":"fxc_device","in":"query","schema":{"type":"string"},"description":"Only visits where device does not contain this value (comma-separated for any of several)."},"f_page":{"name":"f_page","in":"query","schema":{"type":"string"},"description":"Only visits where page is this value (comma-separated for any of several)."},"fx_page":{"name":"fx_page","in":"query","schema":{"type":"string"},"description":"Only visits where page is not this value (comma-separated for any of several)."},"fc_page":{"name":"fc_page","in":"query","schema":{"type":"string"},"description":"Only visits where page contains this value (comma-separated for any of several)."},"fxc_page":{"name":"fxc_page","in":"query","schema":{"type":"string"},"description":"Only visits where page does not contain this value (comma-separated for any of several)."},"f_goal":{"name":"f_goal","in":"query","schema":{"type":"string"},"description":"Only visits where goal is this value (comma-separated for any of several)."},"fx_goal":{"name":"fx_goal","in":"query","schema":{"type":"string"},"description":"Only visits where goal is not this value (comma-separated for any of several)."},"fc_goal":{"name":"fc_goal","in":"query","schema":{"type":"string"},"description":"Only visits where goal contains this value (comma-separated for any of several)."},"fxc_goal":{"name":"fxc_goal","in":"query","schema":{"type":"string"},"description":"Only visits where goal does not contain this value (comma-separated for any of several)."},"f_visitor":{"name":"f_visitor","in":"query","schema":{"type":"string"},"description":"Only visits where visitor is this value (new or returning)."},"fx_visitor":{"name":"fx_visitor","in":"query","schema":{"type":"string"},"description":"Only visits where visitor is not this value (new or returning)."},"f_ad":{"name":"f_ad","in":"query","schema":{"type":"string"},"description":"Only visits where ad campaign is this value (google:<campaign id> or meta:<campaign id>, comma-separated for any of several)."},"fx_ad":{"name":"fx_ad","in":"query","schema":{"type":"string"},"description":"Only visits where ad campaign is not this value (google:<campaign id> or meta:<campaign id>, comma-separated for any of several)."}},"schemas":{"Error":{"type":"object","required":["error","hint","docs"],"description":"Every error has a stable code, a message, a hint with the next step and a docs link. Unknown query parameters are rejected with unknown_parameter (400).","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","example":"unknown_parameter"},"message":{"type":"string","example":"Unknown query parameter: \"dimension\". This endpoint takes: dim, f_channel, from, g, limit, period, site, to."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"parameters":{"type":"array","items":{"type":"string"},"description":"unknown_parameter: the names that were not accepted."},"allowed":{"type":"array","items":{"type":"string"},"description":"unknown_parameter: the names this endpoint takes."},"retry_after":{"type":"integer","description":"rate_limited: seconds to wait."}}},"hint":{"type":"string","description":"What to do next, written for a model or a person.","example":"Remove the parameter or use one of the names in `allowed`."},"docs":{"type":"string","format":"uri","description":"The page that explains this error."}}},"Meta":{"type":"object","description":"Context for the data: the site, the exact range, the filters and the attribution model.","properties":{"site":{"type":"object","properties":{"id":{"type":"string"},"public_id":{"type":"string"},"domain":{"type":"string"},"timezone":{"type":"string"},"currency":{"type":"string"}}},"range":{"type":"object","properties":{"period":{"type":"string"},"label":{"type":"string"},"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"timezone":{"type":"string"},"granularity":{"type":"string"}}},"filters":{"type":"object","description":"The filters in effect, by key and operator, e.g. { \"country\": { \"is\": [\"PT\", \"ES\"] }, \"page\": { \"contains\": [\"/blog\"] } }.","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"attribution":{"type":"string","example":"last touch: the visit before the payment"},"warnings":{"type":"array","description":"Present when a filter value cannot match: an unknown country, device or channel (\"Narnia\"), or a value that no visit in the range has. The answer is still computed; a 0 next to a warning means the filter is probably wrong, not that nobody came.","items":{"type":"object","properties":{"key":{"type":"string"},"op":{"type":"string","enum":["is","is_not","contains","not_contains"]},"value":{"type":"string"},"code":{"type":"string","enum":["unknown_value","no_match"]},"message":{"type":"string"}}}}},"additionalProperties":true},"Site":{"type":"object","properties":{"id":{"type":"string"},"public_id":{"type":"string","example":"tb_8Kd2…"},"domain":{"type":"string"},"name":{"type":["string","null"]},"role":{"type":"string","enum":["owner","member","viewer"]},"timezone":{"type":"string"},"currency":{"type":"string"},"cookieless":{"type":"boolean"},"first_event_at":{"type":["string","null"],"format":"date-time"},"last_event_at":{"type":["string","null"],"format":"date-time"},"tracking_plan":{"type":"boolean"},"dashboard_url":{"type":"string"},"script_tag":{"type":"string"},"kpi":{"type":"string","description":"\"revenue\", \"mrr\" or \"goal:<name>\"."},"color":{"type":"string"},"is_public":{"type":"boolean"},"public_slug":{"type":["string","null"]},"public_url":{"type":["string","null"]},"weekly_report":{"type":"boolean"},"spike_alert":{"type":"boolean"},"excluded_ips":{"type":"array","items":{"type":"string"},"description":"IP addresses and CIDR ranges whose visits are not recorded."},"excluded_paths":{"type":"array","items":{"type":"string"}},"excluded_hostnames":{"type":"array","items":{"type":"string"}},"drop_pings":{"type":"boolean","description":"Usage control: ping events are not recorded."},"count_cloud_visits":{"type":"boolean","description":"Visits from cloud and hosting networks count as people. false (default): they are likely bots and every report leaves them out."},"pageview_sample_rate":{"type":"integer","description":"Usage control: percent of visitors whose page views are kept (100 = all)."},"attack_mode":{"type":"boolean","description":"Attack mode is on: stricter rules against a bot attack. See /docs/privacy#attack-mode."},"attack_mode_auto":{"type":"boolean","description":"Attack mode turns on at a traffic spike and off after 24 hours of normal traffic."},"attack_mode_since":{"type":["string","null"],"format":"date-time","description":"When attack mode turned on (null when it is off)."},"attack_mode_source":{"type":["string","null"],"enum":["manual","auto",null],"description":"Who made the last attack mode change."},"excluded_countries":{"type":"array","items":{"type":"string"},"description":"ISO codes whose new visits are not recorded."},"hidden_countries":{"type":"array","items":{"type":"string"},"description":"Excluded countries whose past data every report leaves out too."}}},"Kpis":{"type":"object","properties":{"visitors":{"type":"integer"},"visits":{"type":"integer"},"pageviews":{"type":"integer"},"bounce_rate":{"type":"number","description":"Fraction of visits with one page view and no goal."},"avg_visit_seconds":{"type":"integer"},"revenue":{"type":"number","description":"Net of refunds, site currency."},"refunds":{"type":"number"},"customers":{"type":"integer"},"paying_visitors":{"type":"integer"},"conversion_rate":{"type":"number","description":"Paying visitors / visitors."},"revenue_per_visitor":{"type":"number"}}},"Event":{"type":"object","properties":{"ts":{"type":"string","format":"date-time"},"type":{"type":"string","enum":["pageview","goal","identify","outbound"]},"name":{"type":["string","null"]},"path":{"type":["string","null"]},"hostname":{"type":["string","null"]},"props":{"type":["object","null"]}}},"Funnel":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Saved funnels only (null for tracking-plan and ad-hoc funnels)."},"source":{"type":"string","enum":["dashboard","tracking_plan"]},"name":{"type":["string","null"]},"steps":{"type":"array","items":{"type":"object","properties":{"step":{"type":"string","description":"page:/path, page:*text* (contains), page:/prefix* (starts with), goal:name or not:goal_name (did not complete)."},"did_not":{"type":"boolean"},"revenue":{"type":"number","description":"Payments made after people reached the step (last touch)."},"revenue_stopped_here":{"type":"number","description":"The part of revenue from people whose last step this is."},"match":{"type":["string","null"],"enum":["equals","contains","starts",null]},"label":{"type":"string"},"visitors":{"type":"integer"},"from_previous":{"type":"number"},"from_start":{"type":"number"}}}},"conversion":{"type":"number"},"weakest_step":{"type":["string","null"]},"by_channel":{"type":"array","items":{"type":"object"}},"days_to_convert":{"type":"array","items":{"type":"object"}}}},"Person":{"type":"object","properties":{"visitor_id":{"type":"string"},"name":{"type":"string","description":"Identified name, or a friendly name like amber-fox-4821."},"identified":{"type":"boolean"},"user_id":{"type":["string","null"]},"email":{"type":["string","null"]},"first_seen_at":{"type":"string","format":"date-time"},"last_seen_at":{"type":"string","format":"date-time"},"visits":{"type":"integer"},"first_channel":{"type":["string","null"]},"country":{"type":["string","null"]},"city":{"type":["string","null"]},"revenue":{"type":"number"}}},"Segment":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"filters":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"example":{"country":{"is":["PT","ES"]}}},"query":{"type":"string","example":"f_country=PT%2CES","description":"The filters as URL parameters."},"description":{"type":"string","example":"country is PT or ES"},"created_at":{"type":"string","format":"date-time"}}},"Annotation":{"type":"object","properties":{"id":{"type":"string"},"ts":{"type":"string","format":"date-time"},"label":{"type":"string"},"source":{"type":"string"},"url":{"type":["string","null"]}}},"FunnelStep":{"type":"object","required":["kind","value"],"additionalProperties":false,"properties":{"kind":{"type":"string","enum":["page","goal"]},"value":{"type":"string"},"match":{"type":"string","enum":["equals","contains","starts"],"default":"equals","description":"Page steps only: the exact path, any path that contains value, or any path that starts with value."},"label":{"type":"string","maxLength":60},"not":{"type":"boolean","description":"Goal steps only, never the first step: a \"did not complete\" step. It keeps the people who did not do the goal before the next step (or by the end of the range)."}}},"SavedFunnel":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"source":{"const":"dashboard"},"created_via":{"type":"string","enum":["dashboard","api","mcp"],"description":"Where the funnel was made."},"steps":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"value":{"type":"string"},"match":{"type":["string","null"],"enum":["equals","contains","starts",null]},"label":{"type":["string","null"]}}}},"step_strings":{"type":"array","items":{"type":"string"},"example":["page:*blog*","page:/pricing","goal:signup"],"description":"Each step as a string for ?steps= (gives the same numbers)."},"created_at":{"type":"string","format":"date-time"}}},"Alert":{"type":"object","properties":{"id":{"type":"string"},"trigger":{"type":"string","description":"Goal name or \"payment\"."},"trigger_type":{"type":"string","enum":["goal","payment"]},"channel":{"type":"string","enum":["email","webhook","slack","discord"]},"target":{"type":"string","description":"Email address, or the start of the webhook URL."},"enabled":{"type":"boolean"},"subject":{"type":["string","null"],"description":"Custom subject; null = the default text."},"message":{"type":["string","null"],"description":"Custom message; null = the default text."},"sent_count":{"type":"integer"},"created_at":{"type":"string","format":"date-time"}}},"Member":{"type":"object","properties":{"email":{"type":"string"},"name":{"type":["string","null"]},"role":{"type":"string","enum":["owner","member","viewer"]},"status":{"type":"string","enum":["active","invited","expired"],"description":"expired: the invite ran out; invite again to send a new one."},"invited_at":{"type":["string","null"],"format":"date-time"},"invite_expires_at":{"type":["string","null"],"format":"date-time","description":"Open invites: when the invite stops working (14 days after it was sent)."},"created":{"type":"boolean","description":"POST only: true when the person was new to the site."},"emailed":{"type":"boolean","description":"POST only: true when an invite email was sent."},"invite_url":{"type":["string","null"],"description":"POST only: the invite page (https://twelvebit.app/invite/…) to share in another channel; null when the person already joined. It works only for the invited address, and the person confirms that email before joining."}}},"ApiKey":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string","example":"tbk_8Kd2Qx"},"scopes":{"type":"array","items":{"type":"string","enum":["read","write:events","write:config","admin"]}},"created_by":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"type":["string","null"],"format":"date-time"}}},"Exclusions":{"type":"object","properties":{"ips":{"type":"array","items":{"type":"string"},"description":"IP addresses and CIDR ranges whose visits are not recorded."},"paths":{"type":"array","items":{"type":"string"},"description":"Paths that are not recorded (* = one segment, ** = any depth)."},"hostnames":{"type":"array","items":{"type":"string"},"description":"Hosts that are not recorded."},"countries":{"type":"array","items":{"type":"string"},"description":"ISO codes whose new visits are not recorded."},"hidden_countries":{"type":"array","items":{"type":"string"},"description":"Excluded countries whose past data every report leaves out too."}}},"ExclusionEntries":{"type":"object","additionalProperties":false,"properties":{"ips":{"type":"array","maxItems":200,"items":{"type":"string"},"description":"IP addresses or CIDR ranges: 203.0.113.7, 203.0.113.0/24, 2001:db8::/48."},"paths":{"type":"array","maxItems":100,"items":{"type":"string","pattern":"^/"},"description":"Paths like /admin/**."},"hostnames":{"type":"array","maxItems":100,"items":{"type":"string"},"description":"Hosts like staging.example.com."},"countries":{"type":"array","maxItems":250,"items":{"type":"string"},"description":"ISO codes (DE) or English names (Germany)."},"hidden_countries":{"type":"array","maxItems":250,"items":{"type":"string"},"description":"Countries whose past data is hidden in every report. Adding one also excludes it."}}},"SiteSettingsPatch":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"name":{"type":["string","null"],"maxLength":80},"timezone":{"type":"string","description":"IANA name, like Europe/Berlin."},"currency":{"type":"string","pattern":"^[A-Z]{3}$"},"kpi":{"type":"string","description":"\"revenue\", \"mrr\" or \"goal:<name>\"."},"color":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$"},"cookieless":{"type":"boolean"},"weekly_report":{"type":"boolean"},"spike_alert":{"type":"boolean"},"is_public":{"type":"boolean","description":"Needs admin."},"public_slug":{"type":["string","null"],"description":"Needs admin. The public link is /share/<slug>."},"excluded_paths":{"type":"array","maxItems":100,"items":{"type":"string","pattern":"^/"},"description":"Replaces the list. Paths that are not recorded: * is one segment, ** any depth (/admin/**). Excluded traffic does not count toward usage."},"excluded_hostnames":{"type":"array","maxItems":100,"items":{"type":"string"},"description":"Replaces the list. Hosts that are not recorded, like staging.example.com."},"excluded_ips":{"type":"array","maxItems":200,"items":{"type":"string"},"description":"Replaces the list. IP addresses and CIDR ranges (203.0.113.7, 203.0.113.0/24, 2001:db8::/48) whose visits are not recorded. Entries are checked and stored in one form; an entry that is not an address is a 400 invalid_ip."},"excluded_countries":{"type":"array","maxItems":250,"items":{"type":"string"},"description":"Replaces the list. ISO codes or English names whose new visits are not recorded. A country that leaves this list also leaves hidden_countries (its past data shows again)."},"hidden_countries":{"type":"array","maxItems":250,"items":{"type":"string"},"description":"Replaces the list. Countries whose past data every report leaves out (nothing is deleted). Each one is added to excluded_countries too."},"drop_pings":{"type":"boolean","description":"Usage control. true = ping events (sent when a page is hidden) are not recorded. Visit durations and time on the last page get shorter; bounce rate and counts do not change."},"pageview_sample_rate":{"type":"integer","enum":[100,50,25,10],"description":"Usage control for very high traffic. Percent of visitors whose page views are kept; 100 = off (default). Goals, identify and payments are never sampled, so visitor and page view counts drop to about this share while conversion rates read higher. See /docs/usage."},"attack_mode":{"type":"boolean","description":"Attack mode: during a bot attack, new visits count only after a click, scroll or key press; fast repeat hits (30 a minute per address, 150 per network), referrer spam and requests without browser headers are dropped. Real visitors who never interact are left out too. Needs write:config."},"attack_mode_auto":{"type":"boolean","description":"Turn attack mode on when the hourly spike check sees more than twice the usual daily visitors in the last 24 hours, and off after 24 hours of normal traffic. The owner gets an email at each switch."},"count_cloud_visits":{"type":"boolean","description":"true = count browser visits from cloud and hosting networks (AWS, Google Cloud, Azure, Oracle, DigitalOcean, Linode, Hetzner, OVHcloud, GitHub Actions) as people. Default false: they are likely bots, so people, reports, conversion rates and usage leave them out, and the bot card shows them under Cloud networks. Turn it on only when real visitors use cloud desktops or VPNs in data centres. Applies to new visits. Needs write:config. See /docs/bots#cloud-networks."}}},"GoalSettings":{"type":"object","properties":{"name":{"type":"string"},"display_name":{"type":["string","null"]},"color":{"type":["string","null"]},"hidden":{"type":"boolean"},"is_kpi":{"type":"boolean"},"seen_90d":{"type":"boolean","description":"The goal arrived in the last 90 days."},"updated_at":{"type":["string","null"],"format":"date-time"}}},"AlertDelivery":{"type":"object","properties":{"id":{"type":"string"},"alert_id":{"type":["string","null"]},"ts":{"type":"string","format":"date-time"},"kind":{"type":"string","enum":["alert","test","weekly","spike"]},"channel":{"type":"string","enum":["email","webhook","slack","discord"]},"target":{"type":"string","description":"Email address, or the start of the webhook URL."},"trigger":{"type":["string","null"]},"subject":{"type":["string","null"]},"status":{"type":"string","enum":["sent","failed","skipped"]},"error":{"type":["string","null"]}}},"Integration":{"type":"object","properties":{"provider":{"type":"string"},"name":{"type":"string"},"category":{"type":"string","enum":["payments","commerce","search","ads","annotations"]},"connected":{"type":"boolean"},"status":{"type":["string","null"],"description":"\"connected\" or \"error\"; null when not connected."},"connected_at":{"type":["string","null"],"format":"date-time"},"last_sync_at":{"type":["string","null"],"format":"date-time"},"last_event_at":{"type":["string","null"],"format":"date-time","description":"Last webhook, payment or commit."},"last_error":{"type":["string","null"]},"details":{"type":"object","description":"Non-secret extras: payments, livemode, property, account_name …"},"connect_url":{"type":"string","description":"The dashboard page where a person connects it."},"can_disconnect":{"type":"boolean"}}},"Billing":{"type":"object","properties":{"billing_live":{"type":"boolean","description":"true on twelvebit.app. false only on a server without Dodo Payments keys (a local or self-hosted copy): no plan applies there."},"status":{"type":"string","enum":["beta","exempt","trialing","active","past_due","canceled","expired"]},"plan":{"type":["string","null"],"enum":["starter","growth",null]},"tier":{"type":["string","null"]},"interval":{"type":["string","null"],"enum":["month","year",null]},"price_usd":{"type":["number","null"],"description":"The price for one period; the discounted price when the account pays the launch price."},"price_usd_full":{"type":["number","null"],"description":"The standard price for one period, without the launch price."},"trial":{"type":["object","null"],"description":"The no-card trial of an older account (made before the card trial); null for the others.","properties":{"kind":{"type":"string","enum":["no_card"]},"started_at":{"type":"string","format":"date-time"},"ends_at":{"type":"string","format":"date-time"},"days_left":{"type":"integer"},"active":{"type":"boolean"}}},"card_trial":{"type":["object","null"],"description":"The 14-day card trial of a Starter or Growth checkout: no charge until ends_at.","properties":{"ends_at":{"type":"string","format":"date-time"},"days_left":{"type":"integer"}}},"card_trial_available":{"type":"boolean","description":"The next checkout includes the 14-day free trial (one per account)."},"usage":{"type":["object","null"],"properties":{"events":{"type":"integer"},"since":{"type":"string","format":"date-time"},"resets_at":{"type":"string","format":"date-time"},"sites":{"type":"integer"},"members":{"type":"integer"}}},"limits":{"type":"object","properties":{"events":{"type":["integer","null"]},"sites":{"type":["integer","null"]},"members":{"type":["integer","null"]},"source":{"type":"string"}}},"dashboard_locked":{"type":"boolean","description":"The dashboard pages ask for a plan. Tracking, public dashboards and the API keep working."},"lock_reason":{"type":["string","null"],"enum":["no_plan","trial_expired","over_events","past_due","plan_ended",null]},"message":{"type":"string"},"launch_price":{"type":"object","description":"The launch price: the first 10 paid places at 10k or 100k events get a lifetime 50% discount; after that, the standard price. A cancel or any plan change ends it for good. One place per account.","properties":{"rule":{"type":"string"},"offer":{"type":["object","null"],"description":"The offer the next checkout of this account gets; null when it does not apply (places are gone, the account has a place or a live subscription).","properties":{"open":{"type":"boolean"},"places_total":{"type":"integer"},"places_taken":{"type":"integer"},"places_left":{"type":"integer"},"next_place":{"type":["integer","null"]},"off_pct":{"type":["integer","null"]},"left_at_this_price":{"type":"integer"},"next_off_pct":{"type":["integer","null"],"description":"The discount after this step; null when the next step is the standard price."},"until":{"type":["string","null"],"format":"date-time"}}},"mine":{"type":["object","null"],"description":"The account's own place; null when it has none.","properties":{"place":{"type":"integer"},"off_pct":{"type":"integer"},"status":{"type":"string","enum":["held","taken","ended"],"description":"held: an open checkout holds it (2 hours). taken: the subscription pays the launch price. ended: a cancel or a plan change ended it."},"plan":{"type":"string","enum":["starter","growth"]},"tier":{"type":"string"},"interval":{"type":"string","enum":["month","year"]},"price_usd":{"type":"number"},"price_usd_full":{"type":"number"},"taken_at":{"type":["string","null"],"format":"date-time"},"ended_at":{"type":["string","null"],"format":"date-time"},"end_reason":{"type":["string","null"],"description":"canceled, plan_changed or replaced."}}}}},"billing_url":{"type":"string"},"account":{"type":"string","description":"\"owner_only\" in the reduced answer for members and viewers."},"owner_name":{"type":["string","null"],"description":"Members and viewers: the owner's display name."}}},"Usage":{"type":"object","properties":{"month":{"type":"string","example":"2026-10"},"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"complete":{"type":"boolean"},"events":{"type":"object","properties":{"pageviews":{"type":"integer"},"goals":{"type":"integer"},"pings":{"type":"integer"},"bot_hits":{"type":"integer"},"cloud_visits":{"type":"integer","description":"Visits from cloud and hosting networks that the site does not count (likely bots). Free: they make no events."},"other":{"type":"integer"},"billable":{"type":"integer"}}},"billable_rule":{"type":"string"},"account":{"type":["object","null"],"description":"Owner only: billable events of all the owner's sites.","properties":{"sites":{"type":"integer"},"billable_events":{"type":"integer"}}},"projected_billable":{"type":"integer"},"plan":{"type":"object","properties":{"name":{"type":"string"},"allowance":{"type":["integer","null"]},"billing_live":{"type":"boolean"},"note":{"type":"string"},"cycle":{"type":["object","null"],"description":"Owner only, billing on: the plan's monthly cycle, which starts on the day of the trial or paid-period start (not on the 1st), with its billable events on all the owner's sites.","properties":{"events":{"type":"integer"},"since":{"type":"string","format":"date-time"},"resets_at":{"type":"string","format":"date-time"}}}}},"tiers":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"events_per_month":{"type":"integer"},"sites":{"type":"string"},"monthly_usd":{"type":"number"}}}},"fits_tier":{"type":"string"},"top_paths":{"type":"array","items":{"allOf":[{"type":"object","properties":{"events":{"type":"integer"},"pageviews":{"type":"integer"},"goals":{"type":"integer"}}},{"type":"object","properties":{"path":{"type":"string"}}}]}},"top_hostnames":{"type":"array","items":{"allOf":[{"type":"object","properties":{"events":{"type":"integer"},"pageviews":{"type":"integer"},"goals":{"type":"integer"}}},{"type":"object","properties":{"hostname":{"type":"string"}}}]}},"controls":{"type":"object","properties":{"excluded_paths":{"type":"array","items":{"type":"string"}},"excluded_hostnames":{"type":"array","items":{"type":"string"}},"drop_pings":{"type":"boolean"},"pageview_sample_rate":{"type":"integer"},"settings_url":{"type":"string"},"docs_url":{"type":"string"}}}}},"TrackingPlan":{"type":"object","description":"analytics.yaml. Unknown keys are errors.","required":["version","events"],"additionalProperties":false,"properties":{"version":{"const":1},"project":{"type":"string","description":"The site's domain."},"integrations":{"type":"array","items":{"type":"string","enum":["stripe","polar","lemonsqueezy","paddle","dodo","shopify","other"]}},"events":{"type":"object","propertyNames":{"pattern":"^[a-z][a-z0-9_]*$","maxLength":64},"additionalProperties":{"type":"object","required":["description"],"additionalProperties":false,"properties":{"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"revenue":{"type":"boolean"},"props":{"type":"object","maxProperties":30,"additionalProperties":{"type":"object","required":["type"],"additionalProperties":false,"properties":{"type":{"enum":["string","number","boolean"]},"enum":{"type":"array","items":{"type":"string"}},"required":{"type":"boolean"},"description":{"type":"string"}}}}}}},"dashboards":{"type":"object","additionalProperties":false,"properties":{"funnels":{"type":"array","items":{"type":"object","required":["name","steps"],"additionalProperties":false,"properties":{"name":{"type":"string"},"steps":{"type":"array","minItems":2,"maxItems":8,"items":{"type":"string","description":"An event name, or page:/path."}}}}}}}}}}},"externalDocs":{"description":"Docs and agent guide","url":"https://twelvebit.app/docs/api"},"x-mcp":{"url":"https://twelvebit.app/api/mcp","transport":"streamable-http","auth":"Bearer tbk_… or tbt_…"}}