Every month a data marketplace receives a file describing what its inventory did. Which segments were bought, by which advertiser, through which agency, on which channel, how many impressions, and what it earned. The file arrives, the numbers are added up, and everybody gets paid.
That is the version in the pitch deck. We built the real one.
The marketplace we worked with carries a few dozen data providers. Each month's file runs to something over a hundred thousand rows of revenue and usage reporting, and every one of those rows eventually becomes money moving to somebody. The interesting engineering is not storing them. It is answering, for each row, a question that turns out to be surprisingly hard: who earned this?
The provider is not in the provider column
There is a provider field. It is always the same value — the marketplace's own umbrella brand — because that is who the distribution platform is contracting with. As a per-segment identifier it is useless.
The actual provider is encoded in the segment's name, which is a hierarchical path. That would be fine if the path meant the same thing every time. It does not, because each supplier onboarded to the platform at a different time under a different convention. In one corpus we found five:
Umbrella > Powered by SUPPLIER > Category > ... supplier at level 2
Umbrella > Political Data > SUPPLIER > ... supplier at level 3
SUPPLIER > Automotive Owners > ... supplier first
SUPPLIER > 01 > ... supplier first, tier-coded
SUPPLIER > RV > 1 supplier first, flag at the leaf
None of these is wrong. They are just five different answers to "where does the supplier's name go," accumulated over years, and no one was ever going to go back and restate the old ones.
So attribution runs as a chain of fallbacks. First, look the segment up properly: segment id to audience name to owner. When that misses, parse the name path and resolve whatever token falls out against the known suppliers. When that misses too, the row is set aside for a person to look at.
The rule that governs the whole chain is the only interesting design decision in it: never invent an owner. If the extracted name does not resolve to a supplier we actually know, the row stays unattributed.
Guessing is always the cheaper option in the moment. It clears the queue, it makes the coverage number look better, and it costs nothing today. That is precisely what makes it a false economy: an unattributed row is visible, embarrassing and fixable, whereas a confident wrong answer is none of those. It is indistinguishable from a right one until the quarter closes and a supplier reads a number that is not theirs.
I would rather explain a gap than explain a transfer.
The description arrives after the money
The revenue file and the taxonomy that describes it are produced by different systems on different schedules. In practice this means every month's file contains segments that did not exist when the last taxonomy was sent — real revenue attached to identifiers that nothing can yet describe.
You cannot wait for the taxonomy, because the revenue is already earned. You cannot drop the rows, because that money is real. So the pipeline loads them, attributes them however it can, and marks the gap explicitly. A drift check runs after every load and counts segments that are live in the revenue data with no matching taxonomy entry. It fails loudly past a threshold, and it has a strict mode where placeholder rows synthesised to keep the joins working do not count as coverage — because otherwise you will cheerfully report full coverage of your own guesses.
This is the general shape of the problem in any multi-supplier feed. Metadata and data travel separately. Systems that assume they arrive together break quietly, in the direction of looking fine.
Machines propose, people confirm
The leftovers do not get solved by cleverness. They get solved by a person, and the job is to make that person fast.
For every unattributed segment, the pipeline finds its neighbours — segments sharing the top two levels of the path that already have an owner set — and suggests the one with the highest revenue behind it. That suggestion goes into a spreadsheet, a human confirms or overrides, and the answers flow back. An earlier version guessed with regular expressions; neighbour evidence turned out to be considerably better, because a segment's siblings are usually owned by whoever owns the segment.
The same shape works elsewhere. Where segments had been filed under the wrong vertical, we narrowed the candidates mechanically and used a cheap model as a judge on what survived, rather than asking a large one to reclassify a corpus it had no context for.
Neither of these is automation in the sense of removing the human. They are automation in the sense of only showing the human the decisions that actually need a person.
Why it has to tie out
There is one test that matters and it is arithmetic: the sum of every provider's revenue must equal total revenue.
When it does not, the instinct is to hunt for a rounding error. It is almost never a rounding error. It is a supplier who started earning before they were fully set up — earning revenue that lands correctly in the total but under no provider, so a provider-by-provider sum comes up short by exactly their earnings. The variance is not the bug. The variance is how you find out that onboarding is incomplete, which is why the reconciliation is worth running even when you are confident.
What this actually is
A marketplace's back office is not a reporting layer bolted onto a data business. For everyone in it, it is the business — it is the thing that tells a supplier what they made and the thing that pays them. Every provider gets a live view of their own earnings across advertisers, agencies and channels, and only their own.
Getting it right is mostly a refusal to guess about the things that turn into money.
