Documentation
One script tag and you have pageviews. Everything after that is optional.
Paste this before the closing body tag. It loads asynchronously, weighs about 2.2KB gzipped, and starts recording pageviews immediately, including on single-page apps.
<script defer src="https://metricward.eu/mw.js"
data-project="prj_yours"></script>Prefer a bundler? Serve the same file from your own origin and import it. An npm package is planned and is not published yet, so there is nothing to install today.
Pageviews tell you about traffic. Events tell you about your product. Properties are scalars, and numbers stay numbers so you can sum them later.
mw.track("checkout_started", { plan: "pro", seats: 12 });No JavaScript on the page? Add data-mw-event="signup" to any element and it fires on click.
Cookieless tracking cannot follow an anonymous visitor across days, by design. identify() records the signed-in user on the event without adding a cookie.
mw.identify("usr_882", { plan: "pro" });Today it arrives as an event property. The Customers screen and the user-based cohort grid read a separate trusted user field that only an authenticated server call may set, and that endpoint is not shipped yet, so both stay empty until it is. Said here rather than in a release note, because the whole point of a docs page is that the code in it does what the sentence above it claims.
Blockers target known analytics hostnames. Serving the script and the endpoint from your own origin is the difference between complete numbers and numbers with a hole in them. This is a Next.js rewrite; there are one-liners for Nginx, Caddy, Cloudflare and Vercel too.
// next.config.js
async rewrites() {
return [{ source: "/_mw/:path*", destination: "https://ingest.metricward.eu/:path*" }];
}Every traffic, customer and revenue report has an Export button, and so do the raw events behind them. It is CSV, it respects the segment and the date range on screen, and it is on every plan including the free one. The raw export stops at 200,000 rows and the response says when it did, because a truncated file that does not admit it reads as the whole dataset.
GET /api/export?project=prj_yours&report=events&days=30
Verifying
Open the event debugger in your dashboard. It shows every event as it arrives, with what the pipeline decided about it: the visitor hash, the session, whether the session was new, the resolved country and device, the consent state, and any tracking-plan violations. If an event was rejected, the debugger says which rule rejected it and what to do about it.
You can also send one by hand, without a browser:
curl -X POST https://ingest.metricward.eu/e \
-H 'content-type: application/json' \
-d '{"p":"prj_yours","t":"pageview","u":"https://your-site.eu/","v":2}'A 204 means it was accepted.
Reference
| mw.pageview(path?, props?) | Record a pageview by hand, for routers you control yourself |
| mw.track(event, props?) | Any custom event, with scalar properties |
| mw.identify(userId, traits?) | Record the signed-in user. Arrives as a property today, see step 3 |
| mw.group(type, id, traits?) | Attach an account, team or workspace to the current user |
| mw.setProperties(props) | Properties added to every subsequent event |
| mw.consent(state) | granted, denied or unknown. Buffers until answered in required mode |
| mw.reset() | Call on logout. Clears the user binding and any buffered events |
| mw.flush() | Force a send, for a page you are about to unload yourself |
| mw.disable() | Stop collecting entirely for this visitor |
Every method returns void and none of them can throw into your page. If something goes wrong the SDK stays quiet, unless you turn on debug mode.
Segments
The same syntax works in the app, in the URL and in the API, so a filtered view is a link you can paste into an issue.
| country==SE | One country |
| country==SE|NO|DK | Any of several |
| country==SE;device_type==mobile | Both conditions, AND |
| channel==email,channel==referral | Either condition, OR |
| path^=/blog | Paths starting with /blog |
| path=~^/docs/[a-z]+$ | A regular expression |
| properties.plan==pro | Any custom property you send |
| num.seats>=5 | A numeric property |
| utm_source!= | Only visits that carry a campaign source |
Keep reading
Tracking events
Naming, properties, identity, and server-side events. Includes the mistakes that are expensive to undo.
Segments
The full filter syntax, custom properties, and the fields you deliberately cannot filter on.
Privacy settings
Collection modes, consent handling, retention, and how a subject request is answered.
Data quality
The tracking plan, the live event debugger, and why bots are stored rather than dropped.
Ready?
Create a free project