> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.pivolio.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.pivolio.com/_mcp/server.

# Attribution

A conversion rarely has one cause. Someone clicks a Meta ad, comes back from a
newsletter, searches your brand, and buys. Attribution decides how much of that
purchase each touch gets to claim — and different models give wildly different
answers from the same data.

Every touchpoint is scored on three dimensions: **channel**, **source** and
**campaign**.

## Models

Five models, applied across the `n` touches in a conversion path. The
conversion's own visit is always the **final touch**.

| Model            | Weights                                                                                     |
| ---------------- | ------------------------------------------------------------------------------------------- |
| `first`          | `[1, 0, …, 0]` — all credit to the first touch                                              |
| `last`           | `[0, …, 0, 1]` — all credit to the final touch                                              |
| `linear`         | `1/n` to every touch                                                                        |
| `position_based` | 40 / 20 / 40 — first and last take 40% each, the middle splits 20%. With `n = 2` it's 50/50 |
| `time_decay`     | `2^(-days_before / half_life)`, normalized. Default half-life **7 days**                    |

Time decay is the one worth understanding: a touch seven days before the
conversion carries half the weight of one on the conversion day, fourteen days
carries a quarter, and so on. Shortening the half-life concentrates credit near
the conversion; lengthening it flattens toward linear.

> **Note**
>
> The workspace default is **last-touch with a 30-day window**. It's the model
> most ad platforms report in, so it's the one that reconciles most easily against
> what Meta or Google tells you.

## Attribution window

Touches older than the window don't participate. The default is **30 days**, and
the accepted range is **1–365**. A value outside it is rejected with a `422`,
not silently adjusted.

Pick a window that matches your sales cycle. Too short and you drop the
awareness touches that started the journey; too long and unrelated visits from
months ago dilute credit for the campaign that actually worked.

## Channel derivation

Before credit can be assigned, each touch needs a channel. It's resolved by
strict precedence — the first rule that produces an answer wins:

#### Click ID

A recognized click ID names its platform's channel outright. `gclid` →
`paid_search`, `fbclid` → `paid_social`, and so on. This is the most
trustworthy signal available, so it beats everything below.

#### UTM medium

If there's no click ID, `utm_medium` is mapped (see below). You told us what
this traffic is, so we believe you.

#### Referrer inference

With neither, the referrer host is matched against **13 search domains** and
**20 social domains**. Same registrable domain as the current page, or no
referrer at all, means **direct**. Any other external host is **referral**.

#### None

Events with no page context at all — most server-side events — get no
channel rather than a guessed one.

### UTM medium map

| `utm_medium`                   | Channel          |
| ------------------------------ | ---------------- |
| `cpc`, `ppc`, `paid_search`    | `paid_search`    |
| `cpm`, `paid_social`, `social` | `paid_social`    |
| `email`                        | `email`          |
| `organic`                      | `organic_search` |
| `referral`                     | `referral`       |
| anything else                  | `other`          |

An unrecognized medium becomes `other` — it isn't dropped, but it also won't
roll up with the channel you probably meant. Stick to the values above.

## Email is never inferred

> **Warning**
>
> **Email traffic is never inferred from a referrer.** If you don't tag your links
> with `utm_medium=email`, email traffic will not appear as email — it lands in
> `referral` or `direct` instead, and your newsletter looks like it drives nothing.

The reason is that webmail collapses. A click from `mail.google.com` shares a
registrable domain with Google search, so referrer-based inference cannot tell a
newsletter click from an organic search click. Rather than guess wrong at scale,
the pipeline refuses to guess at all.

The fix is one query string:

```
https://example.com/sale?utm_source=newsletter&utm_medium=email&utm_campaign=spring
```

Many email clients also strip referrers entirely, which lands untagged clicks in
`direct`. Tagging is the only reliable answer.

#### [Identity resolution](/documentation/core-concepts/identity-resolution)

How touchpoints get joined to one person.

#### [Events](/documentation/core-concepts/events)

Which events earn attribution credit.