Track the events metrics read
Custom metrics are a lens over events. This page is about sending those events. Everything you track lands in one event store, and metrics aggregate them at analysis time, so you send raw events here and shape them into numbers there.
There are three ways to emit an event. They all reach the same store and are interchangeable.
Browser: statskit.track
Section titled “Browser: statskit.track”For anything that happens in the page, use the tracker. It already knows the visitor and session, so you only pass the event name and props.
window.statskit?.track("checkout", { value: 4999, plan: "pro" });value is in minor units (cents), so a $49.99 checkout is 4999. See tracking events for declarative data-statskit-* attributes and autocapture.
Server REST: POST /v1/track
Section titled “Server REST: POST /v1/track”For events that only your backend can see (a webhook-confirmed payment, a job that completes), post them from the server with a ssk_ server key or a vsk_ secret key.
curl -X POST https://statskit.ai/api/v1/track \ -H "Authorization: Bearer ssk_live_…" \ -H "Content-Type: application/json" \ -d '{ "unit": "user_8f21", "event": "checkout", "value": 4999, "currency": "usd", "props": { "plan": "pro" }, "url": "/pricing", "eventId": "evt_2026_ord_10432" }'| Field | Meaning |
|---|---|
unit | The id you experiment on (visitor id, user id, account id). Ties the event to the right assignment. Required. |
event | The event name a metric aggregates. Required. |
value | Amount in minor units (cents). Read by sum / mean metrics. |
currency | ISO currency for the value, for example usd. |
props | Free-form properties object. |
url | The page or context the event belongs to. |
ts | Event timestamp (ms). Defaults to now. |
eventId | Idempotency key. See below. |
Server SDK: client.track
Section titled “Server SDK: client.track”The @statskit/sdk server client wraps the same ingest and batches events for you (POST /v1/events under the hood). See the SDK overview for setup.
await client.track("user_8f21", "checkout", { value: 4999, currency: "usd", props: { plan: "pro" }, url: "/pricing", eventId: "evt_2026_ord_10432",});The signature is client.track(unit, eventName, { value?, currency?, props?, url?, ts?, eventId? }).
Values are in cents
Section titled “Values are in cents”Everywhere you send money, send minor units. $49.99 is 4999. This keeps sum and mean metric math exact and lets the usd format render it correctly.
Idempotency with eventId
Section titled “Idempotency with eventId”Server events often fire from retryable code: webhook handlers, queue workers, cron jobs. Pass a stable eventId derived from the thing that happened (an order id, a Stripe event id) and StatsKit de-duplicates repeat deliveries so a single payment is not counted twice.
Browser or server?
Section titled “Browser or server?”- The event is a UI action the visitor takes (click, view, scroll).
- The visitor is present and the tracker is loaded.
- You want autocapture and automatic visitor/session context.
- Only your backend knows the event happened (a confirmed charge, an async success).
- You need idempotency because the source can retry.
- The event should count even if the browser closed or an ad blocker dropped the client script.
For revenue specifically, tracking server-side after the payment settles is the reliable path. See the server revenue recipe.