How a visit's channel is decided
Channel is not a stored column in AdPix. It is computed the moment you open a report, from source, medium, campaign and click-id type. Eighteen default rules are checked in order, the first match wins, and anything that matches nothing stays Unassigned.
Where a channel comes from#
Every visit reaches AdPix carrying four things: source, medium, campaign and click-id type. They are extracted at collection time from the landing URL and the referrer, and then stored.
The order of extraction is fixed: click id first, then the utm_* parameters, then the referrer, and if none of those exist the visit is direct.
| If the URL or referrer carries | Source and medium become |
|---|---|
| one of the recognised click ids | the ad network as source, cpc as medium |
utm_source and utm_medium |
exactly those values, lower-cased and whitespace-collapsed |
| a referral from a search engine | the host, medium organic |
| a referral from a social network | the host, medium social |
| any other referral | the host, medium referral |
| none of the above | (direct) and (none) |
The click-id parameters AdPix recognises in a landing URL are gclid, wbraid, gbraid, fbclid, msclkid, ttclid and li_fat_id.
Channel, though, is settled at none of those steps. It is computed from those four values at the moment you open the report.
The rules are ordered and the first match wins#
The ruleset is an ordered list. For each visit the rules are evaluated from lowest priority number upward, and the first rule that matches decides the channel; the rest are never tested. If nothing matches, the channel stays Unassigned.
These are the eighteen platform defaults:
| Priority | Channel | Group | Matches when |
|---|---|---|---|
| 1 | Direct | Direct | source is (direct) or empty, and medium is (none), (not set) or empty |
| 2 | Cross-network | Paid | campaign contains cross-network, or click id is wbraid or gbraid |
| 3 | Paid Shopping | Paid | source is in the shopping category or the campaign matches shop, and a paid signal |
| 4 | Paid Search | Paid | source is in the search category, and a paid signal or click id gclid / msclkid / yclid |
| 5 | Paid Social | Paid | source is in the social category, and a paid signal or one of the social click ids |
| 6 | Paid Video | Paid | source is in the video category, and a paid signal |
| 7 | Display | Paid | medium is display / banner / expandable / interstitial / cpm, or click id dclid |
| 8 | Paid Other | Paid | a paid signal alone |
| 9 | Organic Shopping | Organic | source is in the shopping category, or the campaign matches shop |
| 10 | Organic Social | Organic | source is in the social category, or medium is social / social-network / social-media / sm |
| 11 | Organic Video | Organic | source is in the video category, or medium contains video |
| 12 | Organic Search | Organic | source is in the search category, or medium is organic |
| 13 | Referral | Earned | medium is referral / app / link |
| 14 | Owned | source is email, or medium is email / e-mail / e_mail / newsletter |
|
| 15 | Affiliate | Earned | medium is affiliate |
| 16 | Audio | Earned | medium is audio |
| 17 | SMS | Owned | source or medium is sms |
| 18 | Push | Owned | medium ends with push, or contains mobile or notification |
The conditions above are exactly as the rules are written, but several click ids they mention — dclid and yclid among them — are not extracted anywhere in the collection path. The real recognised set is the seven listed above; for every other network, rely on utm_medium rather than a click id.
Order is what produces the behaviour. That is why all the paid rules come before all the organic ones: a click from Google with medium cpc matches Paid Search and never reaches Organic Search, because rule 4 is tested before rule 12.
The paid signal#
Several rules lean on a "paid signal", and it is exactly one thing: the medium value. A medium is paid if any of these hold:
- It contains
cpanywhere — which catchescpc,cpm,cpvandcpa, and because it is a contains test rather than a prefix,social-cpcanddisplay-cpmare paid too. - It starts with
paid—paid-social,paid-video,paidsearch. - It is exactly
ppcor exactlyretargeting— those two are full matches, not prefixes, soppc-brandproduces no paid signal.
The practical consequence is that a paid ad on a social network, without a paid medium, reads as organic in your reports. If your paid campaigns keep landing under Organic Social, look at the ad's destination URL; almost always utm_medium is either missing or set to something like social, which is not a paid value.
Source categories#
Rules 3 to 6 and 9 to 12 look at the source's category rather than its name. The categories are a registered list:
| Category | Default sources |
|---|---|
| search | google, bing, yahoo, duckduckgo, baidu, yandex, ecosia, brave |
| social | facebook, instagram, x, twitter, linkedin, tiktok, pinterest, reddit, telegram, threads |
| shopping | amazon, ebay, etsy, walmart, digikala |
| video | youtube, vimeo, twitch, aparat |
| ad network | taboola, outbrain, criteo, yektanet, tapsell |
The match is exact, not a substring. utm_source=google falls in the search category; utm_source=google-ads does not. If your ad platform sends a different name, normalise it in your campaign tagging.
Attribution is computed on read#
This is the most important and most surprising behaviour in this part of the product: the channel is never written into the event to be read back later. Acquisition reports compile your effective ruleset into query conditions and build the channel there, every time.
Three consequences follow directly:
- Editing a rule reclassifies history instantly. No reprocessing, no queue, no waiting. Save the rule and the next report query reads years of past traffic through it.
- No data moves. The events are untouched; only their interpretation changes. So trying a rule carries no risk — if it is wrong, put it back.
- A misclassification is repaired just as fast. In one period of the product, two internal paths blanked source and medium when demoting a referral; that pair matched no rule and roughly forty percent of sessions fell into
Unassigned. The fix was to widen the Direct rule to also accept an empty source — and that single change read about a million already-written rows correctly, on the spot, with no data mutation at all.
Genuinely direct traffic carries the (direct) and (none) pair and matches rule 1. Unassigned means a visit for which no rule existed. If your Unassigned share is noticeable, the problem is campaign tagging or a gap in the rules, not direct traffic. Everything says direct covers that case.
The coarse group — paid versus organic#
Besides a channel name, every rule carries a group: Paid, Organic, Owned, Earned, Direct or Unassigned. That is the rollup behind the one-click paid-versus-organic view, and like the channel itself it is computed on read.
The default split is in the Group column of the table above: the six paid channels plus Cross-network under Paid, the four organic ones under Organic, email, SMS and push under Owned, and referral, affiliate and audio under Earned.
Scopes, and who edits them today#
The data model carries four rule scopes: platform, organization, account and property. Property rules are evaluated before account rules, those before organization rules, and all of them before the platform defaults; within a scope, lower priority first.
In today's console, though, there is one editor and it is global and super-admin only: the Channel management screen, reached from Admin → Global settings → the Global data & channels card → Channel definitions (platform defaults). The screen says so itself: order matters, and changes reclassify history instantly. A rule created there is written at platform scope, so it applies to every tenant.
A business user sees the result across every acquisition report but does not open that screen. If you need a rule the defaults do not cover, raise it through support — and, as above, the correction lands across your whole history the moment it is saved.
Where you see this number, and where you do not#
| Surface | Where the channel comes from |
|---|---|
| Acquisition overview, Traffic acquisition, and their exports | the ruleset, at read time |
| The paid-versus-organic view | the ruleset, at read time |
| The ROAS-by-channel table on the cost page | the channel guessed at collection time |
The ft_channel dimension in Explore, the heatmap channel filter, the realtime visitor feed |
the channel guessed at collection time |
Know that difference so two screens showing two numbers does not send you hunting. The acquisition reports are the reference; the others show the rough label stamped at collection, which has never seen your ruleset.
One small but frequent detail: a referral from an Android app carries a package name, not a web host. AdPix classifies those before any host matching, which is why a click out of the Gmail app counts as Email and not organic search — even though the package name looks like a Google domain.
Frequently asked questions#
Why is some of my traffic Unassigned?
Because that traffic's source and medium pair matched no rule. The usual cause is an unconventional utm_medium that appears on no list. Drill into the row in an acquisition report, see what source and medium actually hold, then either fix the campaign tagging or add a rule for that value.
If I change a rule, do I have to reprocess data?
No. Channel is computed on read, so an edit reclassifies history on the very next query. No event is rewritten and no background job runs.
Where exactly does the paid signal come from?
From utm_medium, and from nothing else. A medium counts as paid if it contains cp anywhere (cpc, cpm, cpv, cpa, and anything else with cp in it), or starts with paid, or is exactly ppc or retargeting. The source being an ad platform is not enough on its own — without a paid medium the click reads as organic.
Who can edit the rules?
Today the rule editor in the console is one global, super-admin-only screen, and rules created there are written at platform scope, so they apply to every tenant. A business user sees the result in every acquisition report but does not change the rules from the console.
Thanks — your feedback helps us improve the docs.