Your analytics can't see 80% of the economy
Pixels were built for sales that finish in a browser. Most sales don't. Here's the method that works on exports you already have — and why the tools you're paying for structurally cannot do it.
Roughly four out of five retail dollars in the United States are still spent in a shop rather than a browser. That is not a nostalgic statistic about high streets — it is a statement about the measurement stack most marketing teams are using, which was designed around the fifth that isn't.
And retail is the easy case. Once you leave it — a roofing quote, a dental implant consult, an equipment lease, a legal retainer, a fleet contract — the proportion of revenue that finishes in a browser rounds to nothing. The click starts a conversation. The money arrives weeks later, through a phone call, a site visit, or a signature on a kitchen table.
If your sale finishes on a phone call, your analytics stack is not measuring your business. It is measuring the first thirty seconds of it.
What attribution without pixels actually means
Attribution without pixels means matching a *person* to a sale rather than matching a browser session to a sale. You reconcile a closed-sales export against a lead or call export using phone numbers and email addresses, so revenue is credited to the channel that produced the customer — regardless of how long the sale took or which device they used.
That is the whole idea. It is unglamorous, and it is the reason it works: a phone number does not clear itself when someone switches from their phone to their laptop, opens a private window, or takes six weeks to decide.
The trade is real and worth stating up front. You lose the granularity of session-level analytics — you will not get scroll depth or a path through the site. You gain the only number the finance director cares about, which is how much money each channel actually produced.
Why the browser stopped being a reliable witness
Even for sales that do finish online, the cookie-based attribution window has been shrinking for years. Safari caps JavaScript-set first-party cookies at seven days, and at 24 hours when the visitor arrived through a decorated ad link — so a sales cycle longer than a week is already partly invisible.
Read that against a typical considered purchase. A homeowner researching a £12,000 job does not decide in seven days. A practice manager choosing a supplier does not decide in 24 hours. The measurement window closed before the decision was made, and what the report shows is not "this channel produced nothing" — it is "we stopped watching".
The industry response has been to build increasingly elaborate machinery to reconstruct what the browser forgot: server-side tagging, conversion modelling, probabilistic matching. Some of it is genuinely clever. All of it is an attempt to rebuild a signal that a stable identifier would have preserved for free.
The three standard fixes, and what each one actually solves
Longer attribution windows, offline conversion uploads and call tracking each solve a real problem — but none of them solves the problem of knowing which channel produced revenue. Here is what each one is genuinely for.
| Approach | What it genuinely gives you | What it cannot tell you |
|---|---|---|
| Longer attribution windows | A few more days of cookie-based credit for short cycles | Anything after the cookie expires, or across devices — the ceiling is the browser's, not yours |
| Offline conversion upload | Better bidding: the ad platform learns which leads closed | An independent number. The platform being measured is doing the measuring and reporting on itself |
| Call tracking with dynamic numbers | Which channel produced a phone call | Whether that call produced money — unless you also reconcile the outcome, which is the actual problem |
| Last-click in the ad platform | A fast, free, directionally useful signal for short cycles | Any sale that closed outside the window, offline, or through a different device |
| Reconciling two exports (this method) | Revenue by channel from your own closed-sales data, retroactively | Session-level behaviour: scroll depth, on-site paths, micro-conversions |
I want to be fair to offline conversion upload, because it gets unfairly dismissed and it is genuinely valuable. Feeding closed-won data back to Google or Meta materially improves bidding — the algorithm starts optimising toward revenue instead of form fills, and that is worth doing. But it is an optimisation input, not an audit trail. When the platform reports on the performance of the platform, you have a vendor's self-assessment, and no CFO has ever been persuaded by one.
Optimisation and measurement are different jobs. Let the ad platform optimise. Do not let it mark its own homework.
The method: reconcile two exports you already have
Export your closed sales, export your leads or calls, and match them on phone number and email. Revenue follows the person, so a March enquiry that signs in June is credited to March's campaign.
The data needed to answer this question almost always exists already. It is simply sitting in two systems that have never spoken to each other.
- The closed-sales export. One row per sale. A customer phone number or email, the amount, and the date it closed. This comes out of a CRM, a practice-management system, a POS, a job-management tool, or a spreadsheet somebody maintains by hand. Any column layout works — the columns get mapped once.
- The lead or source export. The same people, tagged with the channel that produced them. From a call tracker, an ad platform's lead-form download, a booking system, or a web form's submission log.
- Normalise the identifiers on both sides. This is the step that decides whether the whole exercise succeeds. More on it below, because it is where most attempts quietly fail.
- Match, and rank the confidence. An exact normalised phone match is strong evidence. An email domain match is not. Treat them differently and say which is which.
- Report what matched, and label what didn't. The unmatched revenue is a real number that belongs on the page.
Normalisation is where this succeeds or fails
Every system stores a phone number differently, and each of them is confident it is doing it correctly. One writes (415) 555-0142. Another writes +14155550142. A third writes 4155550142 with a leading 1 and no country code, because the person who built the form in 2011 lived in one country and assumed everyone else did too.
Email is worse, because it looks tidy and is not. Plus-addressing, dots that Gmail ignores and most providers do not, personal addresses on one side and work addresses on the other, and the perennial classic of a shared inbox — info@ — appearing against forty different customers.
- Normalise every phone number to E.164 before comparing anything. Use a real library with per-country rules rather than a regular expression; the edge cases are not edge cases, they are Tuesdays.
- Canonicalise email: lowercase, strip plus-addressing, and be explicit about whether you are stripping dots — the correct answer depends on the provider, and guessing wrong in either direction costs matches.
- Treat shared inboxes as unmatchable rather than as one enormous customer. A single info@ address matched to forty sales is not a match, it is a bug with good manners.
- Keep the raw value alongside the normalised one. When somebody disputes a match, the raw value is the evidence.
Skip normalisation and your match rate collapses to something like 20%, which looks like the method failing rather than the data being untidy. It is the single most common reason a first attempt at this gets abandoned.
What to do with the revenue that doesn't match
Some sales will not tie to any source — usually between a fifth and a half. That revenue belongs in a clearly labelled unattributed bucket, sized accurately and never redistributed across the channels that did match.
This is the part where attribution tools quietly become fiction, and it is worth being blunt about it. Spreading unmatched revenue proportionally across the known channels is mathematically tidy, invisible to the reader, and always flattering to the tool doing it. Every channel is inflated by the same factor. Nobody can tell which part of any number was measured and which was assumed.
If a report does not show you its unattributed bucket, the correct question is not "how accurate is this?" It is "where did that revenue go?"
A practice that knows 22% of its revenue arrives by word of mouth is in a far stronger position than one shown 4% unattributed because the remainder was allocated away. The first can act. The second is being managed by a spreadsheet with opinions.
The test that matters: can you defend a single number?
The practical test of any attribution report is whether you can take one channel's figure and show the individual sales behind it. If any part of that figure came from an assumption, the number will not survive a finance review.
This is not a theoretical standard. It is the actual conversation that happens when a marketing director asks for another £30,000 a month and a CFO asks where the last £30,000 went. "The platform says 4.2x" is not an answer that survives the follow-up question. "Here are the 68 sales, here are the leads they came from, here is why we judged them the same person" is.
- Every matched sale should be inspectable: which lead, which channel, and on what basis.
- Match confidence should be visible and reversible. A human overriding a weak match is a feature, not an admission.
- The unattributed bucket should be a line on the report, not a footnote.
- The whole thing should be reproducible from the two source files, by somebody who does not trust you.
An uncomfortable opinion about dashboards
Most attribution dashboards are optimised to look authoritative rather than to be checkable, and the two goals are in genuine conflict.
Confidence intervals are ugly. Unattributed buckets are ugly. "We could not determine a source for 31% of this revenue" is an ugly sentence to put on a slide. So tools smooth them away, and the resulting dashboard is more persuasive and less true.
I would rather hand somebody a report with a visible 31% hole in it than a seamless one they cannot interrogate. The hole is information: it tells you which export to widen next. The seamless version tells you nothing, and it fails at exactly the moment it matters most, which is the first time somebody senior asks a hard question in a room full of people.
A number you cannot defend is not a number. It is a decoration with a decimal point.
Does this give you multi-touch attribution? Not really, and that is fine
Reconciling exports gives you single-source credit: the channel that produced the customer gets the revenue. It does not model every touch along the way. For most businesses closing offline, that is the correct trade — because a multi-touch model built on a broken signal is a confident answer to a question the data cannot support.
Multi-touch attribution is genuinely useful when you can observe the touches. In a browser-completed funnel with a short cycle, you can. In a business where the second touch was a phone call and the third was a site visit, the model is being asked to weight interactions nobody recorded — so it weights the ones that were recorded, which are the digital ones, which is exactly the bias you were trying to correct.
So the honest hierarchy: know which channel produced the customer and what they were worth. Add touch weighting later, when you have a real record of the touches. Reversing that order produces a beautiful model resting on a guess, and beautiful models resting on guesses are how six-figure budgets end up in the wrong place for a year.
How to try this on your own data this week
You do not need to install anything or change how anyone works. Export two files, normalise the identifiers, match, and look at the unattributed bucket before you look at anything else.
- Export closed sales for the last complete quarter, with phone, email, amount and close date.
- Export leads or calls for the same period *plus* the three months before it, so long-cycle deals have something to match against.
- Normalise phones to E.164 and canonicalise emails on both sides.
- Match on phone first, then email. Count exact matches separately from fuzzy ones.
- Look at the unmatched revenue. Ask which system it probably came from. That answer is usually worth more than the matched half on the first pass.
The first run is rarely flattering, and that is the point. Most teams discover that a channel they had been defunding was producing the largest deals slowly, and a channel they had been celebrating was producing volume that never closed. Both of those are expensive things to learn late.
CloseRev does exactly this: two exports in, revenue by channel out. Unmatched revenue is shown as its own line and never redistributed, every match is auditable and reversible, and it works on months that have already closed — because nothing had to be installed before they happened.
Questions people actually ask
- Can you attribute revenue to marketing channels without tracking pixels?
- Yes. Instead of matching a browser session to a sale, you match a person to a sale — reconciling a closed-sales export against a lead or call export on phone number and email. Because a phone number does not expire the way a cookie does, this works on sales that closed weeks or months after the click, and on months that have already ended.
- Why don't tracking pixels work for phone or in-person sales?
- A pixel records a browser event. If the sale finishes on a call, at a site visit or on a signature, there is no browser event to record. The pixel can tell you a lead form was submitted; it cannot tell you whether that lead became £40,000 of revenue or nothing at all.
- What data do I need to attribute offline revenue?
- Two exports. A closed-sales file with one row per sale carrying a customer phone number or email and the amount, and a lead or call file with the same people tagged by the channel that produced them. Both usually already exist in a CRM, a practice-management system, a call tracker or an ad platform's lead export.
- How long can offline attribution look back?
- As far back as your exports go. Because matching is on a stable identifier rather than a browser session, a lead from March that closes in June is still credited to March — and a quarter that already closed can be analysed today, with nothing installed at the time.
- What is a realistic match rate for offline attribution?
- Typically 50–80% of revenue matches to a known source, depending on how completely the lead side is exported and how consistently identifiers are recorded. The rest belongs in an honest unattributed bucket rather than being spread across the channels that did match.
- Is offline conversion upload to Google or Meta the same thing?
- No. Offline conversion upload sends your closed-won data to the ad platform, which then optimises and reports on itself. It is useful for bidding and useless as an independent answer, because the party being measured is doing the measuring.