Reference
Website Tracker
Add one script tag to your site and watch your visitors' journeys appear as a living process map — pages, forms, clickouts, and the paths between them. No cookies, no consent banner required in the default mode.
Install
Add this to your site's <head>, using the tracker ID from Workspace → Data → API Keys (create a key with type "Website Tracker"):
<script src="https://flowmyna.com/fm.js?t=YOUR_TRACKER_ID" defer></script>
That's it. Pageviews, call-to-action clicks, form starts/submissions, outbound links, and file downloads are captured automatically, and every visit becomes a Session you can explore, filter, and ask the copilot about.
If you call FlowMyna.capture(), FlowMyna.identify(), FlowMyna.register() or FlowMyna.optOut() in code that may run before the script loads, add the stub above the script tag so early calls queue instead of failing:
<script>
window.FlowMyna = window.FlowMyna || {
q: [],
capture: function () { this.q.push(['capture', arguments]) },
identify: function () { this.q.push(['identify', arguments]) },
register: function () { this.q.push(['register', arguments]) },
optOut: function () { this.q.push(['optOut', arguments]) },
}
</script>
To verify the install: browse a few pages of your site, then open the tracker's dataset in Flow Myna — your session should appear within seconds.
What gets captured
Event names are deliberately a small, meaningful vocabulary — every event type becomes a step on your process map:
Viewed /pricing— pageviews, one event type per (normalized) page pathClickout: Book a Demo— outbound links,mailto:,tel:, and file downloadsForm Started/Form Submitted— form interactions (field names only; values are never captured, and fields that look sensitive are excluded)- Anything you capture yourself:
FlowMyna.capture('Trial Activated', { plan: 'team' })
Clicks on internal navigation don't create noise events — the clicked element rides along as navigation_source on the next pageview, so the map shows which button took visitors where.
Each Session carries the analytical dimensions you'd expect from a web-analytics tool, derived server-side: entry and exit page, traffic source and channel (Direct, Organic Search, Organic Social, Referral, Email, Paid, AI Assistants), UTM parameters, device class, browser, operating system, and country/region/city.
Configuration
All configuration is optional, via attributes on the script tag:
| Attribute | Effect |
|---|---|
data-exclude-urls="/app,/admin" | Never track pages under these path prefixes |
data-path-patterns="/blog/*,/project/*/settings" | Collapse dynamic paths into one map step. A * in the middle matches exactly one segment ("Viewed /project//settings"); a trailing /* matches everything below it ("Viewed /blog/" instead of a step per post) |
data-collapse-ids="true" | Replace id-looking path segments (UUIDs, numbers, long tokens containing digits) with :id, so /orders/8123 and /orders/8124 are one step: "Viewed /orders/:id". The easy setting for an app with record pages; patterns still win where they match |
data-privacy-mode="identify" | Opt into persistent visitor identity (see Privacy modes) |
data-auto-track="false" | Disable autocapture; only manual capture() calls are recorded |
data-respect-dnt | Also honor the legacy Do-Not-Track signal (Global Privacy Control is always honored) |
Swipe to see the rest of the table
Labelling visits: session properties
FlowMyna.register({ plan: 'team', role: 'admin' }) labels the current visit. The properties land on the Session object, next to its source, device and entry page, so you can filter and compare sessions by them like any other attribute ("sessions where role = admin"). They're sent with every batch and kept in memory only (nothing is stored in the browser), so call register() on each page load, typically once you know who's signed in. FlowMyna.unregister('role') stops sending one from then on (the Session keeps the value it already has); passing null for a key does the same.
Properties can't overwrite what the server derives itself (source, channel, device, entry and exit page, location…), so a page can never rewrite a visit's attribution.
Excluding your own team
Your staff clicking around your own site skews every number. Three ways to keep them out, strongest first:
- In your app, when a staff member signs in:
FlowMyna.optOut({ dropSession: true }). The browser is opted out from then on (on every page, signed in or not, on any network), and what the tracker recorded for the visit so far, including the pages viewed before signing in, is deleted. Events your own backend attached to that Session stay. A visitor can only drop their own current visit. - On devices where your team never signs in (a phone, a borrowed laptop): open any page of your site with
?fm_opt_out=1once, as a full page load (typed or pasted into the address bar, or a link from outside the site).?fm_opt_in=1undoes it. The parameter is removed from the address bar straight away. - By hand:
FlowMyna.optOut()in the browser console (orlocalStorage.setItem('fm_ignore', 'true')).
The opt-out is per browser: private windows and cleared site data start fresh. Browsers that send Global Privacy Control (Brave by default; Firefox with "Tell websites not to sell or share my data") are never tracked in the first place, private windows included. FlowMyna.isOptedOut() tells you whether the current browser is opted out, and FlowMyna.optIn() turns tracking back on.
Privacy modes
Cookieless (the default)
The tracker stores nothing in the visitor's browser — no cookies, no localStorage, nothing. Visitors are recognized server-side through a salted hash that is rotated and deleted daily, so:
- full journeys within a visit, and repeat visits within the same day, are stitched together;
- no one can be recognized across days, and raw IP addresses and browser signatures are never stored;
- no consent banner is required for this mode under GDPR/ePrivacy, on the same legal footing as other cookieless analytics tools.
The trade-off is deliberate: no returning-visitor or retention analysis in this mode.
Identify mode (opt-in)
Calling FlowMyna.identify('user-123', { plan: 'team' }) — or setting data-privacy-mode="identify" — switches to persistent identity: a visitor ID is stored in localStorage, Sessions link to your User records, and first-touch attribution (first source, first campaign) becomes available.
This mode stores an identifier on the visitor's device and requires consent in the EU. Wire it into your existing consent flow — a common pattern is calling identify() only after login, where your terms of service already cover it.
Beyond the browser: connecting your backend
The script sees what happens in the browser. For everything else, combine it with the Events API:
- Browser-visible actions →
FlowMyna.capture('Step Name', {...})in your frontend. Joins the current session automatically. - Backend truths (payment confirmed, subscription changed, order shipped) → send from your server with your secret
fm_live_API key. Never put the secret key in browser code — the tracker ID is the only credential that belongs in a page. - Joining the two: call
identify()when you know the user, and reference the same{ "type": "User", "id": "user-123" }(or your own objects — Order, Subscription) in your server-side events. The web journey and your business process become one connected graph. - To attach a server-side event inside the current web session's case, forward
FlowMyna.getSessionId()to your backend and include{ "type": "Session", "id": "<that id>" }in the event's objects.
Limits and good citizenship
- Batches are capped at 100 events; event properties at 30 keys / 500 characters each.
- Each tracker key has an event budget over a rolling 30-day window; events beyond it are dropped (the response says so in an
x-fm-droppedheader). - Known bots and automated browsers are filtered server-side.
- Query strings are stripped from captured URLs (UTM parameters excepted), and email addresses appearing in captured text are masked before leaving the page.