Clicktag docs
Email reports
Clicktag emails daily, weekly or monthly stats digests: set workspace defaults once for every site, give any site its own settings (up to 25 recipients) or switch it off, and get one workspace digest across all sites, plus API alerts that email when an API fails, slows down or goes quiet, checked every minute.
Base URL: https://clicktag.io. Download OpenAPI or read this page as Markdown.
How the layers work
| Layer | Covers | Wins when |
|---|---|---|
| Workspace defaults | Every verified site set to Workspace default | The site's mode is inherit (new sites start here) |
| Site settings | One site, up to 25 recipients | The site's mode is custom |
| Workspace digest | One email per recipient across all your sites | Always, independent of the site modes |
A site on Custom never picks up default changes; a site on Workspace default always does. A site set to Off sends nothing, and unverified sites never send.
Access
Every route below except unsubscribe needs the owner's Firebase ID bearer token or a management key (read for GET, manage for changes); test sends need the Firebase token. The unsubscribe endpoints are public: the token in the link is the capability.
| Method | Route | Result |
|---|---|---|
| GET | /api/reports/defaults |
Workspace defaults and which sites use them |
| PUT | /api/reports/defaults |
Save workspace defaults |
| GET | /api/reports/workspace |
Workspace digest settings |
| PUT | /api/reports/workspace |
Save workspace digest settings |
| POST | /api/reports/workspace/test |
Email the digest to yourself now |
| GET | /api/reports/workspace/preview |
Render the digest as HTML |
| PATCH | /api/sites/{hostname} |
Set reports_mode to inherit, custom or off |
| GET | /api/sites/{hostname}/reports |
Mode, site subscriptions and what the site sends |
| POST | /api/sites/{hostname}/reports |
Add a recipient (201) |
| PATCH | /api/sites/{hostname}/reports/{id} |
Update frequency, schedule, sections |
| DELETE | /api/sites/{hostname}/reports/{id} |
Remove a recipient |
| POST | /api/sites/{hostname}/reports/{id}/test |
Queue a test email for the current window |
| GET | /api/sites/{hostname}/reports/preview |
Render the email as HTML |
| GET | /api/reports/unsubscribe/{token} |
Confirmation page (HTML) |
| POST | /api/reports/unsubscribe/{token} |
One-click unsubscribe (HTML) |
Cadence and windows
| Frequency | Sends | Covers |
|---|---|---|
daily |
every day | yesterday (local) |
weekly |
Mondays | previous Monday to Sunday |
monthly |
the 1st of the month | previous calendar month |
send_hour is a local wall-clock hour (0-23) in the subscription's IANA timezone; daylight-saving changes follow the clock, not a fixed UTC offset, so a send hour that a spring-forward change skips sends when the clocks jump, and one that a fall-back change repeats sends once. Due reports are queued at 7 minutes past each hour. Every report covers the last fully elapsed period, so a weekly report mailed Monday 09:00 Europe/Amsterdam always covers the Monday-Sunday that just ended in that timezone.
Timezones are stored in canonical form, so europe/amsterdam comes back as Europe/Amsterdam; fixed offsets such as +02:00 are refused with 400 invalid timezone.
Changes compare with the period before of the same kind: the day before, the Monday to Sunday before, or the whole previous calendar month. A top row's change uses its own count in that period, even when it wasn't a top row then.
Sections
| Key | Default | Contents |
|---|---|---|
overview |
always on | Totals, change vs previous period, chart, live count |
pages |
on | Top 5 pages with change vs previous period |
referrers |
on | Top 5 referrers with change |
countries |
off | Top 5 countries with change |
events |
off | Top 5 events with change |
alerts |
on | Spike/dip callout for abnormal days |
api |
on | Requests, error rates, p95, top and failing endpoints |
The alert section flags the most extreme day in the period whose pageviews land more than 2.5 standard deviations and more than 50% away from the trailing 28-day mean, and names the top 3 referrers of a spike day. It needs at least 14 days of history before the period, so new sites get no callout for about two weeks.
The api section shows request volume and p95 latency with change, 5xx and 4xx rates, the top 5 endpoints and the endpoints with the most 5xx responses. It only appears when the site received API requests in the period or the one before; a site with API traffic but no pageviews in either period gets it as the headline instead of empty web totals.
Workspace defaults
PUT /api/reports/defaults accepts any subset of enabled, recipients (up to 10), frequency, timezone, send_hour and sections. Changes apply on save to every site on Workspace default; each recipient gets one email per site. GET also returns sites (how many sites inherit, which are on Custom or Off), stalled (default recipients that failed five times in a row on an inheriting site) and dropped (addresses removed by an unsubscribe or a hard bounce since the last save). A save clears dropped and resets the failure counts of default and digest recipients, so stalled ones are retried.
curl -X PUT "$TT_HOST/api/reports/defaults" \
-H "Authorization: Bearer $TT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled":true,"recipients":["team@company.com"],"frequency":"weekly","timezone":"Europe/Amsterdam","send_hour":9}'Send back the updated_at you read to avoid overwriting someone else's change: if the settings changed in between, the save returns 409. The defaults and the digest share one version, so save one before editing the other. POST /api/sites/{hostname}/reports on a site that isn't set to Custom returns 409 with switch this site to custom reports first.
Per-site settings
PATCH /api/sites/{hostname} with {"reports_mode":"custom"} gives the site its own recipients; the first switch to Custom copies the current default recipients, schedule and sections into the site, so nothing changes until you edit them. Switching away from Custom keeps the site's own recipients for when you switch back. GET /api/sites/{hostname}/reports returns mode, the site's subscriptions and effective: who the site mails right now and when, or why it sends nothing.
POST /api/sites/{hostname}/reports accepts email, frequency, and optional timezone (default UTC), send_hour (default 9) and sections. The site must be verified (403) and set to Custom (409). One subscription per site, email and frequency; duplicates return 409. At most 25 subscriptions per site, paused ones included and an address on two frequencies counting twice; the next one returns 429 with too many recipients for this site. Addresses you add are active right away, without a confirmation email.
curl -X POST "$TT_HOST/api/sites/$TT_HOSTNAME/reports" \
-H "Authorization: Bearer $TT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"email":"teammate@company.com","frequency":"weekly","timezone":"Europe/Amsterdam","send_hour":9}'PATCH accepts any subset of frequency, timezone, send_hour, enabled, sections. Setting enabled to false pauses without deleting. Setting it back to true resets fail_count and needs a verified site. Moving an address to a frequency it already has returns 409.
POST .../test queues a real email to that subscription's address for the current last-full-period window and returns 202 with the window it used, or 502 when the queue is unavailable. It sends at most once per 30 seconds per subscription (429 otherwise), and a paused subscription returns 409. Test sends never consume or block the scheduled window; a delivered test resets fail_count and sets last_sent_at.
GET .../preview?frequency=weekly&tz=Europe/Amsterdam§ions=pages,referrers,alerts renders the email HTML inline; omit sections to see everything on. overview is always included, unknown section keys are ignored, frequency defaults to weekly and an unknown tz falls back to UTC.
Workspace digest
One email per recipient with total visitors and pageviews against the previous period, a row per site with visitors in either period, the biggest movers (up to three each way among sites with at least 50 visitors in either period), the quiet sites, and one requests and 5xx line per API site. It follows the same cadence and windows as site reports.
PUT /api/reports/workspace accepts enabled, recipients (up to 10), frequency, timezone, send_hour and updated_at, with the same rules as the defaults. POST /api/reports/workspace/test emails the digest for the last full period to you, the signed-in owner, at most once per 30 seconds (429 otherwise), and returns the address and window. It needs at least one digest recipient and a verified site (400 otherwise), though it only mails you. GET /api/reports/workspace/preview?frequency=weekly&tz=Europe/Amsterdam renders it as HTML.
Delivery and unsubscribe
Mail is sent from reports@totallytics.com over a Postmark broadcast stream with List-Unsubscribe and RFC 8058 one-click headers. Unsubscribing from a site's own subscription sets enabled to false and keeps history; re-enable from the site's settings. Unsubscribing from a default or digest email removes that address from that workspace list right away (a default address stops for every inheriting site); it shows up under dropped until the next save.
A permanent failure (an invalid address, or one Postmark marks inactive, for example after a hard bounce) disables a site's own subscription immediately. On a default or digest email, an inactive address is removed from the workspace list and an invalid one counts as a failure. Transient failures retry three times, then the send is marked failed and fail_count rises. Recipients with five consecutive failures stop being scheduled until re-enabled or saved again.
API alerts
Per-site rules on API traffic, checked every minute. All routes need the owner's Firebase ID token or a management key (read for GET, manage for changes), except POST .../test, which needs the Firebase token. Adding, changing and testing rules also needs a verified site; listing and deleting don't.
| Method | Route | Result |
|---|---|---|
| GET | /api/sites/{hostname}/alerts |
List rules |
| POST | /api/sites/{hostname}/alerts |
Add a rule (201) |
| PATCH | /api/sites/{hostname}/alerts/{id} |
Pause or resume with enabled |
| DELETE | /api/sites/{hostname}/alerts/{id} |
Remove a rule |
| POST | /api/sites/{hostname}/alerts/{id}/test |
Email a sample alert now |
| Metric | Fires when | threshold |
|---|---|---|
error_rate |
the share of 5xx responses in the window is above the threshold | percent, 0 to below 100 |
p95_ms |
p95 latency in the window is above the threshold | ms, below 600000 |
silence |
the window has no requests while the same window yesterday had min_requests |
none |
window_minutes is 5, 15 or 60, ending a minute back so in-flight batches land first. Set method and route (the template shown in the API view, such as /v1/orders/:id) to watch one endpoint; leave both out for the whole API. Windows with fewer than min_requests requests (default 20) never fire a rule, and a firing rule resolves in them with a "too few requests" email. A p95_ms window without latency buckets keeps the current state. recipients defaults to the signed-in owner's email; a management key has none, so it must send recipients (400 recipients is required when using a management key otherwise). Up to 10 recipients per rule and 20 rules per site, after which adding one returns 429.
curl -X POST "$TT_HOST/api/sites/$TT_HOSTNAME/alerts" \
-H "Authorization: Bearer $TT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"metric":"error_rate","threshold":2,"window_minutes":15,"method":"POST","route":"/v1/orders"}'A rule is ok or firing. Crossing the threshold sends one alert and marks it firing; nothing more is sent until it recovers, which sends one resolved email. An alert stays firing or resolved for at least 10 minutes, so a value hovering at the threshold can't flap. last_value holds the value behind the latest change. Pausing resets the rule to ok. POST .../test emails a sample only to you, the signed-in owner, once per 30 seconds per rule, and leaves the state alone.
Alerts come from alerts@totallytics.com on a transactional stream with a link to the site's API view. Every firing and resolved email carries a one-click unsubscribe link (and List-Unsubscribe headers) for its own recipient: GET /api/alerts/unsubscribe/{id}/{token} shows a confirm page and POST removes that address from the rule, or pauses the rule when it was the last one. Report unsubscribes don't affect alerts.