Attribute at Capture, Never Infer Later: An Untagged Record Belongs in a Bucket, Not in an Estimate
Attribution reconstructed after the fact from session data, timing or traffic patterns is a model presented as a measurement, and it is confidently wrong in the cases that matter most. Capture the context at the moment the thing happens, store it on the record, and put what arrives without context into an explicit unattributed bucket rather than apportioning it.
Attribute at Capture, Never Infer Later: An Untagged Record Belongs in a Bucket, Not in an Estimate
The short answer. Every system that runs more than one campaign, channel or source eventually has to say which one produced a given outcome. There are two ways to answer and they are not variations of the same approach. Capture the context at the moment the thing happens and store it on the record, which is a measurement. Or reconstruct it afterwards from sessions, timing and traffic, which is a model wearing a measurement's clothes. The second is wrong in a specific and unhelpful direction: it is least reliable exactly where attribution is contested, and it produces a complete-looking report where an honest one would have an unattributed bucket. Capture it, and when there is no context, say so.
The requirement arrives innocuously. Run several campaigns, report on each. Or several channels, several partners, several sources.
The data is already being collected. The outcomes are in a table. Surely the attribution can be worked out from what is there.
It can be approximated from what is there, which is a different thing, and the gap between those two words is where the reporting stops being trustworthy.
The two approaches
Captured. At the moment the outcome occurs, the context that produced it is written onto the record. The campaign identifier, the source, the referrer, whatever determines attribution. It is a field on the row, set once, immutable.
Inferred. Afterwards, a process looks at sessions, timestamps, traffic patterns and whatever else is available, and assigns each outcome to a probable source.
The first answers "what was the context" with a stored fact. The second answers "what was probably the context" with a computation, and then the report presents both kinds of answer in the same column with no indication of which is which.
Why inference fails where it matters
Inference is not uniformly unreliable. It is reliable in the easy cases and unreliable in the contested ones, which is the worst possible distribution.
One obvious source: accurate. Single campaign, direct traffic, clean session. Inference gets it right and adds nothing, because it was never in doubt.
Multiple plausible sources: a guess. Someone saw an email, searched later, clicked an advertisement, came back directly and then acted. Every attribution model produces a different answer and all are defensible. Inference picks one and reports it as a fact.
No usable signal: fabrication. Cleared storage, a privacy-preserving browser, a different device, a long delay. The inference still returns something, because that is what the code does, and the something is a prior dressed as an observation.
The contested and signal-free cases are precisely the ones that determine budget allocation, partner payments and which campaign gets repeated. The method is weakest where the decisions are largest.
Capture is nearly free
The objection to capture is effort, and the effort is small.
The context reaches the system in the request that produces the outcome. It is in the parameters, the referrer, or in state established when the visitor arrived. Reading it and writing it to the record is a field and a line of code.
What makes it feel expensive is that it has to be done at the point of capture, which means it has to be designed before launch rather than added when reporting becomes a question. That is a sequencing problem rather than an engineering one, and it is also why it is so often absent: by the time anybody wants the report, the records exist without the field, and the only remaining option is inference.
Once captured, three good properties follow. The value is immutable, so last quarter's report does not change when the attribution model is revised. It is auditable, because each record says where it came from. And it is honest about absence, because a record with no context has an empty field rather than a guess.
The unattributed bucket
Everything turns on what happens to records that arrive without context, and there is exactly one good answer.
Put them in an explicit bucket called unattributed, report its size, and do not apportion it.
Apportioning, meaning distributing unattributed outcomes across known sources in proportion to their measured share, is the common move and it is circular. It assumes the unattributed records resemble the attributed ones, and the reason they are unattributed is usually that they do not: a different privacy posture, a different device pattern, a different channel. You spread them according to a distribution they are not drawn from, and the resulting report has no uncertainty visible anywhere.
A visible unattributed bucket is more useful than a complete-looking report. If it is 5 percent, the attributed numbers are solid. If it is 40 percent, every comparison between campaigns is noise, and the report should say that rather than hide it inside a tidy pie chart.
One core, many campaigns
A related failure in the same family, which matters because it is how attribution quietly stops being consistent.
When a product runs many campaigns, the tempting implementation is to copy the capture flow per campaign. Each gets exactly what it wants, with no shared abstraction to negotiate.
Then they drift. The attribution field is set in one copy and forgotten in another. A fix lands in the campaign somebody was looking at. Six months later the same outcome is attributed differently depending on which page produced it, and no report can be trusted across campaigns.
One core with campaigns as data keeps the capture identical by construction. The campaign is a row carrying its identifier, its goal and its page. The capture path is written once.
The honest limit
Captured attribution answers which context produced the record. It does not answer what caused the outcome, and those are different questions.
Someone who sees an email, searches a week later and then acts has a captured context of search and a cause that includes the email. Capture gives you an accurate, consistent, auditable record of the final context and does not resolve multi-touch causation, which no method resolves from observational data alone.
That is a reason to be modest about what attribution reports mean, not a reason to prefer inference. An accurate record of the last context plus an honest unattributed bucket is a better foundation for a causal question than a fabricated complete picture.
FAQ
What is the difference between captured and inferred attribution? Captured attribution writes the context onto the record at the moment the outcome occurs, so it is a stored fact. Inferred attribution reconstructs it afterwards from sessions and timing, so it is a model. Reports usually present both in the same column with no indication of which is which.
Why is inferred attribution unreliable where it matters most? Because it is accurate in the easy cases and a guess in the contested ones. Single obvious source, it gets right and adds nothing. Multiple plausible sources, every model gives a different defensible answer. No signal at all, it still returns something, which is a prior dressed as an observation.
What should happen to records with no attribution context? They go in an explicit unattributed bucket whose size is reported. Apportioning them across known sources assumes they resemble the attributed records, and the reason they lack context is usually that they do not, so you distribute them according to a distribution they are not drawn from.
Why is a large unattributed bucket better than a complete report? Because it tells you whether the comparison is meaningful. At 5 percent the attributed numbers are solid. At 40 percent every comparison between sources is noise. A tidy report with no visible uncertainty hides exactly the information you need to know how much to trust it.
Why shouldn't I copy the capture flow per campaign? Because the copies drift. The attribution field gets set in one and forgotten in another, fixes land only where somebody was looking, and eventually the same outcome is attributed differently depending on which page produced it. One core with campaigns as data keeps capture identical by construction.
Does captured attribution tell me what caused the outcome? No. It accurately records the final context, which is a different question from causation. Someone who sees an email, searches later and then acts has a captured context of search and a cause that includes the email. No method resolves multi-touch causation from observational data, which is a reason for modesty rather than for preferring inference.
Tell us the system, the stakes, and the date that matters. You get a straight technical reply from the person who would lead the work, within 24 hours.
Bring us the program