Three questions get bundled together and called “the funnel”, and Leed answers each of them with a different mechanism. How far did this visit get? is the session funnel, computed from session aggregates. What kind of content is this reader consuming? is journey stages, which are your own vocabulary attached to your own pages. Where did this visit come from? is attribution, assembled from UTM-tagged arrivals.
Keeping them apart matters, because two of the three are called “journey” and they have nothing to do with each other.
The session funnel
The session funnel has four nested steps, computed per entry content type — the page type of the page a session started on.
| Step | Definition | Source field |
|---|---|---|
| Sessions | Sessions that entered on this content type and viewed at least one page | sessions.entryPageTypeId |
| Engaged | Of those, sessions that saw more than one page | sessions.isBounce = 0 |
| Explored 3+ pages | Of those, sessions that viewed three or more pages | sessions.pageviews >= 3 |
| Converted | Of those, sessions that produced at least one form fill | sessions.formFills > 0 |
The steps are genuinely nested — every converted session is also engaged, and every engaged session is also a session — so the drop-off between bars is real drop-off rather than four unrelated counts drawn next to each other.
Two definitions to hold on to, because they are narrower than the words suggest:
- Engaged means “not a bounce”, and a bounce in Leed is a session with exactly one page view. Time on page does not enter into it. A reader who spent twenty minutes on a single article and left is not engaged by this measure.
- Converted means a form fill happened during the session. Nothing else counts — not a video watched to the end, not a file downloaded, not a short link followed. A conversion here means a form fill, which is defined in how forms work.
Only sessions that actually started — real sessions with at least one page view — are counted, so bot-shaped traffic that never produced an event does not inflate the first bar.
The Journey funnel card on Know shows one content type: the one with the most sessions in the window. Its subtitle tells you which — “Sessions entering on Article”, or whatever your busiest entry type is. The other content types are computed but not displayed; there is no per-type breakdown screen and no control for switching types. If the busiest entry type changes between two visits, the card is quietly describing a different population, so read the subtitle before comparing.
With too little traffic to make a funnel at all, the card says “Not enough session data for a funnel yet” rather than drawing four empty bars.
Journey stages
Journey stages are your own funnel vocabulary. You define them once in Settings → Journey stages, and assign one to a page in Page Settings.
A stage is a name and an optional description — nothing more. Leed ships no default set and imposes no ordering; “Awareness → Consider → Decide” is the example in the settings card, not a schema. Use whatever your team already says out loud.
Creating and deleting stages needs the Content Publisher role or higher. Defining and assigning the stages themselves is covered in journey stages.
Stage is a property of a page, not of a person. Leed does not stamp a visitor with a stage and move them along it. It records which pages each session saw; the stages on those pages are what let you say where a reader had got to. That is a deliberate design: a stage assigned by hand goes stale the moment behavior contradicts it, while a stage read out of behavior cannot.
The other use is planning, and it is the one most teams get value from first. Count your own pages by stage. Thirty awareness pages and two decision pages is a visible gap, and it is a gap you can see without any traffic at all — which makes it the one piece of funnel work you can do on day one.
Two different things called a journey
| Your journey stages | Engage’s buying journey | |
|---|---|---|
| Where defined | Settings → Journey stages | Fixed in the product |
| Who sets it | You | Derived automatically |
| Values | Whatever you name | awareness, consideration, evaluation, decision |
| Attached to | A page | A contact |
| What reads it | Your own reporting, the API and MCP | Engage |
Engage’s buying journey is derived from a contact’s readiness and intent scores — the higher of the two decides the stage, at thresholds of 25, 50 and 75. It describes a person’s buying temperature. Your journey stages describe a page’s place in your content plan. Engage’s four-stage journey is explained in Engage scores.
Attribution: where a visit came from
Attribution answers one question: which campaign or channel brought this visit. It is a single ranked table with six columns.
| Column | What it holds | Typical value |
|---|---|---|
| Count | Arrivals matching this exact combination | 412 |
| Source | utm_source — who sent them | newsletter, google, internal |
| Campaign | utm_campaign — which effort | spring-launch, leed |
| Medium | utm_medium — what kind of placement | email, cpc, hero, recommendation |
| Content | utm_content — which variant or link text | Get started |
| Term | utm_term — the specific item | a button name, a page id |
Rows are grouped on all five UTM values together, so two arrivals only share a row when every field matches. Default sort is by count, descending.
Three sources, one table
Attribution is a union of three different kinds of event, not a single log.
| Source | What it represents |
|---|---|
| Page views with an external referrer | Someone arrived from a third-party site — a search engine, a social platform, an ad. Referrers are recorded on the first page view of a session, so these are genuinely arrivals rather than internal navigation. |
| Short-link redirects | Someone followed a /s/ link. These usually come from off-site placements, and they carry the UTM values you configured on the link. |
| In-site clicks carrying UTM context | Someone clicked a link on one of your own pages that had a data-reason attribute on it. |
Short links are the cleanest way to get UTM-tagged arrivals from off-site placements — see short links and attribution.
Every row from all three sources must carry a utm_source. Rows without one are dropped before anything is grouped.
flowchart TD
C["In-site click carrying data-reason"] --> G
S["Short-link redirect via /s/"] --> G
P["Page view with an external referrer"] --> G
P -.-> SR["Referrer host is one of your own domains — dropped as a self-referral"]
G{"Does the row carry a utm_source?"}
G -- "no" --> D1["Dropped — untagged traffic is not attribution"]
G -- "yes" --> H{"utm_campaign=leed and utm_medium=email?"}
H -- "yes" --> D2["Dropped — email tracking, counted elsewhere"]
H -- "no" --> Q{"Was a pageId supplied?"}
Q -- "no — site-wide" --> SW["Keep short links and external referrals. Drop every on-site click."]
Q -- "yes — one page" --> PS["Keep all three, filtered to arrivals at that page"]
Site-wide attribution excludes your own on-site clicks
This is the least obvious behavior on the page, and the one most likely to make you think attribution is broken.
In the site-wide view, a click that happened on one of your own pages is not an arrival. Someone already on your site clicking through to another of your pages is internal navigation; counting it as an inbound channel would make your own site your biggest traffic source, every time. So site-wide attribution drops those clicks and keeps short links and external referrals.
Self-referrals are removed
Your own domains are excluded as referrer hosts — the public domain, the preview domain, and the Cloudflare Pages domains behind both. Without that filter, one of your own pages linking to another with a stale ?utm_source= string left in the URL would appear as an external channel called you.
This is also a good reason not to leave UTM query strings on internal links: they no longer create a phantom channel, but they still clutter URLs your readers copy and share.
Untagged organic referrals are not here
Attribution requires a UTM source. A plain referral from a blog that linked to you without any tagging has no utm_source and will not appear in this table at all.
That traffic is not lost — it is in the referrer metrics, which group arrivals by referring host regardless of tagging. The two tables answer different questions and neither is a superset of the other: referrers tell you who linked to you, attribution tells you which campaign worked. Both are cataloged in the list metrics reference.
One more exclusion, so a number is never counted twice: clicks tagged utm_campaign=leed and utm_medium=email are dropped from attribution entirely. Email-tracked links have their own reporting and would otherwise show up as a channel as well.
The hidden UTMs Leed stamps for you
Leed attaches UTM context to its own links without putting parameters in the URL. The context rides on a data-reason attribute, which the click tracker reads and records as the UTM columns above. The visitor sees a clean address; you still get the attribution.
Several features do this automatically. You do not add these, and you should not try to reproduce them by hand:
| Feature | utm_campaign | utm_source | utm_medium | utm_term | utm_content |
|---|---|---|---|---|---|
| Menu links | leed | menu | menu name | menu item id | item name |
| Autolinks | leed | internal | autolink | — | matched text |
| Dynamic CTAs | leed | internal | cta | CTA id | link text |
| Recommendation cards | leed | internal | recommendation | page id | link text |
| Docs header logo and CTA | leed | menu | header | — | — |
| Docs footer logo | leed | menu | footer | — | — |
utm_campaign=leed is the standard value that buckets every internal interaction into one campaign, so you can separate “traffic from my own site furniture” from “traffic from a real campaign” in one filter. utm_medium is what distinguishes the feature.
For links in templates you write yourself — hero buttons, inline CTAs, promotional blocks, custom navigation — you add the attribute by hand, in the same field order:
<!-- Right: the UTM context rides on the attribute -->
<a href="pageid:8f2c1e04-1a3b-4a5c-9d21-77b6c0f0a1e2"
data-id="b2c3d4e5-f6a7-8901-bcde-f12345678901"
data-reason="utm_campaign=leed&utm_source=internal&utm_medium=hero&utm_term=get-started">
Get started
</a>
<!-- Wrong: query parameters on an internal link -->
<a href="pageid:8f2c1e04-1a3b-4a5c-9d21-77b6c0f0a1e2?utm_source=internal&utm_medium=hero">
Get started
</a>The standing rule for template authors: never put UTM parameters on an internal link URL. Use data-reason. A query string on an internal link is visible to the reader, survives copy-and-paste into places it makes no sense, and can turn your own site into a self-referral. The attribute has none of those problems and produces the same columns.
The data-reason attribute is recorded by the click tracker described in how Leed tracks visitors, and you add it yourself in writing your own partials. How recommendation and CTA clicks in particular get attributed is covered on recommendations and dynamic CTAs.
Reading a channel breakdown
The same attribution data is rendered in two places, at two different granularities.
Sessions by channel on the Know dashboard rolls the table up by utm_source and shows the top five, each with its count and a share bar. It is the “which channels are working” view, and it deliberately throws away campaign, medium and content to stay readable.
Attribution in the editor’s Page Analytics panel shows the full five-column table for one page, five rows at a time, honoring the panel’s filters. It is the “what exactly brought people to this page” view.
If a channel appears in the panel but not in the Know card, the reason is almost always the site-wide click exclusion described above rather than a data problem. Per-page attribution is one block of the Page Analytics panel; the card and the funnel are two of the widgets cataloged in the Know widgets reference.