Report on marketing attribution

Follow one click through to revenue, and understand exactly what the number does and does not mean.

Owned by Admin · 10 steps · about 18 minutes

Why this exists

Attribution here is deliberately simple, and understanding the simplifications is the difference between a useful report and a misleading one.

Every post gets a tracked short link per platform. The public redirect records the click, flags whether this visitor has clicked this link before, sets two cookies — a long-lived visitor id and a short-lived attribution id — and sends the person on to the tagged destination. When an order is later paid, the payments module hands the attribution cookie to marketing, and if the click is recent enough a conversion row is written. CORRECTED 2026-08-30: that last handshake is not wired. The cookie is set on every click and read by nothing — payments does not import this module, and the only callers of attribute_order are the seed, the debug simulate-conversion endpoint and the test suite. So outside a seeded database there are no conversions at all, and every orders / revenue / conversion-rate figure in the reports below is structurally zero. Plan 647 wires it; read the rest of this workflow knowing which half runs.

Four rules follow, and each one is a caveat you should say out loud when you present the numbers. Last click wins, because the redirect overwrites the cookie. The window is finite — a click older than the configured number of days attributes nothing. One conversion per order, first write wins, enforced by a unique constraint. And revenue is a gross snapshot taken at attribution time: a later refund does not remove the conversion or reduce the figure.

Attribution is also never allowed to break a purchase. The attribution call cannot raise — an unknown click, an expired window, a duplicate order, even a missing table all return quietly false. Nobody's checkout has ever failed because a marketing cookie was odd, and that is a deliberate ordering of priorities.

The reports are pure read-time SQL over those rows. There are no denormalised counters anywhere, so there is nothing to drift and nothing to rebuild — a report is always a straight answer about what is in the tables right now.

Before you start

  • An admin session for the debug forensics; a venue manager can read the same reports.
  • At least one published post with short links (the seed ships one with simulated clicks).
  • An honest willingness to quote the caveats along with the conversion rate.

Practise with

PersonaEmailPasswordNote
adminadmin@club.test admin123reads the reports and the per-link forensics
venue_managermanager@club.test manager123reads the same reports; the debug forensics are admin-only

Steps 1 — Admin

their manual →
  1. 1
    List the tracked links and pick one to follow: note its short code, the post and platform it belongs to, and its campaign tags.
    Expected result Links with their short codes, targets and UTM parameters.
    Watch out for A link is stable per post and platform. Retrying a post reuses the same link, which is why retries do not fragment your reporting.

Steps 2 — Customer (Guest)

their manual →

The click that starts the whole chain is made by a stranger with no account at all.

  1. 2
    Open the short link the way a stranger would, from a phone, with no account and no session.
    /l/{short_code} marketing
    Expected result A redirect to the tagged destination, a click recorded, and two cookies set.
    Watch out for This is the only public route in the whole workflow, and it works with no login by design — a marketing link that demanded a session would convert nobody. An unknown code is a 404 that records nothing and sets no cookies.

Steps 3–8 — Admin

their manual →
  1. 3
    Look at that single link's forensics: the link row, every click on it, and every conversion attributed to it.
    /debug/marketing/links/{short_code} marketing
    Expected result The whole chain for one link in one response.
    Watch out for Admin-only. This is the tool for 'why did this campaign report nothing'. CORRECTED 2026-08-30: it used to say that clicks with no conversions is a landing page problem and not a tracking one. Until plan 647 lands it is ALWAYS a tracking problem — nothing reads the attribution cookie, so no purchase can ever produce a conversion. Read clicks-with-no-conversions as the known gap first, and only look at the landing page once the chain is wired.
  2. 4
    Open the channel report and read it per platform: posts published, links, clicks, unique clicks, orders, revenue and conversion rate, plus totals.
    Expected result One row per platform, filterable by event and date range.
    Watch out for Unique clicks are per visitor per link. The gap between clicks and unique clicks is people re-opening the same link, not new reach.
  3. 5
    Pull the same report as JSON, or ask for CSV when somebody wants it in a spreadsheet.
    Expected result The per-platform figures and totals in the format you asked for.
    Watch out for A scoped host may call this only for their own event id. If you are building a shared report, remember that the same endpoint answers differently depending on who calls it.
  4. 6
    Switch from channels to one event's funnel, broken down per rule.
    /api/marketing/reports/events/{event_id} marketing
    Expected result The funnel from clicks through to attributed orders and revenue, plus which rules produced which.
    Watch out for Rule-driven posts and hand-written posts appear side by side. A rule that produced clicks but no orders is worth more attention than one that produced neither.
  5. 7
    In training, simulate clicks on a link so the report has something in it, giving a visitor id and a count.
    Expected result The created click ids.
    Watch out for Simulated clicks are indistinguishable from real ones in the reports. Do this on a demo database only, or you will be explaining your own traffic to somebody next quarter.
  6. 8
    Attribute one of those clicks to an order through the real attribution path, with a revenue figure.
    Expected result A written flag and, when it is false, the reason: unknown click, expired window, duplicate order.
    Watch out for The reason codes are the fastest way to learn the rules. Try it with a click older than the window and read what it tells you, rather than trusting this page.

Steps 9 — Venue Manager

their manual →

They own the campaign and read the same report — without the debug tooling.

  1. 9
    As the manager who ran the campaign, read the same report — and notice you get the numbers without the debug tooling.
    Expected result The identical report page.
    Watch out for Every /debug route in this workflow is 404 or 403 for you. If a number looks wrong, the escalation is to an admin, and this page is the evidence you bring.

Steps 10 — Admin

their manual →
  1. 10
    Before you present anything, say the caveats out loud: last click wins, the window is finite, one conversion per order, and revenue is a gross figure that refunds never reduce — and, until plan 647 lands, the biggest one of all: no real purchase is attributed at all, so a non-zero conversion figure on this platform came from the seed or from a debug simulation.
    Expected result A report you can defend rather than one you have to withdraw.
    Watch out for The commonest mistake is comparing attributed revenue to actual takings. They are different numbers measuring different things, and the gap is refunds, unattributed sales and the attribution window — not an error.