The short version
- Almost everything written about conversion funnels is written about shops, so the worked example is never yours
- Your funnel runs from a pricing page to a signup to a first payment, and it crosses two domains on the way
- The step that decides the business, the upgrade, usually finishes on your server rather than in the browser
- Tiny Funnel is built for that shape. The funnel is drawn before you configure anything
The funnel a software business actually has
Search for anything about conversion funnels and you land in a shop. That is where the volume is, so it is fair enough. It does mean the worked example is always somebody else's, and that the advice under it assumes a kind of buying decision you do not sell.
Yours looks more like this:
- Somebody arrives on the marketing site, from a search, a newsletter, a comparison page or a link somebody posted
- They read
/pricing - They sign up at
/signup, which is very often a different domain - They reach
/welcomeand do, or do not do, the first thing the product needs from them - Days later a card goes on, and your backend hears about it before their browser does
Five steps, four gaps, and every gap is a different problem. Pricing page to signup form is a pricing or positioning question. Signup to the first real action is an onboarding question. Onboarding to the card is a product question, and it takes a week to show up.
Point Tiny Funnel at that path and it opens on that path: bars top to bottom, drop-off between each pair of steps, on load, with nothing to assemble first.
Your funnel crosses two domains, and that is where journeys break
The marketing site is on one name and the app is on another: yoursite.com and app.yoursite.com if you were tidy about it, two unrelated names if the app came first.
This is the thing that quietly breaks funnel tracking, and the reason is mechanical. Tiny Funnel is cookie-less: each browser gets a random first-party id, kept in localStorage. localStorage names a browser on a domain. So without help, the person who read your pricing page and the person who signed up ten seconds later are two different people, and the second one has no referrer and no entry page. That is a large part of why the direct bucket in most analytics tools is so implausibly big.
Three mechanisms stitch them back, and none of them uses a cookie:
- Links between your own domains are decorated automatically. A
tf_vparameter carries the visitor id across, and your backend can put the same parameter on a link inside an email, which is the other place a journey usually restarts tf.identifywrites your own user id onto the visitor. Once somebody is signed in you already know who they are, so the laptop and the phone become one journey instead of two- Server events match on that id first, then on the visitor token, then on IP
The full explanation, including what happens when a browser clears its storage, is in the docs: how one visitor's journey stays in one piece.
The step that decides the business finishes on your server
Trial to paid is the number you actually care about, and the browser is usually not there when it happens. A subscription webhook fires, or an invoice is paid on a schedule, somewhere the visitor never sees.
Your backend knows. Post a named event to /api/v1/events with the funnel's API key and it attaches to the visitor who caused it, so the last step of your funnel is the real one rather than a proxy for it. The endpoint, the auth and the payload are on the server events page.
One rule travels with that: a server event can never be a funnel's first sight of somebody. It attaches to a visitor the script tag has already seen, or it is dropped, and dropped events are listed on the Install panel for 30 days rather than vanishing.
Steps that describe your app, not just its URLs
A funnel step does not have to be a URL, which matters more in software than anywhere else, because half of a modern app is one URL.
| A step can be | For example |
|---|---|
| A page view | Somebody reaches /pricing |
| A click | Somebody clicks "start free trial" |
| A server event | Your backend posts subscription.created |
| A page state | An element matches a CSS selector, or contains a given word |
That last one is the escape hatch for a single-page app. If /welcome is /welcome whether onboarding finished or not, count the step only when .plan-status contains the word "subscribed". That is a condition on a step, not a code change and not a deploy.
None of this is written as event code. You open your own site in setup mode and click through your own signup flow, and each click records a step. It is a tiny setup on purpose: there is no taxonomy to agree with anybody and no naming convention to keep.
Separately, there is a click capture switch. It is off until you turn it on, and when you do it records clicks on interactive elements only: buttons, links and form controls, never prose. It never captures typed values, and it honours data-tf-private on any element or any of its ancestors.
One filter, and the funnel redraws
The useful question is almost never "what is our conversion rate". It is "do visitors from this place behave differently from visitors from that one". So the funnel filters by date range, referrer domain, UTM source, entry page, country, and how far down the visitor got. Pick "from Google" and "reached at least step two" and the bars, the chart and the visitor list all redraw for exactly those people.
The part worth knowing: every option in a filter dropdown carries its own visitor count and how many of those converted. You can often read the answer off the list without selecting anything, which is the difference between having the data and having the answer.
When the numbers stop being enough, open one person's journey and read the whole path, return visits included, down to the step where they went. Five of those usually make the answer obvious, because people fail in patterns.
Where this is the wrong tool
Worth saying plainly, because it is the fastest way to be believed. If you need cohort retention curves, feature flags and experiments, session replay, a SQL layer, or general analytics for the whole site, this is not that. Product analytics measures how people use the thing. This measures who bought it, and where everyone else went. Both are real questions and they are not the same one.
Plenty of teams run a general analytics tool for site-wide traffic and this for the one path that pays for everything. That is a normal answer, and it is how this compares to Google Analytics 4 in one line: for the funnel question it is fewer moves, and for everything else you should keep what you have.
Frequently asked questions
Does this replace Google Analytics? For the funnel question, yes, and in fewer moves. For everything else, no, and we are not trying to. Running both is a perfectly sensible answer.
How long does setup actually take? About two minutes for the common case. Paste one script tag, then open your own site in setup mode and click through the flow. No SDK, no npm package, no build step, no event code.
Our marketing site and our app are on different domains. Will it work? Yes. Put the same script tag on both and add both to the funnel's allowed domains, a list you edit from the Install panel. Links between your own domains are decorated automatically, so the journey carries across.
Can it count an upgrade that happens in a webhook? Yes. Post it as a server event and it attaches to the visitor who caused it. The one condition is that the visitor has been seen by the script tag first, otherwise there is no journey for it to join.
Do we need a cookie banner? We cannot answer that for you, and anybody who does is guessing about your jurisdiction and your use. What we can tell you is exactly what the script does: no cookies, first-party only, no fingerprinting, no cross-site tracking, nothing shared with ad networks, typed values never captured, and a random visitor id in localStorage with roughly a six-month idle lifetime. Take that to whoever advises you.
What happens to the bill when a campaign works? Nothing. It is $10 a month per active funnel, no seats and no event tiers, so traffic never moves the price. Pause a funnel and billing stops while the history stays. The trial is seven days with no card, and one funnel until a card is added.