Custom events
window.stepmetrics('signup');
window.stepmetrics('download');
window.stepmetrics('purchase', { plan: 'pro' }, { revenueCents: 2900, currency: 'USD' }); Money is passed in minor units so it stays integral all the way through.
Every custom event a site records appears on the dashboard's conversions row by itself,
busiest first, for as long as it is firing. To keep a name on the row at zero - so a
conversion that stops is seen to have stopped rather than quietly leaving - pin it in the
site's conversions configuration, in the order you want it shown.
Each metric can carry a description of what it measures, written from the tile itself on the
dashboard or as metricDescriptions in the site's configuration. It shows under the
number, truncated, and in full on click, and every API and MCP response that returns the metric
returns the description with it, so an agent reading "downloads" is told what a download is.
Across sites the dashboard keeps each site's conversions in its own row, because a
download on two sites can mean two different things.
From a server
Some conversions are only known to your backend: a purchase confirmed by a payment webhook, an account created by a signup handler. Post them to the same endpoint and declare that a server is reporting, so the reporter's user agent is not read as a crawler.
POST https://app.stepmetrics.co/v1/collect
content-type: application/json
{
"siteId": "YOUR_SITE_ID",
"name": "purchase",
"path": "/checkout",
"client": { "platform": "server" },
"props": { "plan": "pro" },
"revenueCents": 2900,
"currency": "USD"
} The count and the revenue are exact, and the event appears in conversions and the event stream. It is not a visit and not a visitor: nobody loaded a page, so it never adds to your visits, visitors or bounce rate. What a server cannot supply is the buyer's browser, so the event does not join the visit that started on your pricing page. Attribute across that gap from your billing records, where the durable identifier is the order.
A server sends custom events only. A pageview declared from a server is refused with a 400, because nobody saw a page.