SupplyChain Object (schain) Implementation Guide for Ad Networks and Publisher Aggregators

When a tier-1 DSP ingests an OpenRTB bid request, its supply path optimization (SPO) engine cross-references the schain object against the publisher's root

Macro 3D illustration of five interconnected transparent glass SupplyChain nodes on a circuit plane linked by a continuous fiber-optic bridge from publisher origin to buyer vault.

When a tier-1 DSP ingests an OpenRTB bid request, its supply path optimization (SPO) engine cross-references the schain object against the publisher's root authorization file and each intermediary's hosted sellers.json file before committing compute resources to auction evaluation. If a single node in that chain exhibits a mismatched seller identifier, an unresolvable canonical domain, or a contradictory completeness flag, the buying platform either applies heavy bid shading or drops the request at the edge router. For ad networks, publisher aggregators, and curation platforms operating multi-hop monetization architectures, deterministic schain serialization is a prerequisite for maintaining bid density and clearing prices across web, mobile app, and Connected TV (CTV) inventory.

The IAB Tech Lab supply chain foundations establish that the OpenRTB SupplyChain object (v1.1) works alongside ads.txt v1.1 and sellers.json to expose every entity paid in a bid request via an ordered nodes array (asi, sid, hp) and a complete boolean flag (1 or 0). While static authorization files declare which platforms are permitted to sell a publisher's inventory in the abstract, the schain object provides real-time, per-impression cryptographic-style lineage showing which specific entities handled and monetized a given auction request.

At Sky Boosts, where we operate across Google Ad Manager (GAM) Multiple Customer Management (MCM), client-side Prebid.js 11.x, Prebid Server, and direct OpenRTB 2.6 pipelines for 100% Google-friendly and HUMAN/Pixalate IVT-clean inventory, supply chain hygiene directly governs yield performance. Because we settle publisher and aggregator payouts on reliable Net-30 and Net-60 terms, our clearing mechanics require strict alignment between actual financial flows and the hp (payment) flags declared inside every outbound bid stream.

Anatomy of the OpenRTB schain Object (v1.1)

The SupplyChain specification was originally introduced as an extension for OpenRTB 2.5 at the path BidRequest.source.ext.schain and was subsequently promoted to a first-class attribute in OpenRTB 2.6 at BidRequest.source.schain. Ad networks and SSPs operating hybrid exchange integrations must account for both locations during ingestion and serialization. When an aggregator ingests an OpenRTB 2.5 payload from a legacy SDK wrapper and translates it into an OpenRTB 2.6 request for a modern DSP endpoint, the transformation layer must migrate or mirror the object to source.schain without mutating the chronological order of the upstream nodes.

Verification relies on a three-way structural handshake. First, the publisher lists the initial monetization partner in its ads.txt or app-ads.txt file, which publishers can configure using our app-ads.txt guide to prevent unauthorized reselling. Second, that partner lists the publisher in its publicly hosted sellers.json file. Third, the bid request carries the schain object, where the first entry (nodes[0]) matches the ads.txt record and subsequent entries (nodes[1..n]) match each downstream reseller's sellers.json declaration.

Root-Level Attributes: complete and ver

The parent SupplyChain object serves as a container that defines the metadata and structural integrity of the entire transaction path. It contains two required attributes (complete and ver), the required nodes array, and an optional ext object for exchange-specific extensions.

  • complete (integer, required): An integer flag (1 or 0) acting as a boolean indicator of whether the nodes array accounts for every entity involved in the transaction back to the original owner of the inventory. A value of 1 asserts that nodes[0] represents the first commercial entity to receive payment from or share revenue with the underlying publisher, corresponding to a DIRECT relationship in the publisher's ads.txt or app-ads.txt file. A value of 0 signals that one or more upstream intermediaries prior to nodes[0] are omitted from the array.
  • ver (string, required): Defines the version of the SupplyChain specification in use. Even though the IAB Tech Lab administrative documentation refers to the clarified specification as v1.1, the actual wire-level string serialized inside the JSON payload must be set to "1.0". Passing "1.1" or a numeric float 1.0 triggers schema validation errors on strict DSP parsers that enforce the literal "1.0" string pattern.
  • nodes (object array, required): An ordered array of SupplyChainNode objects. The sequence is strictly chronological: index 0 is the earliest known entity in the transaction path, and the final index (nodes.length - 1) is the immediate seller transmitting the current OpenRTB request to the buying platform.
  • ext (object, optional): A placeholder for custom exchange or bidder-specific telemetry that falls outside the standard IAB specification.

Node Array Parameters: asi, sid, hp, rid, name, and domain

Every entity that participates in the financial settlement of an impression must append a single SupplyChainNode object to the nodes array. The specification defines six attributes for each node, three of which are strictly mandatory in standard revenue-sharing flows.

  • asi (string, required): The canonical domain name of the advertising system (SSP, ad network, or publisher management platform) that owns the node. This value must match the exact root domain used in the publisher's ads.txt file and must resolve to a valid https://{asi}/sellers.json endpoint. It must never include protocol prefixes (https://), subdomains like www. (unless specifically standardized by the exchange), or trailing URL paths.
  • sid (string, required): The seller or reseller account identifier associated with the upstream entity within the advertising system identified by asi. For nodes[0] in a complete chain, this string must be a case-sensitive match for field #3 in the publisher's ads.txt file AND the seller_id entry inside the asi domain's sellers.json file. For nodes[1..n], the sid identifies the immediately preceding node's account within the current node's system.AdOps teams should verify that their account identifiers match across all files prior to pushing configuration changes to production.
  • hp (integer, required): The "holder of payment" flag, expressed as 1 or 0. In v1.1 of the specification, hp: 1 indicates that the entity identified by the node is directly involved in the flow of payment for the inventory. When an ad network or SSP collects clearing funds from the downstream buyer and remits net revenue to the upstream publisher or aggregator (such as under Net-30 or Net-60 settlement cycles), hp must be set to 1. If a technical vendor (such as a standalone header bidding wrapper host, an analytics proxy, or a pure ad server) routes the bid request without ever touching the clearing funds for that impression, it should generally not be included in the schain at all; if included for diagnostic routing reasons, it must declare hp: 0.
  • rid (string, optional but strongly recommended): The unique OpenRTB BidRequest.id assigned to the auction by this specific node when it forwarded or ingested the request. When an impression traverses an aggregator and two SSP hops, each platform often generates its own internal BidRequest.id. Preserving the node-specific rid inside the schain allows DSPs and SSPs to perform deterministic log-level discrepancy debugging and IVT forensic tracing across organizational boundaries.
  • name (string, optional): The legal business name of the company paid for inventory under the given sid. Because this data is already publicly declared inside the sellers.json file of the asi host, most high-throughput exchanges omit name on the wire to minimize payload byte size and only populate it when is_passthrough or confidential seller structures require explicit disclosure.
  • domain (string, optional): The business domain name of the entity represented by sid. Similar to name, this is distinct from the asi field (which belongs to the platform) and instead represents the seller's own corporate root domain. It is typically omitted in standard programmatic traffic to conserve bandwidth unless required by specific buyer contracts.

When to Set complete:1 vs. complete:0 in Multi-Hop Auctions

Determining whether to set complete: 1 or complete: 0 is the most frequent source of implementation errors among ad networks and publisher aggregators. The governing engineering rule is absolute: an intermediary may only emit complete: 1 if it can verify with 100% certainty that nodes[0] is the initial monetization system contracted directly by the inventory owner, AND every single financial intermediary between the publisher and the current bid request is sequentially listed without gaps.

Consider three common programmatic architectures:

  1. Direct Network or GAM MCM (Manage Inventory / Manage Account) Integration: A publisher embeds the ad network's tag, Prebid adapter, or SDK directly onto its property and lists the network as DIRECT in its ads.txt file. The network initializes the schain object from scratch, sets complete: 1, and populates nodes[0] with its own asi, the publisher's sid, and hp: 1. If this network then forwards the request via server-to-server OpenRTB to an external SSP, that downstream SSP appends its own node at nodes[1] (containing the SSP's asi, the ad network's sid within that SSP, and hp: 1) while preserving complete: 1.
  2. Publisher Aggregator with Upstream Opacity: An ad network ingests traffic from a third-party supply partner via a legacy VAST tag or an OpenRTB 2.5 connection that lacks an incoming schain object, or where the partner is listed as RESELLER in the publisher's ads.txt file without providing the upstream DIRECT node. When the ad network constructs an outbound OpenRTB request for its downstream DSPs, it does not know the exact asi and sid of the upstream node that paid the publisher. In this scenario, the ad network must initialize the schain object with complete: 0 and place its own node at nodes[0].
  3. Mutating an Existing Incomplete Chain: If an SSP receives an OpenRTB request from an upstream aggregator where schain.complete is already set to 0, the SSP must never flip that flag to 1. Even though the SSP knows its own direct relationship with the upstream aggregator, the gap at the beginning of the chain remains unresolved.

Attempting to spoof complete: 1 on a multi-hop reseller path where the initial node is missing is catastrophic for yield. DSP verification crawlers automatically compare nodes[0] of any complete: 1 chain against the publisher's ads.txt file. If nodes[0] is only authorized as a RESELLER in ads.txt (or is absent entirely because an unlisted upstream network holds the DIRECT line), the DSP classifies the request as an unauthorized or falsified supply path. By contrast, when an intermediary honestly declares complete: 0 on legacy reseller traffic, many buyers still bid on the inventory according to their reseller floor policies rather than blacklisting the seat for protocol misrepresentation. Within our OpenRTB infrastructure, automated pre-auction validators verify every inbound schain against cached ads.txt and sellers.json graphs to prevent malformed flags from polluting downstream DSP QPS.

Code Examples: Appending schain Nodes in Prebid and Custom ORTB Endpoints

In client-side web integrations using Prebid.js 11.x, publishers or managing ad networks configure the SupplyChain module globally or per bidder. When an ad network manages the wrapper on behalf of a publisher and acts as the primary paymaster (DIRECT in ads.txt), it initializes the first node in the wrapper configuration so that every downstream bidder adapter automatically appends its own SSP node to the existing array:

pbjs.que.push(function() {
  pbjs.setConfig({
    schain: {
      validation: "strict",
      config: {
        ver: "1.0",
        complete: 1,
        nodes: [
          {
            asi: "skyboosts.com",
            sid: "pub-98412",
            hp: 1,
            rid: "req-eb4910a2-77c1"
          }
        ]
      }
    }
  });
});

When operating a server-side aggregator or Prebid Server instance that ingests an upstream OpenRTB 2.6 bid request and forwards it to downstream SSPs or DSPs, the middleware must inspect the incoming payload, preserve any existing nodes, and append its own node before dispatching the auction. The following Node.js/TypeScript middleware function demonstrates compliant node appending across OpenRTB 2.5 and 2.6 schemas:

function appendSchainNode(bidRequest, networkAsi, upstreamPartnerSid) {
  const incomingSchain = bidRequest.source?.schain || bidRequest.source?.ext?.schain;
  const currentNode = {
    asi: networkAsi,
    sid: String(upstreamPartnerSid),
    hp: 1,
    rid: String(bidRequest.id)
  };

  let updatedSchain;
  if (incomingSchain && Array.isArray(incomingSchain.nodes) && incomingSchain.nodes.length > 0) {
    updatedSchain = {
      ver: "1.0",
      complete: incomingSchain.complete === 1 ? 1 : 0,
      nodes: [...incomingSchain.nodes, currentNode]
    };
  } else {
    // Upstream request lacked schain; initialize as incomplete reseller path
    updatedSchain = {
      ver: "1.0",
      complete: 0,
      nodes: [currentNode]
    };
  }

  bidRequest.source = bidRequest.source || {};
  bidRequest.source.schain = updatedSchain;
  return bidRequest;
}

When a publisher aggregator (aggregator-net.example) originates a direct request (complete: 1) and routes it through the Sky Boosts exchange (skyboosts.com), the resulting OpenRTB 2.6 source payload transmitted to the DSP looks like this:

{
  "id": "sb-auction-99402184",
  "source": {
    "fd": 1,
    "tid": "7a8b9c0d-1234-5678-9101-abcdef123456",
    "schain": {
      "ver": "1.0",
      "complete": 1,
      "nodes": [
        {
          "asi": "aggregator-net.example",
          "sid": "direct-pub-4401",
          "hp": 1,
          "rid": "agg-req-55192"
        },
        {
          "asi": "skyboosts.com",
          "sid": "agg-seat-882",
          "hp": 1,
          "rid": "sb-auction-99402184"
        }
      ]
    }
  }
}

Notice how nodes[0].sid ("direct-pub-4401") maps to the publisher's account inside aggregator-net.example/sellers.json and matches the DIRECT line in the publisher's ads.txt file, whereas nodes[1].sid ("agg-seat-882") maps to the aggregator's INTERMEDIARY account inside skyboosts.com/sellers.json and matches the RESELLER line in the publisher's ads.txt file.

For engineering and AdOps teams managing multi-hop programmatic supply, the primary architectural takeaway is to treat schain construction as an immutable ledger operation. Audit your wrapper and server-side adapters to ensure that ver is strictly typed as "1.0", sid values are cast to exact case-sensitive strings matching your sellers.json records, hp: 1 is asserted for every entity participating in Net-30 or Net-60 clearing flows, and upstream nodes arrays are appended rather than overwritten. Enforcing strict schema validation at your ingestion edge eliminates accidental supply chain breaks and ensures your inventory clears at full bid density across tier-1 demand partners.

Next step

Put it to work.

Every pattern in this article runs on our unified SSP and monetization platform: GAM and MCM management, Prebid client and server-side, and CTV SSAI.

Net-30 / Net-60 terms · 100% brand-safe demand