One snippet. Every touch, email and purchase — stitched.
There are three ways to run Flowsk, and they differ in exactly one respect: who writes the visitor id. That single choice decides whether your attribution window is real or decorative.
durability = f(who sets the cookie), not f(who owns the domain)
A cookie written by JavaScript is capped at 7 days in Safari, even on your own domain. A cookie written by a server in an HTTP response is not capped. Snippet mode is fast to install; proxy mode is durable. Both send the same events to the same API.
The anonymous id always travels in the request payload, never as a third-party cookie — so third-party blocking never enters into it.
The three modes
Start at the left, move right when the data matters more than the setup time.
The JavaScript owns the id
- 1Paste one script tag before </head>
- 2Pageviews, clicks and submits start flowing
- 3Optionally add server-side conversions later
Getting your first journeys today.
Your backend owns the id
- 1Your server sets a first-party HttpOnly id cookie
- 2The tracker posts to your own origin
- 3Your backend forwards events to the Flowsk API
Anything where buyers take more than a week.
The Rails app owns the id
- 1A before_action mints and sets the id server-side
- 2Pageviews are enqueued as background jobs
- 3Identification and billing confirm server-side
What flowsk.com itself runs.
The whole install
One tag. It reads the id, records navigation, and posts as a CORS simple request so there is no preflight and sendBeacon works on unload.
<script async src="https://flowsk.com/flowsk.js" data-write-key="pk_live_…"></script>
Then confirm the money from your backend
Same API, source: "server", and a dedup key both sides know.
POST https://flowsk.com/api/events
Content-Type: application/json
{
"write_key": "pk_live_…",
"anonymous_id": "f9fc2575-8339-4573-9ec9-02d2a2282f5c",
"events": [{
"source": "server",
"category": "purchase",
"name": "purchase",
"email": "hello@example.com",
"value_cents": 12900,
"dedup_key": "order_10482"
}]
}
Full reference: the events API · de-duplication · proxy setup
Why both sides, always
They are not redundant. They answer different questions, and neither one alone is enough.
What the person did
- Pageviews, clicks, form submits, tool usage
- Real-time and high volume
- Cheap to lose — one missed click changes nothing
- Blockable: 20–30% of paid traffic runs a blocker
What actually happened
- Purchases, signups, confirmed leads, CRM stages
- Authoritative — the payment processor said so
- Carries the cause: which action triggered the identification
- Unblockable: it never touches the browser
Counted once, with the server's truth
Send the same fact twice on purpose. A shared dedup key collapses the pair into one conversion marked confirmed by both sides — and the server's value wins.
| Event | Source | dedup_key | Result |
|---|---|---|---|
| purchase $129.00 | client | order_10482 | Recorded, pending confirmation |
| purchase $129.00 | server | order_10482 | Merged → one conversion, source: both |
| purchase $129.00 | server (webhook retry) | order_10482 | Ignored — already confirmed |
Choosing a good key is the whole skill — read the de-duplication guide.
Frequently asked questions
Which mode should I choose?
Start with the snippet — it takes minutes and you will see journeys the same day. Move to proxy mode when you want the visitor id to survive Safari past seven days, which matters most if your buyers take longer than a week to decide.
What is the difference between a client event and a server event?
A client event is sent by the browser: fast, detailed, cheap, and blockable. A server event is sent by your backend: authoritative, unblockable, and it carries context the browser never had — like which action actually caused an identification. Anything with money attached should exist on both sides.
What is a dedup key?
A string both sides can derive independently — an order id, a subscription id, a submission id. When the client event and the server event carry the same key, Flowsk merges them into one record marked confirmed by both, instead of counting the sale twice.
Do I have to send server events?
No, but purchases without them are the weakest link in any measurement setup. If a client-side purchase event is blocked, the sale simply never appears.
Does the snippet slow my site down?
It is a few KB, loads async, and posts with sendBeacon so it never blocks navigation. It uses a CORS simple request, which means no preflight round trip.
Can I self-host or proxy through my own domain?
Yes — that is proxy mode. Your backend receives events on your own origin and forwards them to the Flowsk API server-to-server. Nothing on your page talks to a third-party domain.
Install it in five minutes, make it durable in an hour.
Start with the snippet and watch the journeys appear. Move to proxy mode when you want the window in your report to be the window you actually have.