Tracking
What Clamp captures on its own, and what you can add. Pageviews, sessions, and engagement for free. Custom events and Money on Pro.
What’s captured automatically
Every pageview is enriched on our servers with the dimensions below. No extra code, no extra config, all available on the free plan and filterable in every chart, funnel, and MCP query.
| Category | Fields |
|---|---|
| Page | url, pathname, referrer, referrer_host |
| Attribution | utm_source, utm_medium, utm_campaign, utm_content, utm_term, channel (direct, organic_search, organic_social, paid, email, referral) |
| Device | device_type (desktop, mobile, tablet, bot), browser, browser_version, os, os_version, screen_width, screen_height, language |
| Geo | country, region, city (from IP, IP itself is never stored) |
| Session | Per-tab session ID, bounce rate, engagement_seconds (visible time only), rotates after 30 min idle |
| Identity | anonymous_id (no cookies, daily-rotating fingerprint) |
The SDK fires on initial load, history.pushState, replaceState, popstate, and bfcache restore, so SPAs work without extra setup. localhost and 127.0.0.1 are skipped by default so dev traffic never lands in your charts.
See Engagement for how visible time and bounce rate are measured.
Custom events Pro
Custom events require a Pro or Growth plan. On the free plan, track() calls are accepted by the SDK but dropped at ingest. Pageviews, sessions, and engagement keep working for everyone. See pricing.
Track any action with track(name, properties?). Properties are a flat map of strings to strings (or Money values — see below).
import { track } from "@clamp-sh/analytics"
track("signup", { plan: "pro", source: "pricing_page" })
track("feature_used", { name: "csv_export" })
track("invite_sent", { role: "editor", team: "acme" })Calls made before init() are queued and flushed after init runs. See Properties for naming rules and limits.
Typed events
Declare your event map once and get autocomplete and type checking on every track() call. Zero runtime cost.
import { init, track } from "@clamp-sh/analytics"
type Events = {
signup: { plan: string; source: string }
purchase: { amount: string; currency: string }
feature_used: { name: string }
}
init<Events>("proj_xxx")
track("signup", { plan: "pro", source: "homepage" }) // ✓ autocomplete
track("signup", { wrong: "field" }) // ✗ type error
track("unknown_event") // ✗ type errorMoney properties
Any property whose value is a Money object becomes a queryable revenue field. Attach total, mrr, tax, anything, and get_revenue aggregates them over any cohort.
import { track } from "@clamp-sh/analytics"
track("checkout_completed", {
plan: "pro",
total: { amount: 348.00, currency: "USD" },
})See Revenue for the full pattern: refunds, mixed currencies, and server-side tracking for authoritative numbers.
Property limits
| Constraint | Limit |
|---|---|
| Max keys per event | 20 |
| Key length | 128 chars |
| Value length | 512 chars |
| Payload size | 64 KB |
Properties are flat. Nested objects (other than Money), arrays, and non-string values are rejected at ingest.