Traffic sources
The platform you buy from, and the macro configuration that decides whether your sub-ids and your cost are real or empty.
A traffic source is the platform you buy clicks from. It exists in UTMCAP for one reason: it is what turns a bare tracking URL into one carrying the platform's own identifiers.
Set it up wrong and nothing looks broken. Clicks arrive, campaigns route, reports fill in. The sub-id columns are just empty and every click costs zero, and both are the kind of wrong nobody checks until a month has gone by. That is why "has macros configured" is a column on the sources list rather than something you find out by opening one.
#The form
Four tabs:
- Main — the template, name and group, the source's cost, and tags and notes. Picking a template fills in everything below it for that platform.
- Parameters — the macros: which of the platform's placeholders lands in which sub slot, and the string they add to the campaign URL, with a Copy button.
- Postback — where UTMCAP calls the platform back when a click converts.
- Integration — the platform's own reporting: real spend and conversions sent back.
#Macros
A macro is the platform's own placeholder. You write it into the source's configuration; the platform substitutes it when it sends the click.
The Parameters tab has one row per value, in four columns:
- Slot — where the value is kept,
sub1tosub10. Reports group by it. - Parameter — the name it has in the campaign URL. Left empty, it is the
macro's own name:
{zone_id}rides aszone_id=. - Placeholder — the platform's macro, spelled exactly as the platform spells it.
- What it is — your label for it. Reports and columns show this name.
The PropellerAds template, as it fills the tab:
| Slot | Parameter | Placeholder | What it is |
|---|---|---|---|
sub1 |
src_campaign_id |
{campaign_id} |
Campaign ID |
sub2 |
zone_id |
{zone_id} |
Zone ID |
sub3 |
subzone_id |
{subzone_id} |
Subzone ID |
sub4 |
bannerid |
{bannerid} |
Banner ID |
sub5 |
src_country |
{country} |
Country |
sub6 |
src_os |
{os} |
OS |
sub7 |
device |
{device} |
Device |
sub8 |
zone_type |
{zone_type} |
Zone Type |
So the campaign URL reads ?zone_id={zone_id}&subzone_id={subzone_id}…, in
the platform's own words, not ?sub2=…. A name that is also one of UTMCAP's
own tokens gets src_ in front — src_campaign_id is the platform's campaign,
{campaign_id} stays yours.
Two values are not in this table: the platform's click id has its own field, External ID, and so does Cost.
Each template fills the slots its own way. sub2 is the zone on
PropellerAds and the ad group on Google Ads. So compare sources by the name in
What it is, not by slot number, or group a report by source first.
The exact macro text comes from the platform's own documentation — every one spells them differently, and the spelling has to be exact. A macro that is almost right is not substituted; it arrives as literal text, or as nothing.
Push.House: mind the case. Its macros are short and lowercase —
{camp}is the campaign,{site}the zone,{price}the cost and{click_id}its click id. In the notification's own title and text Push.House wants{CITY}and{COUNTRY}in capitals, but in the link every token is lowercase:{city}and{country}. Copy the capitals into the link and every click arrives with the literal text{COUNTRY}. The Push.House template has the link side filled in correctly.
Once configured, the tracking URL on every campaign using that source is built with those macros already in it. Take the URL from the campaign's Tracking tab. Editing a URL you already have is how a source ends up half-configured.
#Cost
On the Main tab, in one row. Two of the settings are a pair, and they are not the same thing:
- Cost macro — what the platform substitutes with the click's cost. What it writes.
- Cost parameter — the query parameter UTMCAP reads that value from. What we read.
Both have to be set, and they have to agree. Together they are how cost arrives on a click; the string they add to the campaign URL is shown under the row, with a Copy button.
A source with no cost macro records every click at zero. The campaign then shows revenue against no spend, which reads as pure margin — profitable, scalable, and completely wrong. It is the failure mode worth checking first on any campaign whose ROI looks too good. Google and Facebook pass no cost macro at all — for those, sync the real spend instead.
Cost model records how the platform bills you — CPC, CPM, and so on — and is the default a new campaign starts with.
Traffic loss is the share of paid clicks that never arrive — the visitor closed the page before the redirect finished. Cost is divided by (1 − loss), so 10% makes a $0.10 click cost $0.111.
#Postback
The Postback tab is where UTMCAP tells the platform a click converted, so its optimiser learns which traffic pays. It is called when a conversion is approved.
Paste the postback URL the platform gives you, with the platform's own click id
placeholder replaced by {external_id}. UTMCAP fills {external_id} with the
platform's id for that click. {payout} is the conversion's payout and
{status} its state.
That id has a field of its own, on the Parameters tab, next to Cost:
| Parameter | Placeholder | |
|---|---|---|
| Cost | cost |
{cost} |
| External ID | src_clid |
${SUBID} |
These two are the values UTMCAP must have from a traffic source, so neither spends one of the ten sub slots. Cost is what you paid. External ID is what lets the platform's optimiser learn which of its clicks paid off — which is most of the reason to connect an ad account at all.
Picking a template fills both in. Nine platforms — Meta, Taboola, Revcontent, TikTok and five more — add their click id to the landing address themselves, so there is no placeholder to write; those get the platform's own parameter name and an empty Placeholder, which is correct.
A source with External ID empty sends UTMCAP's own id instead. The platform has never seen it and cannot count it.
Older sources kept their click id in a sub slot, and those still work: UTMCAP reads the field first and falls back to the slot. That matters for links already running in the platform, which still send the id the old way, and for clicks already recorded, which keep converting for weeks.
For example, PropellerAds: in its dashboard, Tracking → Other tracker or CPA network gives a URL like
http://ad.propellerads.com/conversion.php?aid=…&pid=&tid=…&visitor_id=${SUBID}
Paste it with ${SUBID} replaced by {external_id}, and add
&payout={payout}. The PropellerAds template already fills External ID in
for you: parameter src_clid, placeholder ${SUBID}.
Google Ads, Microsoft Advertising and Meta do not take a postback. Their conversions go through the Integration tab.
#Integration: real spend and conversions
The Integration tab connects the source to the platform's own reporting. Two things come of it, each with its own switch.
First, the account. Google Ads account (or the platform's) lists the ad accounts added under Tools → Integrations, by the name you gave them. For Google, Microsoft and Meta, Account to use then lists the ad accounts that login can reach — pick one rather than typing its IDs. On Google, managers and suspended or cancelled accounts cannot be chosen; on Microsoft, only active accounts can; on Meta, disabled and closed accounts cannot. Currency is the one the ad account bills in.
#Sync cost
On, the last three days of spend are pulled from the platform every three hours, per campaign per day. From then on your reports and ROI use that spend rather than the cost recorded on each click, shared across that day's clicks. Spend in another currency is converted into your account's currency at the day's rate.
Microsoft builds a report when asked rather than answering at once, so a Microsoft sync — or Test — can take up to two minutes. If Microsoft is still preparing it after that, the sync says so and the next one asks again.
Platforms that hand out an API key rather than a sign-in — PropellerAds, Adsterra, NewsBreak, Taboola, Outbrain, MGID and others — take it here, on the source itself: choose the platform, paste the key, press Test. PropellerAds' key is under Profile → API in its dashboard, Adsterra's under Settings → API, and NewsBreak's under Resources → API Access Tokens in Ad Manager. NewsBreak reports at most 30 days by day in one call, so a longer range by hand is refused with NewsBreak's reason.
TrafficJunky's token is under My profile → Connect with TrafficJunky API. TrafficJunky totals a range rather than splitting it by day, so it is asked one day at a time — a sync covers at most 31 days, and a Test of seven days is seven calls.
Off, nothing is pulled on a schedule. Test and Sync last 7 days still work by hand.
#Send conversions to Google Ads
On, each conversion recorded for a Google click is sent to Google Ads within
seconds of the postback, matched on the click's gclid, so Google's bidding
learns from sales it cannot see. An hourly run retries anything that failed,
and when you first switch it on it sends the last 60 days' conversions too —
Google accepts clicks up to 90 days old.
Conversion action is the goal in your Google Ads account the conversions are counted under: its name, its value, and whether Smart Bidding uses it. Google refuses an upload without one, so empty means nothing is sent. Choose one from the list, or press Create one for me — it saves the tab first, so there is nothing to press before it, and with Send conversions on, the action it makes is saved on the spot. Which conversions chooses approved only, or approved and pending.
This needs Google's click ID on each click: the Google Ads template puts
{gclid} in sub10. The tab warns you if a source does not record it. Below
the switch, the last 30 days' uploads are counted — sent, being retried, and
refused with Google's reason.
#Send conversions to Microsoft Advertising
The same as Google's, matched on the click's msclkid: each conversion recorded
for a Microsoft click is sent to Microsoft Advertising within seconds of the
postback, and an hourly run retries anything that failed. Microsoft accepts
conversions up to 90 days old.
Conversion goal is the offline conversion goal in your Microsoft Advertising account the conversions are counted under. Choose one from the list, or press Create one for me, which adds "UTMCAP conversions" — a Purchase goal that counts every conversion, at the value each one carries, within 30 days of the click. Microsoft asks for two hours between creating a goal and sending to it, so sending to a goal made here starts two hours later; nothing is lost, the hourly run sends what waited. Which conversions chooses approved only, or approved and pending.
The Microsoft Ads template puts {msclkid} in sub10 and chooses it as their
click id. The tab warns you if a source does not record it.
#Send conversions to Meta
On a Meta source, each conversion recorded for a Meta click is sent to Meta's
Conversions API within seconds, matched on the click's fbclid, so Meta's
delivery learns from sales it cannot see. An hourly run retries anything that
failed. Meta accepts events up to seven days old, so nothing older is sent.
It needs two values from Meta Events Manager, and no Meta account connection:
- Pixel ID — open the pixel (Meta calls it a dataset), then Settings; the ID is at the top.
- Conversions API token — on the same page, under Conversions API, press Generate access token. It is stored encrypted and never shown again.
Which conversions chooses approved only, or approved and pending. A goal is
sent as Meta's matching standard event — lead as Lead, purchase as
Purchase, registration as CompleteRegistration — and a goal of your own
naming as a custom event of the same name. A network's sale is a Purchase. Each
event carries its value and currency, and the page the visit landed on.
Test event code is for checking the setup: paste the code from the Test Events tab in Events Manager, and the events show there as they arrive. Clear it afterwards.
Meta adds fbclid to the landing address itself, so there is no placeholder
to put in the link. The Meta template sets External ID to the parameter
fbclid with an empty Placeholder, and UTMCAP reads the id from Meta's own
parameter when the visitor arrives. The tab warns you if a source does not
record it.
#Sub-ids for platforms that will not redirect
Google and Microsoft do not allow a redirect, so their clicks arrive by parallel tracking instead. The macros still apply — they go on the ad's tracking template rather than on a redirect URL, and the values land in the same sub slots.
#What the list tells you
Alongside the configuration, each source carries its own performance: Clicks, Unique, Leads, Sales, Rejected, CR, Revenue, Cost, Profit, ROI, EPC.
Note the vocabulary here differs from the report builder, and deliberately:
Leads are conversions still pending approval and Sales are ones the
network approved. Elsewhere those are Pending and Conversions. Same
numbers, named for what a media buyer calls them when comparing sources.
#Reporting on sub slots
Once macros are flowing, sub1 – sub10 are groupings in the
report builder. That is where the
setup pays off: which ad set, which creative, which placement — answered in one
regroup rather than by building ten campaigns.
If a slot is empty in a report, turn off Hide empty rows to confirm it is genuinely arriving blank rather than simply having no data yet.
Tracking tips by email
Practical tips on tracking, attribution and ad costs, from the people who build UTMCAP.
Unsubscribe any time. Privacy policy