openapi: 3.0.1 info: title: Counter API description: >- Counter (counter.dev) is an open-source, privacy-friendly web analytics service. This specification documents the real HTTP surface of the Counter server as implemented in the AGPL-3.0 source at https://github.com/ihucos/counter.dev. The tracking/collect endpoints are public and unauthenticated and are called by the browser tracking snippet. The stats endpoints return aggregated data and are protected either by a session cookie or by a user + token pair (sessionless, read-only). The account endpoints back the dashboard UI and are not a formally published developer API. Endpoint paths are derived directly from the server source; request/response schemas beyond the documented query parameters are not formally published by the project, so responses are described rather than fully schematized. termsOfService: https://counter.dev/ contact: name: Counter (ihucos) url: https://github.com/ihucos/counter.dev license: name: AGPL-3.0 url: https://github.com/ihucos/counter.dev/blob/master/LICENSE version: '1.0' servers: - url: https://t.counter.dev description: Default hosted tracking / collect host (configurable via data-server). - url: https://counter.dev description: Hosted dashboard, stats, and account host. Self-hosted deployments use their own host. paths: /track: get: operationId: track tags: - Tracking summary: Record an aggregated visit. description: >- Called by the tracking snippet ~4.5s after load, once per tab (guarded by a sessionStorage flag). Records a single aggregated visit for the site identified by the data-id. Requires an Origin header (used to derive the site id). No cookies are set and no IP address is stored; country may be supplied explicitly or derived from the CF-IPCountry header. Bots and localhost origins are ignored. parameters: - name: id in: query description: Site data-id (per-account cached UUID). Either id, user, or site must be provided. required: false schema: type: string - name: user in: query description: Account user id, used when id is not supplied. required: false schema: type: string - name: site in: query description: Legacy alias for the user/account id, supported for backward compatibility. required: false schema: type: string - name: referrer in: query description: Document referrer URL; only the host is retained. required: false schema: type: string - name: screen in: query description: Screen resolution as WIDTHxHEIGHT (bucketed to known resolutions or "Other"). required: false schema: type: string - name: utcoffset in: query description: Client UTC offset, used to bucket the visit into the correct local day/hour. required: false schema: type: string - name: country in: query description: Two-letter country code; if omitted the CF-IPCountry header is used. required: false schema: type: string responses: '200': description: Visit recorded. Body is the plain text "OK". content: text/plain: schema: type: string '400': description: Bad request - missing site identifier or missing/invalid Origin header. /trackpage: post: operationId: trackpage tags: - Tracking summary: Record a pageview. description: >- Called by the tracking snippet via the browser Beacon API on every page load to record a pageview counter for the given path. Requires an Origin header. Body is application/x-www-form-urlencoded. requestBody: required: true content: application/x-www-form-urlencoded: schema: type: object properties: id: type: string description: Site data-id (per-account cached UUID). page: type: string description: Page path (window.location.pathname). responses: '204': description: Pageview recorded. Empty body. '400': description: Bad request - missing or invalid Origin header. /query: get: operationId: query tags: - Stats summary: Query archived stats for a date range. description: >- Returns aggregated, archived visit data for the authenticated account over a date range as JSON. Authentication is by session cookie or by a user + token pair (sessionless, read-only). When demo=1 the magic demo account is queried instead. parameters: - name: from in: query description: Start date (YYYY-MM-DD). required: true schema: type: string format: date - name: to in: query description: End date (YYYY-MM-DD). required: true schema: type: string format: date - name: user in: query description: Account user id, for sessionless (token) access. required: false schema: type: string - name: token in: query description: Read-only API token for sessionless access (paired with user). required: false schema: type: string - name: demo in: query description: Set to 1 to query the public demo account. required: false schema: type: string responses: '200': description: Aggregated archive data for the requested range. content: application/json: schema: type: object /dump: get: operationId: dump tags: - Stats summary: Stream live and archived stats (Server-Sent Events). description: >- Opens a text/event-stream (Server-Sent Events) connection that pushes an initial archive payload and then live "dump" payloads (per-site counts, logs, and aggregated visits) as new visits arrive. Authenticated by session cookie, by user + token (sessionless), or via demo. This is the data feed the dashboard UI consumes; it is one-way HTTP streaming, not WebSocket. parameters: - name: utcoffset in: query description: Client UTC offset for day/hour bucketing. required: false schema: type: string - name: user in: query description: Account user id, for sessionless (token) access. required: false schema: type: string - name: token in: query description: Read-only API token for sessionless access (paired with user). required: false schema: type: string - name: demo in: query description: Set to a non-empty value to stream the public demo account. required: false schema: type: string responses: '200': description: Server-Sent Events stream of typed payloads (oldest-archive-date, archive, dump, nouser). content: text/event-stream: schema: type: string /resettoken: post: operationId: resetToken tags: - Account summary: Reset the account read token. description: >- Resets the authenticated account's read-only API token, invalidating the previous token. Requires an authenticated session. responses: '200': description: Token reset. /login: post: operationId: login tags: - Account summary: Log in and establish a session. description: >- Authenticates a user with username and password and sets a session cookie. Body is application/x-www-form-urlencoded. requestBody: required: true content: application/x-www-form-urlencoded: schema: type: object required: - user - password properties: user: type: string password: type: string responses: '200': description: Logged in; session cookie set and account returned. '400': description: Missing input or wrong username/password. /register: post: operationId: register tags: - Account summary: Register a new account. description: >- Creates a new account with a username and password, optional email, and UTC offset preference, then establishes a session. Body is application/x-www-form-urlencoded. requestBody: required: true content: application/x-www-form-urlencoded: schema: type: object required: - user - password properties: user: type: string password: type: string mail: type: string utcoffset: type: string responses: '200': description: Account created; session cookie set and account returned. '400': description: Missing input or account already exists. /logout: post: operationId: logout tags: - Account summary: Log out of the current session. responses: '200': description: Session cleared. components: securitySchemes: sessionCookie: type: apiKey in: cookie name: session description: Session cookie set on login/register, used by the dashboard UI. apiToken: type: apiKey in: query name: token description: >- Read-only account token, paired with the user query parameter, for sessionless access to /query and /dump.