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

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 (1or0) acting as a boolean indicator of whether thenodesarray accounts for every entity involved in the transaction back to the original owner of the inventory. A value of1asserts thatnodes[0]represents the first commercial entity to receive payment from or share revenue with the underlying publisher, corresponding to aDIRECTrelationship in the publisher'sads.txtorapp-ads.txtfile. A value of0signals that one or more upstream intermediaries prior tonodes[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 float1.0triggers schema validation errors on strict DSP parsers that enforce the literal"1.0"string pattern.nodes(object array, required): An ordered array ofSupplyChainNodeobjects. The sequence is strictly chronological: index0is 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'sads.txtfile and must resolve to a validhttps://{asi}/sellers.jsonendpoint. It must never include protocol prefixes (https://), subdomains likewww.(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 byasi. Fornodes[0]in a complete chain, this string must be a case-sensitive match for field #3 in the publisher'sads.txtfile AND theseller_identry inside theasidomain'ssellers.jsonfile. Fornodes[1..n], thesididentifies 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 as1or0. In v1.1 of the specification,hp: 1indicates 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),hpmust be set to1. 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 theschainat all; if included for diagnostic routing reasons, it must declarehp: 0.rid(string, optional but strongly recommended): The unique OpenRTBBidRequest.idassigned 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 internalBidRequest.id. Preserving the node-specificridinside theschainallows 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 givensid. Because this data is already publicly declared inside thesellers.jsonfile of theasihost, most high-throughput exchanges omitnameon the wire to minimize payload byte size and only populate it whenis_passthroughor confidential seller structures require explicit disclosure.domain(string, optional): The business domain name of the entity represented bysid. Similar toname, this is distinct from theasifield (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:
- 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
DIRECTin itsads.txtfile. The network initializes theschainobject from scratch, setscomplete: 1, and populatesnodes[0]with its ownasi, the publisher'ssid, andhp: 1. If this network then forwards the request via server-to-server OpenRTB to an external SSP, that downstream SSP appends its own node atnodes[1](containing the SSP'sasi, the ad network'ssidwithin that SSP, andhp: 1) while preservingcomplete: 1. - 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
schainobject, or where the partner is listed asRESELLERin the publisher'sads.txtfile without providing the upstreamDIRECTnode. When the ad network constructs an outbound OpenRTB request for its downstream DSPs, it does not know the exactasiandsidof the upstream node that paid the publisher. In this scenario, the ad network must initialize theschainobject withcomplete: 0and place its own node atnodes[0]. - Mutating an Existing Incomplete Chain: If an SSP receives an OpenRTB request from an upstream aggregator where
schain.completeis already set to0, the SSP must never flip that flag to1. 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.