Post

Trusting the Programmatic Supply Chain: Follow the Seller, Then Question the Inventory

Trace ads.txt, sellers.json, and OpenRTB schain through a worked supply-chain investigation, then evaluate spoofing, inventory quality, and supply-path economics.

Learning path · Lesson 7 of 16

AdTech from first principles

  1. The invisible auction
  2. From campaign budget to publisher payment
  3. Who decides which ad gets served?
  4. The millisecond handshake
  5. Anatomy of an OpenRTB auction
  6. Campaign lifecycle inside a DSP
  7. Trusting the programmatic supply chain You are here
  8. ML in the millisecond
  9. A real-time ad platform under a millisecond budget
  10. The data exhaust of a page load
  11. The post-cookie frontier
  12. Practical privacy on the web
  13. Learning the win curve
  14. From win curve to bidding
  15. Floor-price optimization
  16. What did the ad actually cause?
Trusting the Programmatic Supply Chain: Follow the Seller, Then Question the Inventory

Priya opens Wanderlust Weekly’s trekking article, and Urban Hiker’s DSP receives two offers for the ad slot beside it. Both name the same article. Both arrive before the auction deadline. One comes directly from the publisher’s SSP; the other arrives through another exchange.

The first temptation is to prefer the shorter route. But the extra exchange might be an authorized reseller, and the shorter route might still carry misrepresented inventory. Before comparing prices, we need to establish what each seller is entitled to offer.

The OpenRTB article showed how to read an auction message. The campaign lifecycle established which opportunities Urban Hiker may buy. We now investigate the seller’s side of eligibility: whether the offered inventory has a coherent, authorized route to the buyer, and whether that route is worth using.

We will assemble three records, break their connections, and compare two buying paths. By the end, we should be able to explain why a particular route passes, fails, or remains unresolved, and what further evidence a buying decision needs.

All companies, domains, payloads, and numbers below are synthetic. We model ordinary web display inventory with a publisher operating its own seller account and intermediaries participating in payment. Managed monetization, passthrough systems, and inventory sharing require additional interpretation.


1. Three Records, Three Different Claims

The supply chain becomes easier to reason about when we ask who is making each statement.

RecordPublished byQuestion it helps answer
ads.txtThe website publisherWhich accounts are authorized to sell this site’s inventory?
app-ads.txtThe app developer, through its declared websiteWhich accounts are authorized to sell this app’s inventory?
sellers.jsonAn advertising systemWhich entity does this seller account represent?
OpenRTB schainSelling systems along this request’s routeWhich selling and payment relationships are declared for this opportunity?

The IAB Tech Lab’s supply-chain overview describes how these components work together. Each answers its own question. An account directory cannot grant itself permission to sell a publisher’s inventory; a publisher’s authorization file cannot tell us which authorized route a particular request used.

This is a join across separately published claims: we connect records using shared identifiers. A seller’s account can exist in a directory while lacking permission to sell this particular site’s inventory. Checking either file alone would miss that distinction.

We should also distinguish three conclusions throughout the investigation:

  • Consistent: the available records agree under the checks we performed.
  • Contradictory: a specific claim conflicts with another record or with the integration we know.
  • Unknown: evidence is missing, stale, confidential, or outside our supported interpretation.

These are evidence states. Urban Hiker may decline both contradictory and unknown paths under its buying policy, but we should preserve their different diagnostic reasons.


2. Start at the Publisher: What Does ads.txt Authorize?

Wanderlust Weekly publishes this file at wanderlust.example/ads.txt:

1
2
3
OWNERDOMAIN=wanderlust.example
ssp-a.example, pub-42, DIRECT
exchange-b.example, acct-88, RESELLER

The first account belongs to Wanderlust Weekly at SSP A. The second is SSP A’s account at Exchange B, through which SSP A can resell the inventory. Those ownership facts are assumptions we will check against the next records.

An authorization row identifies an advertising system, an account within it, and the publisher’s relationship to that account. DIRECT indicates direct account control; RESELLER indicates an account controlled by another authorized entity. The optional fourth field identifies the advertising system within a certification authority. We omit it here. These meanings come from the ads.txt 1.1 specification.

The entire pair matters. Authorization for (exchange-b.example, acct-88) does not authorize acct-89 at the same exchange. Nor does pub-42 at a different system inherit SSP A’s authorization. Seller IDs are account identifiers, not globally meaningful company names.

DIRECT also does not describe an advertiser buying directly from a publisher through a negotiated insertion order. Our DIRECT row still participates in programmatic auctions. Conversely, the RESELLER row explicitly permits a resale relationship; its presence is not an allegation of fraud.

Property identity and company identity

Here the publication and its owning business share a domain. A media company owning five publications could instead have a business domain different from all five.

The ads.txt 1.1 additions make this distinction explicit: OWNERDOMAIN identifies the owner, while MANAGERDOMAIN identifies a primary or exclusive monetization manager, potentially for a particular country. The implementation guide connects those declarations to seller business domains. A corporate domain differing from the page’s hostname is not automatically a mismatch.

Our example has no manager. For outsourced monetization, the manager can be part of the most direct available route. A universal “one node only” rule would discard that legitimate arrangement.

Apps change the discovery step

An app bundle has no website from which to fetch ads.txt, so under app-ads.txt discovery starts with the app’s store listing and its declared developer website, from which the appropriate app-ads.txt location is derived.

For a trekking app, we would preserve the mapping from the exact store listing and app identity to that developer domain. Finding a plausible file on a similarly named website would establish the wrong starting point. Once discovered correctly, its seller-account rows support the same kind of authorization check.


3. Resolve the Accounts: Who Gets Paid?

Next we inspect the corresponding records in each system’s sellers.json. We show one record each; a production directory can contain many accounts.

At ssp-a.example/sellers.json:

1
2
3
4
5
6
7
8
9
10
11
{
  "version": "1.0",
  "sellers": [
    {
      "seller_id": "pub-42",
      "seller_type": "PUBLISHER",
      "name": "Wanderlust Weekly",
      "domain": "wanderlust.example"
    }
  ]
}

At exchange-b.example/sellers.json:

1
2
3
4
5
6
7
8
9
10
11
{
  "version": "1.0",
  "sellers": [
    {
      "seller_id": "acct-88",
      "seller_type": "INTERMEDIARY",
      "name": "SSP A",
      "domain": "ssp-a.example"
    }
  ]
}

The sellers.json specification identifies the entity paid under an account. PUBLISHER means the named entity owns the inventory and receives payment directly from the system. INTERMEDIARY applies when ownership or direct payment does not hold; BOTH covers both kinds of inventory. Setting is_confidential: 1 permits a record to omit its name and domain, limiting public identity resolution.

Our two lookups produce different answers by design:

  • SSP A’s pub-42 resolves to Wanderlust Weekly.
  • Exchange B’s acct-88 resolves to SSP A.

Exchange B pays SSP A, which has the upstream relationship with the publisher. Its directory should therefore name SSP A, even though the page belongs to Wanderlust Weekly. An account keeps one identity across every publication it represents.

The second schain node's system and account together match a RESELLER line in Wanderlust Weekly's ads.txt. Separately, the system names the seller directory to open, exchange-b.example/sellers.json, and the account selects the record in it, which resolves to SSP A. Figure 1. Resolve the pair, then connect the claims. One node, two independent lookups. The advertising-system domain chooses the account namespace; the account ID selects the record within it. An account match alone is insufficient, and authorization remains a separate publisher claim.

We retain the system-account pair, resolved entity, relationship, and source snapshot together. Searching every directory for acct-88, or displaying only a friendly company name, would discard the evidence that makes the lookup inspectable.


4. Follow One Request Through schain

For the indirect offer, Exchange B sends this excerpt, not a complete bid request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
  "id": "b-auction-731",
  "site": {
    "domain": "wanderlust.example"
  },
  "source": {
    "schain": {
      "ver": "1.0",
      "complete": 1,
      "nodes": [
        {"asi": "ssp-a.example", "sid": "pub-42", "hp": 1},
        {"asi": "exchange-b.example", "sid": "acct-88", "hp": 1}
      ]
    }
  }
}

The OpenRTB 2.6 object definitions place this object at source.schain; OpenRTB 2.5 uses source.ext.schain. Nodes run from the initial selling relationship toward the sender. asi identifies the system, sid its seller account, and hp: 1 indicates participation in payment. SupplyChain version 1.0 expects hp to be 1. complete: 1 declares that the chain reaches the inventory owner.

We can now read our example aloud: SSP A pays Wanderlust Weekly under pub-42; Exchange B pays SSP A under acct-88. The buyer receives the request from Exchange B. For this modeled sale, settlement runs in the reverse direction.

Wanderlust Weekly sends the opportunity through SSP A and Exchange B to the DSP. Payment arrows return toward the publisher; each schain node brackets the system and the seller it pays. Figure 2. The request travels out; payment travels back. A node is a system-account relationship, not just a company logo. Reading the payment arrows backward through the request route explains why Exchange B’s seller record names SSP A. The chain encodes no fee amounts.

Our investigation now has four concrete checks:

  1. Authorization: both system-account pairs appear in Wanderlust Weekly’s file with the expected relationships for this example.
  2. Origin: SSP A’s pub-42 resolves to the declared owner, consistent with our publisher-operated account.
  3. Continuity: Exchange B’s acct-88 resolves to SSP A, connecting the second selling relationship to the first.
  4. Receiving integration: the final system agrees with the Exchange B integration delivering this request, including any documented canonical-domain mapping.

These checks fit our ordinary resale example. The IAB implementation FAQ covers arrangements such as passthrough and header bidding that need more context. In practice, we corroborate entity mappings through the documented integration rather than equating companies through similar display names.

What happens when a reseller appends a node?

SSP A’s direct offer would contain just the first node. When Exchange B resells it, it appends the second and preserves the incoming completeness flag. If the upstream chain was absent, Exchange B would start with its known node and declare complete: 0. These are propagation rules, not something a buyer can repair by inference.

With only Exchange B’s node and complete: 1, the declared origin would resolve to SSP A instead of Wanderlust Weekly. That contradicts our known arrangement. The supplying systems must restore the missing relationship; changing the completeness flag cannot create evidence.

Even a consistent chain contains no fee amounts, so the node count implies neither equal commissions nor a particular publisher share. Actual settlement requires the separate money ledger.


5. Break the Joins Before Trusting the Validator

We learn more by changing one fact at a time than by collecting successful examples. Starting from the coherent path above, consider these independent mutations:

ChangeWhat the evidence saysAppropriate next step
Replace acct-88 with acct-89; the fresh publisher file lists only acct-88 for Exchange BThis account lacks the publisher authorization established for our exampleDecline this route and ask which account should be offered
Keep acct-88, but its directory record names an unrelated resellerThe chain’s adjacent entities no longer connectInvestigate the directory or the missing upstream relationship
Remove SSP A’s node but keep complete: 1The claimed complete chain contradicts our known arrangementRequire an upstream correction
Publish the intermediary account as confidentialPublic identity resolution is incompleteApply the buyer’s transparency policy; seek corroboration
Fail to fetch a directory, with no usable cached snapshotWe cannot perform the lookupRecord an availability failure, not an invented seller identity

In the second mutation, both IDs could exist in both files while connecting the wrong businesses. Membership checks alone cannot establish continuity.

A directory error and deliberate misrepresentation can produce similar symptoms. An accusation of fraud needs more evidence than a failed join. Proposed internal reason codes such as authorization_missing, entity_discontinuity, and directory_unavailable describe what we observed without guessing intent; they are our own convention rather than standard OpenRTB codes.

Time is part of the join

Imagine the publisher adds acct-88 at 10:00, Exchange B starts sending requests at 10:05, and our cache still holds the 09:00 file. A missing row in that snapshot is real; the conclusion that the publisher has not authorized the account now is unsupported.

The seller-directory specification addresses cache expiry. For our investigation, we also retain the request time, each file’s fetch time, and the snapshots actually used. A later report should judge yesterday’s decision against yesterday’s files.

Conversely, a stale allowlist can preserve permission after a publisher removes an account. Caching therefore has two costs: delayed acceptance of new relationships and delayed recognition of revoked ones. The refresh and expiry policy must address both.


6. A Consistent Chain Still Leaves Important Questions Open

Return to Priya’s trekking page. Our joins establish that the declared route makes sense. They do not observe her browser, the rendered slot, or whether Priya is a person at all.

Domain spoofing misrepresents the inventory’s origin. An offer can claim wanderlust.example while the actual placement belongs elsewhere. Our first join can catch an unauthorized account, but plausible public declarations copied into a message do not authenticate the page event. The IAB FAQ explicitly says the presence of these records does not guarantee correctness.

We therefore need evidence from the integration and delivery process: verified publisher relationships, authenticated connections, placement investigations, and observation of where an ad renders. Each method has limits. A downstream log that merely copies site.domain supplies another copy of the claim, not independent confirmation.

Above a dashed boundary, public records and the request's site.domain claim meet at a consistency check, and the join ends at the boundary. Below it, inspecting the actual page and slot yields independent evidence, the only arrow that crosses up to test the claim. Figure 3. A matching declaration is not an observed event. Agreement among declarations leaves a gap between the claimed placement and the observed event. Independent origin and delivery evidence must cross that gap; another copy of the same claim, such as a copied site.domain, does not. Traffic quality and permission need their own evidence too.

Invalid traffic (IVT) concerns activity that should be excluded from legitimate advertising measurement. The MRC’s IVT standards distinguish general IVT, addressed with routine filtration such as known-crawler lists, from sophisticated IVT requiring deeper analysis or corroboration. Some evidence arrives after bidding. IVT also includes manipulated human activity, so “a person was involved” is insufficient.

These categories can overlap: domain misrepresentation itself appears in the MRC’s sophisticated IVT examples. We separate the investigative questions because they need different evidence, not because every failure fits exactly one bucket. A coherent seller path carrying bot-generated page loads would pass our account joins and fail the traffic checks.

Made-for-advertising (MFA) inventory introduces a quality problem that can survive both authorization and basic traffic checks. The ANA and partner trade groups’ definition describes combinations such as heavy advertising density, aggressive refresh, dependence on purchased traffic, and low-value content. It also explains that such sites can exhibit good viewability and low measured IVT.

An authorized seller can therefore offer a real, viewable ad in an environment Urban Hiker considers unsuitable. We need to examine the page experience against the campaign’s placement standards. A low price or one isolated characteristic does not establish an MFA diagnosis.

Finally, a selling/payment chain maps payment relationships, not every recipient of request data. Analytics, verification, and other participants may lie outside it. A successful join also supplies no permission to use an audience identifier. The post-cookie article addresses that separate set of constraints.

For Urban Hiker, “eligible supply” means these checks collectively satisfy our buying requirements. A successful seller lookup is one part of that decision.


7. Supply-Path Optimization Starts with Overlap

Once routes satisfy our buying requirements, supply-path optimization (SPO) asks which combination of them best serves the campaign. Counting intermediaries is one input. We also care about unique opportunities, reliable access, response time, cost, delivery quality, and the ability to inspect outcomes.

Suppose Path A is publisher → SSP A → DSP. Path B is publisher → SSP A → Exchange B → DSP, the route we just investigated. Across one observation window:

  • A exposes 80,000 distinct slot opportunities.
  • B exposes 60,000.
  • 50,000 appear through both routes.

Together they expose 90,000 unique opportunities: 80,000 + 60,000 − 50,000. B adds 10,000 beyond A, although it sends 60,000 offers. These counts are slot opportunities rather than purchased impressions or people reached, the same distinction used in our campaign feasibility estimate.

An area-proportional partition shows 30,000 opportunities unique to A, 50,000 shared by A and B, and 10,000 unique to B. Coverage brackets show A totals 80,000 and B totals 60,000, together 90,000 unique. Figure 4. Two routes do not mean two sets of new inventory. Removing B eliminates 60,000 offers but only 10,000 currently unique opportunities. The shared region is where route substitution can be tested; B’s unique region needs a separate value assessment.

This arithmetic depends on recognizing the same underlying slot opportunity. Two request IDs from different exchanges need not match. Nor is “same URL within one second” sufficient: a page can contain multiple slots or refresh them. Our toy counts assume reliable publisher-origin opportunity mapping. Where an integration supplies a shared transaction identifier, we still need to validate its scope and propagation before using it for deduplication.

Without that mapping, we should report overlap as an estimate with matching error, or restrict the calculation to a validated subset.

The shared 50,000 need not cause duplicate purchases: both routes may converge on one final placement decision. They do create repeated bidding work. We still need to test whether A can substitute for B under the relevant eligibility, timeout, deal, and floor conditions.

B’s observed unique access could reflect routing rules, throttling, or outages. The partition may change when we alter participation, so the diagram describes our observation window rather than promising future coverage.


8. Compare Cost Against the Outcome We Actually Count

Even within comparable inventory, fewer nodes need not give the lowest cost for useful delivery. Consider two purchase cohorts, separate from the opportunity counts above. Each contains 10,000 billable impressions. To isolate the arithmetic, assume all are measurable and pass the same IVT filtration, with consistent reporting cutoffs and measurement rules.

MeasurePath A: one selling systemPath B: two selling systems
Advertiser’s all-in spend₹1,000₹1,100
Billable impressions10,00010,000
Measured viewable impressions6,0008,000
All-in CPM₹100₹110
Cost per 1,000 measured viewable impressions₹166.67₹137.50

For A, we calculate ₹1,000 ÷ 6,000 × 1,000 = ₹166.67. For B, ₹1,100 ÷ 8,000 × 1,000 = ₹137.50. B costs 10% more per billable impression but 17.5% less per measured viewable impression.

Two paired bar charts show the ranking reversing: Path A costs ₹100 CPM versus B's ₹110, but A costs ₹166.67 per thousand measured viewable impressions versus B's ₹137.50. Figure 5. The cheaper CPM can buy costlier viewable delivery. Changing the denominator reverses the ranking in these synthetic cohorts. Both comparisons use all-in advertiser spend; neither number reveals publisher revenue or incremental sales.

The difference comes from measured viewability, potentially reflecting placement mix or rendering reliability. The cohorts establish the arithmetic only; showing that adding Exchange B caused better delivery requires a test.

In a test, we would assign eligible opportunities to routing policies randomly where feasible, align campaign and measurement settings, and compare results by placement, device, geography, and time. Looking only at purchases hides opportunities each policy failed to receive or win. We need enough observations to distinguish an effect from sampling noise.

With partial coverage, we must report measurable counts as well as viewable counts. An unmeasurable impression is not an observed non-viewable impression. A route whose measurement covers only its easiest placements can look superior for that reason alone.

Viewability is still an intermediate measure. As the MFA discussion showed, it can look healthy in an undesirable environment. Urban Hiker ultimately wants suitable exposure and incremental boot purchases, so the final evaluation connects path changes to the campaign’s objective, with sufficient outcome maturity. Publisher proceeds remain a separate reconciliation question.

Our provisional decision is to test A against B on shared opportunities while retaining eligible B-only access. We would reduce a route if it contributes little unique value and performs worse under a sound comparison. Its node count alone cannot settle the decision.


9. Turn the Investigation into a Repeatable Control

We would not fetch several public files synchronously during the auction’s 120 ms response window. A practical design collects and validates reference data outside the bidding path, then supplies versioned lookup results to the bidder.

The collector discovers the publisher or developer authority, retrieves and parses the files, and resolves account relationships. The bidder combines those results with the incoming chain and receiving integration. Missing evidence follows our configured policy.

For each sampled decision, a useful evidence record includes:

  • The request time, claimed site or app, receiving integration, and ordered system-account pairs.
  • The reference snapshot identifiers, fetch times, and freshness results used for the checks.
  • The resolved entities, detected contradictions or unknowns, and the rule version producing the buying decision.
  • Later quality and delivery findings, attached as later evidence rather than written over the original decision.

We can usually preserve this explanation without copying the full audience payload. Access and retention should reflect the investigation’s actual needs.

A dashboard should separate failure reasons and identify denominators: received requests, evaluated paths, and spend answer different questions. A crawler outage should appear as an evidence-availability incident, not a sudden surge in fraudulent publishers.

Repairs belong with the party controlling the statement: the publisher updates authorization; the selling system fixes its directory or chain; the buyer corrects stale caches or unsupported assumptions. After a repair, we rerun the failed example and confirm that an unauthorized neighboring account still fails.

Write the decision we can defend

For our intact example, a compact investigation note would read:

Exchange B’s acct-88 is authorized to resell Wanderlust Weekly’s inventory and resolves to SSP A. SSP A’s pub-42 resolves to the declared owner. The ordered chain and receiving integration agree with that route. The declared path is consistent with our reference snapshots. Inventory origin, traffic quality, and campaign suitability require their separate evidence before purchase.

Replacing acct-88 with acct-89 changes the authorization conclusion. Hiding its public identity changes what we can resolve. Neither change should be buried inside a single unexplained “trust score.”

For a real publisher, public files let us construct a possible authorized path. Establishing that a particular auction took that path requires a request sample obtained through an authorized integration, along with the relevant historical snapshots. A public directory alone cannot reveal which route Priya’s impression actually followed.

In the next article, we return to prediction and optimization. We now have a more precise input to that machinery: an opportunity whose seller relationships we can explain, whose unresolved evidence we can name, and whose route we can evaluate against the campaign’s objective.


Resources

Enjoyed this article? Never miss out on future posts - follow me.
© Sayan Biswas. All rights reserved.