The Ops Atlas xOps School

Business & Growth Established term Sources verified

AdOps

AdOps (advertising operations) turns sold or auctioned ad space into delivered, measured and billable impressions: it sets up and checks campaigns in the ad server, runs the auction plumbing, keeps fraud and unauthorised sellers out, and reconciles the numbers.

Key takeaways

  • Ad operations is an established job function (IAB runs a certification for it), but it has no single body of knowledge: its rules come from contracts, IAB Tech Lab specifications and MRC measurement guidelines.
  • The core skill is making three records agree: what was sold (the order or deal), what the ad server and auctions decided, and what was counted and billed.
  • ads.txt, sellers.json and the SupplyChain object make the programmatic supply chain checkable, but only when publishers keep them accurate and buyers enforce them.
  • Discrepancies between ad servers are normal (Google documents up to 20% for third-party-hosted line items), so agree the controlling count and the tolerance before launch.
  • Judge delivery, yield and trust together: pacing, fill and eCPM mean little if the measured rate is low, invalid traffic is high or unauthorised sellers carry your domain.
  • Header bidding adds price competition, and also latency, line-item sprawl and new failure modes; set the timeout and price granularity from your own data.
  • A small site with one ad network needs an owner and a checklist, not an AdOps team.

AdOps (advertising operations, usually shortened to “ad ops”) is the work that turns advertising that was sold, or put up for auction, into ads that actually appear, are counted correctly and can be billed. An ad ops team sets up orders, creatives and targeting in an ad server, checks that everything renders and tracks, watches delivery daily, connects the auctions that let outside buyers bid, keeps fraud and unauthorised resellers out, and reconciles counts with the buyer’s before anyone invoices.

The open-source header-bidding project Prebid gives a practical definition of the role: “When we refer to ad ops, we’re talking about the people who login to the ad server software and manage the campaigns and accounts.”[1] This page uses that operational meaning and extends it to the supply-chain controls the job now depends on.

Written for beginners, publisher and agency ad ops teams, engineers who integrate ad tech, and leaders who own advertising revenue or budgets. Sources were last verified on 2026-10-23.

Question Answer
Goal Every sold or auctioned impression is delivered as agreed, measured to an agreed standard, sold only by authorised parties, and billed on reconciled numbers
Who does it Publisher ad ops and yield teams; agency and advertiser trafficking teams; account and solutions teams at ad platforms
Inputs Insertion orders (IOs) and deals, creatives and third-party tags, inventory definitions, price floors, consent signals
Outputs Live line items and deals, QA evidence, delivery and discrepancy reports, invoices that survive scrutiny, an accurate ads.txt
Core systems Publisher ad server, header-bidding wrapper, supply-side platform (SSP), demand-side platform (DSP), verification and consent tools
Prerequisites Comfort with spreadsheets and basic HTML/JavaScript; no other Atlas page is required
Term status Established: a recognised job function with an IAB professional certification and a large body of IAB Tech Lab and MRC standards, but no single framework document

Digital advertising is sold in two very different ways, and both fail without operations.

  • Direct (guaranteed) sales. A salesperson promises, say, 1.5 million impressions on the sports section in November. Someone must turn that into line items with the right targeting, dates, caps and creatives, check that the advertiser’s tags fire, and steer delivery to the goal. A missed promise means a “make-good” (free replacement delivery) or a refund.
  • Programmatic (automated) sales. Each impression is auctioned to many buyers in a fraction of a second. Someone must connect the auctions, set floors, map bids into the ad server, and make sure buyers get this publisher’s inventory, not a counterfeit. In 2025 programmatic advertising in the US reached $162.4 billion,[2] by an IAB/PwC definition that also counts “publisher direct via a proprietary ad platform (i.e. self-serve)”;[3] in this Atlas’s reading that includes social platforms, so the figure is not a measure of the open web alone.

The money at stake makes the failures visible. A study by the US Association of National Advertisers (ANA), based on log-level data from 21 advertisers, concluded that “Only 36 cents of every dollar that enters a demand-side platform (DSP) effectively reaches the consumer”.[4] A UK study could not trace 15% of programmatic spend (Scenario 2), and fraud operations such as 3ve sold counterfeit inventory under real publishers’ names (Scenario 1).

Before and after. Without ad ops discipline, creatives break after launch, under-delivery surfaces in the last week, ads.txt lists long-gone partners, and month-end is an argument about numbers. With it, every campaign has a QA record, pacing is checked daily, partners are verifiable, and the controlling count is agreed before launch.

  • Not media planning or campaign strategy. Audiences, budgets, channels and messages belong to MarketingOps; AdOps executes and verifies what was planned and sold.
  • Not the tag and data infrastructure. Tag managers, server-side tracking and CDPs belong to MarTechOps; AdOps owns which pixel fires on which ad.
  • Not privacy law. Whether a lawful basis exists is for PrivacyOps and counsel; AdOps makes sure the user’s recorded choice travels with the ad request and is honoured.
  • Not video streaming engineering. Encoding, packaging, ad markers and content delivery for video belong to MediaOps; AdOps covers what fills each ad break and how it is counted (see video and CTV ad operations).
  • Not search-engine optimisation. Organic search belongs to SEOps and SEO Operations; paid search is usually run by performance-marketing teams.
  • Not “DevOps for ads”. In this Atlas’s reading the suffix comes from the job title “ad operations” (the sense in Prebid’s definition above), not from the DevOps movement, though modern ad ops borrows its habits (version control, automated checks, post-incident reviews).

In this Atlas’s summary (no primary history of the job title was found), ad operations grew out of “trafficking” campaigns into ad servers. The milestones below changed the job and can be dated from primary sources.

When What changed Why it mattered to ad ops
November 2010 – January 2012 OpenRTB began as a pilot of three DSPs and three SSPs; 2.0 (June 2011) unified display, mobile and video; 2.1 became an IAB Tech Lab standard (January 2012)[5] Interoperable real-time auctions to configure
August 2015 / October 2017 MRC and IAB viewability guideline v2.0 (2015)[6]; MRC invalid-traffic guidelines issued October 2015[7]; IAB/MRC display impression guideline v1.1 (2017)[8] Billing disputes gained a reference definition
2015 Prebid.js launched as an open-source header-bidding wrapper[9] Line-item mapping and timeout tuning joined the job
2016 TAG launched its Certified Against Fraud programme[10] Anti-fraud controls became certifiable
June 2017 ads.txt 1.0 released by IAB Tech Lab[11] Publishers declare authorised sellers
July 2019 sellers.json and the OpenRTB SupplyChain object finalised[12] Buyers can identify every paid intermediary
January 2024 Google required a Google-certified consent platform integrated with the TCF for personalised ads to EEA and UK users[13] Consent signals became a delivery prerequisite
2025–2026 Chrome kept third-party cookies and retired most Privacy Sandbox APIs (October 2025)[14]; the European Commission fined Google over adtech self-preferencing (September 2025)[15]; a US court found Google monopolised open-web ad tech (April 2025)[16] and chose behavioural remedies (September 2026)[17] Regulators now shape auction rules too

Is AdOps an established discipline? Yes, as a job function. IAB lists a Digital Ad Operations Certification that “Demonstrates knowledge and competency in the area of digital ad operations”.[18] Its 2017 study guide divides the job into executing campaign launch (33% of exam weight), managing live campaigns (38%), supporting ad operations initiatives (19%) and managing incidents (11%).[19] IAB also ran a Mobile Ad Ops Working Group.[20] There is no single framework document like ITIL; this page maps the contract templates, specifications and guidelines instead.

Term Meaning Example
Inventory Ad space a publisher can sell: ad units (slots), sizes, formats A 300×250 slot on article pages
Insertion order (IO) Contract for a direct campaign: dates, quantities, price, targeting, terms 1.5 million impressions at $18 CPM
Line item Ad-server object for one part of an order: type, priority, goal, targeting, creatives “Standard”, priority 6, goal 1.5 million
Ad server Decides which ad fills each request; counts impressions and clicks Google Ad Manager (sell side), Campaign Manager 360 (buy side)
Avails / sell-through Forecast unsold impressions for given targeting / share of inventory sold 200,000 sports avails in November
Key-values Targeting pairs sent with the ad request section=sports, hb_pb=12.50
Passback A tag that cannot fill hands the impression to another ad server Each hop adds discrepancy[21]
Dynamic allocation / unified auction Exchange and other non-guaranteed demand competing with line items in real time[22][23] AdX outbids a price-priority line item
First look / last look A favoured source buys before others bid / sees competing bids before bidding (the EC described AdX being told the best rival bid in advance)[15] Barred in the US proposed judgment[24]
SSP / DSP / exchange Supply-side platform (sells a publisher’s impressions) / demand-side platform (bids for buyers) / the marketplace where bids meet A bid request flows SSP → DSP
RTB / OpenRTB Real-time bidding / the IAB Tech Lab protocol for bid requests and responses[5] A JSON bid request with a 300 ms tmax
Header bidding Collecting bids from several demand partners before calling the ad server, so they compete with it[1] Prebid.js on the page, Prebid Server in the cloud
CPM / eCPM Cost per thousand impressions / effective CPM = revenue ÷ impressions × 1000[19] $9,360 for 520,000 impressions = $18.00 eCPM
Viewable impression At least 50% of an ad’s pixels in view for at least one continuous second (display) or two seconds (video)[6] Measured by a verification tag
IVT (GIVT / SIVT) Invalid traffic: general (found with lists and routine checks) or sophisticated (needs advanced analysis)[25] Data-centre bots (GIVT); spoofed domains (SIVT)
ads.txt / app-ads.txt A public file listing the sellers a publisher authorises[26] ssp.example, 1001, DIRECT
sellers.json A public file in which an SSP lists the sellers it pays and whether each is a publisher or an intermediary[27] "seller_type": "PUBLISHER"
SupplyChain object (schain) Every entity paid along the way, listed in the bid request[28] Publisher’s SSP node, then a reseller’s
CMP / TCF / GPP Consent management platform / the IAB Europe and IAB Tech Lab strings carrying user choices[29][30] A TC string in the bid request

A mental model: three records that must agree

Link to section “A mental model: three records that must agree”

Most ad ops problems are disagreements between three records (pacing, fill, make-good and discrepancy are defined in metrics and KPIs):

  1. What was promised: the IO, the deal terms, the floor, the brand-safety rules.
  2. What was decided: the line items, auctions and ad-server logs that chose an ad for each request.
  3. What was counted: impressions, viewable impressions, clicks, invalid traffic and money, as measured by each party.

Trafficking aligns records 1 and 2; measurement standards align record 3 with reality; reconciliation aligns the parties’ versions of record 3 with each other and with record 1; supply-chain transparency (ads.txt, sellers.json, schain) checks that whoever sold in record 2 had the right to.

Figure 1. AdOps and its neighbours: what crosses each boundary
MarketingOps:plans, budgets,campaigntaxonomyMarTechOps: tagmanager, CDP,consent plumbingPrivacyOps:lawful basis,consent policyRevOps: revenuedefinitions,forecastAdOps:trafficking,auctions,supply-chaintrust,reconciliationMediaOps: videoencoding, admarkers, deliveryAnalyticsOps:governed metricsand dashboardsWebOps: pagespeed and CoreWeb VitalsContentOps:content andpages tomonetise
Diagram source (Mermaid)
%%{init: {"flowchart": {"wrappingWidth": 120, "nodeSpacing": 14, "rankSpacing": 22, "padding": 8}}}%%
flowchart LR
  MK["MarketingOps: plans, budgets, campaign taxonomy"]
  MT["MarTechOps: tag manager, CDP, consent plumbing"]
  PV["PrivacyOps: lawful basis, consent policy"]
  RV["RevOps: revenue definitions, forecast"]
  A["AdOps: trafficking, auctions, supply-chain trust, reconciliation"]
  MD["MediaOps: video encoding, ad markers, delivery"]
  AN["AnalyticsOps: governed metrics and dashboards"]
  WB["WebOps: page speed and Core Web Vitals"]
  CT["ContentOps: content and pages to monetise"]
  MK --> A
  MT --> A
  PV --> A
  RV <--> A
  MD <--> A
  A --> AN
  A <--> WB
  CT --> A
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
  class A core
  class MK,MT,PV,RV,MD,AN,WB,CT neutral

Text equivalent: AdOps sits in the centre. It receives plans and taxonomy from MarketingOps, tag and consent infrastructure from MarTechOps, consent policy from PrivacyOps and pages from ContentOps. It exchanges revenue definitions with RevOps and page-performance constraints with WebOps. With MediaOps the exchange is two-way: ad markers and ad-break inventory come in; ad decisions and measurement requirements go back. Delivery data goes to AnalyticsOps.

Neighbouring page Shared ground AdOps focus Neighbour’s focus
MarketingOps Paid campaigns, budgets, naming taxonomy Trafficking, pacing, verification, discrepancies Campaign process, lead management, attribution
MarTechOps Pixels, tags, consent signals Which tags run on which ads; signals on ad requests Tag manager and CDP architecture, server-side tracking
PrivacyOps Consent for personalised ads Passing and honouring TCF/GPP signals Lawful basis, notices, data-subject rights
MediaOps Ad breaks in video and CTV streams VAST/VMAP, ad pods, insertion method, ad measurement Encoding, packaging, ad markers, CDN delivery
RevOps Advertising revenue as a business line Yield, fill, eCPM, billing accuracy Revenue definitions, forecasting across functions
AnalyticsOps Ad revenue dashboards Producing trustworthy delivery data Metric definitions and dashboard governance
WebOps Page weight and latency added by ads Timeouts, lazy loading, ad density Measuring and fixing Core Web Vitals
ContentOps Pages and sections that carry ads Ad units and targeting keys per section Content lifecycle and structure

The boundary with MarTechOps is the one most often blurred: MarTechOps owns the tag infrastructure; AdOps owns delivery. This is an editorial distinction of this Atlas, not an industry standard; small teams often do both jobs.

No industry body publishes an AdOps manifesto; the charter below is this Atlas’s proposal, distilled from the sources cited.

Mission. Deliver every impression that was sold or auctioned exactly as agreed, count it by an agreed method, sell it only through parties the owner has authorised, and bill it on numbers both sides can trace.

  1. The contract is the specification. The IO or deal fixes targets, dates, counting method and tolerances before launch; the 4A’s/IAB template, for example, names which count controls and what happens above a 10% gap.[31]
  2. Count late, count once, count the same way. An impression counts when the ad has loaded and begun to render, not when the server decides to send it.[8]
  3. Every seller is declared and checkable in your ads.txt or app-ads.txt, and every paid hop in the SupplyChain object and its system’s sellers.json.[26][28]
  4. Consent travels with the ad request and is enforced where you control it. Passing a TCF or GPP string lets each partner apply it but blocks no request by itself.[29][30][32]
  5. Every change is reversible and attributable, so “what changed?” has an answer within minutes of a revenue drop.
  6. Revenue does not outrank the reader. Ad density, page speed, malicious creatives and intrusive formats are operational risks.[33]
  7. Investigate while it is live, when evidence and options still exist.[21]

Non-goals. Maximising today’s CPM at the cost of trust; deciding whether a legal basis exists for personalised advertising (that is PrivacyOps); choosing audiences and creative strategy (that is MarketingOps).

Role Side Main responsibilities
Ad ops lead Either Operating model, standards, staffing, escalation, partners
Trafficker / campaign manager Either Orders, line items, creatives; pre-launch QA
Yield or programmatic manager Sell Floors, deals, bidders, auction analysis, supply-partner review
Ad tech engineer Sell Tags, wrapper, consent integration, page performance, ads.txt publishing
Sales planner / account manager Sell Avails and forecasts; client communication
Buy-side trafficker Buy Third-party ad server, tags, DSP line items, verification
Billing / finance Either Invoices, credits, make-goods, audit trail

The RACI below is a starting template for a publisher (R = responsible, A = accountable, C = consulted, I = informed).

Activity Ad ops lead Trafficker Yield mgr Ad tech eng. Sales / finance
IO intake and feasibility A R C I C (sales)
Trafficking and QA A R I C I
Daily pacing and fixes A R C I I
Floors, deals, bidder mix C I A/R C C (sales)
Wrapper and timeout changes A I C R I
ads.txt and sellers.json audit A I R R I
Consent-signal integration A I C R C (privacy owner)
IVT or malvertising response A R R R I
Discrepancy reconciliation A R C C R (finance)
Invoice approval C C I I A/R (finance)

Ad ops runs two lifecycles side by side: per campaign (weeks) and per partner (years).

Campaign stage Main output Exam domain in IAB’s 2017 study guide[19]
Intake and feasibility (avails, forecast) Accepted IO with complete specification Executing campaign launch
Trafficking Orders, line items, creatives, targeting, frequency caps Executing campaign launch
Pre-launch QA Signed QA checklist, test-page evidence Executing campaign launch
Launch and verify First-hour delivery and tracking confirmed Executing campaign launch
Monitor and optimise Daily pacing, fixes, client updates Managing live campaigns
Wrap and reconcile Final counts, discrepancy analysis, make-good decision Managing live campaigns
Bill and review Invoice, post-campaign report, lessons Managing live campaigns / Managing incidents

Partners follow their own cycle: evaluate (terms, data and consent handling, TAG status) → onboard (ads.txt line, adapter, staging test, sellers.json entry checked) → operate (monthly scorecard of bid rate, timeouts, IVT, revenue share) → review quarterly (keep, renegotiate, remove) → offboard (remove adapter, line items and ads.txt line; confirm no further requests). Keep the ads.txt diff and decision record at each step.

A workable cadence for a mid-sized publisher (an Atlas proposal; scale it up or down):

  • Daily (15 minutes): pacing outside 95–110%, zero-delivery line items, fill or eCPM jumps, wrapper errors and timeouts.
  • Weekly: yield review (floors, deals, bidders, latency); creative incidents; open discrepancies.
  • Monthly: billing close; partner scorecards; ads.txt and sellers.json audit (Lab 2).
  • Quarterly: supply-path review, contract renewals, maturity self-assessment.

Change control. Priorities, pricing rules, wrapper timeouts and ads.txt can each move site-wide revenue; give each change an approver, a ticket and a rollback step. Google warns that “Incorrectly configured pricing rules can sometimes lead to ad delivery issues, such as low fill rates”.[34]

Escalation. Tie severity to money and users: Sev 1 for site-wide loss of serving, malicious creatives or consent failure; Sev 2 for a guaranteed campaign that cannot meet its goal or a large revenue drop; Sev 3 for single-line-item faults. Incident mechanics in general are in IncidentOps.

Reference architecture: the publisher ad stack and its components

Link to section “Reference architecture: the publisher ad stack and its components”

The common open-web pattern for a publisher selling direct and programmatic. App and connected-TV (CTV) stacks swap the page for an SDK or video player but keep the layers.

Figure 2. Publisher ad stack: from page to paid impression
checks before biddingbids as key-valueswinning creativePage: consentplatform (TCF orGPP string)Page:header-biddingwrapper (Prebid.js)SSPs andexchangesDSPs bidding forbuyersPublic files: ads.txt,sellers.jsonPage: ad-servertag (e.g. GPT)Publisher adserver: priorities,floors, auctionVerification:viewability, IVT,brand safety
Diagram source (Mermaid)
%%{init: {"flowchart": {"wrappingWidth": 130, "nodeSpacing": 16, "rankSpacing": 24, "padding": 8}}}%%
flowchart TB
  CMP["Page: consent platform (TCF or GPP string)"]
  HB["Page: header-bidding wrapper (Prebid.js)"]
  SSP["SSPs and exchanges"]
  DSP["DSPs bidding for buyers"]
  TXT["Public files: ads.txt, sellers.json"]
  TAG["Page: ad-server tag (e.g. GPT)"]
  AS["Publisher ad server: priorities, floors, auction"]
  VER["Verification: viewability, IVT, brand safety"]
  CMP --> HB
  CMP --> TAG
  HB --> SSP
  SSP --> DSP
  DSP -. "checks before bidding" .-> TXT
  HB -- "bids as key-values" --> TAG
  TAG --> AS
  AS -- "winning creative" --> TAG
  TAG --> VER
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  class AS,HB core
  class CMP,TAG,SSP,DSP neutral
  class VER,TXT decision

Text equivalent: On the page, the consent platform feeds the consent string to the header-bidding wrapper and the ad-server tag. The wrapper asks SSPs, which ask DSPs, for bids, and passes them to the tag as key-values. The tag calls the publisher ad server, which returns the winning creative; verification services measure it. DSPs check ads.txt and sellers.json before trusting the inventory (dotted line).

Component Responsibility Trust boundary and trade-off
Consent platform Encodes the user’s choices for tags Wrong signals mean regulatory exposure and, for Google, limited ads[13]
Header-bidding wrapper Parallel bid requests, timeout, winners to the ad server Third-party code and latency; server-side bidding (Prebid Server, Open Bidding[23]) cuts page work
Ad-server tag Requests, renders and counts ads Runs creative code inside your page
SSP / exchange Packages impressions as OpenRTB bid requests Sees user data; may resell; more paths add competition and duplicates
Publisher ad server Chooses between guaranteed, remnant and auction demand; counts Holds commercial truth; the European Commission found Google’s DFP dominant[15]
Verification Independent viewability, IVT and brand-safety measurement Adds tags, latency and a second count to reconcile
Public files ads.txt (publisher domain), sellers.json (SSP domain) Public; stale entries mislead buyers
Figure 3. One web impression with header bidding, step by step
DSPSSP or exchangePage (CMP, Prebid, GPT)Ad serverread consent stringbid request (consent, floor, schain)OpenRTB request, tmaxbid price and creativebid (rounded later to a price bucket)ad request with hb_pb key-valuespriorities, floors, unified auctionwinning creativerender, count impression, fire trackers
Diagram source (Mermaid)
sequenceDiagram
  participant AS as Ad server
  participant P as Page (CMP, Prebid, GPT)
  participant S as SSP or exchange
  participant D as DSP
  P->>P: read consent string
  P->>S: bid request (consent, floor, schain)
  S->>D: OpenRTB request, tmax
  D-->>S: bid price and creative
  S-->>P: bid (rounded later to a price bucket)
  P->>AS: ad request with hb_pb key-values
  AS->>AS: priorities, floors, unified auction
  AS-->>P: winning creative
  P->>P: render, count impression, fire trackers

Text equivalent: Participants from left to right: ad server, page, SSP or exchange, DSP. The page reads the consent string and sends a bid request (consent, floor, schain) to the SSP, which sends an OpenRTB request with a time limit to the DSP. The bid returns through the SSP to the page, which calls the ad server with the price bucket as key-values. The ad server applies priorities, floors and its auction and returns the winner, which may not be the header bid. The page renders it, counts the impression and fires trackers.

Three details explain most surprises:

  • The ad server still decides. Prebid’s documentation is explicit: “Yes, you still need an ad server,” and “The ad server will still determine the final winner for each ad unit.”[1] Header bids enter as non-guaranteed line items matching key-values, a use Google’s line-item documentation names.[22]
  • Priorities come before price for guaranteed demand. In Google Ad Manager (as of October 2026), Sponsorship line items have priority 4 and Standard 6, 8 or 10; Network, Bulk, Price priority and Ad Exchange items have 12; House 16; “The lower the number, the higher the priority.”[22] When dynamic allocation is on, non-guaranteed demand can still compete with guaranteed line items in real time.[22]
  • Win is not bill. In OpenRTB the win notice is “not necessarily indicative of a delivered, viewed, or billable ad”; a separate billing notice fires when the impression becomes billable.[5] A DSP’s “wins” never match a publisher’s “impressions” exactly.

End-to-end workflow: from order to invoice

Link to section “End-to-end workflow: from order to invoice”
Figure 4. Order-to-invoice workflow with the main exception paths
noyesnoyesnoyes, flight endsnoyesIO or dealreceivedSpec completeand avails OK?Return to saleswith gapsTraffic line itemsand creativesQA passes ontest page?Fix tags, macros,targetingLaunch; verifyfirst hourPacing 95-110%and tracking OK?Diagnose:targeting, priority,creative,discrepancyGap with buyerwithin tolerance?Reconcile;make-good oradjusted invoiceInvoice oncontrolling count
Diagram source (Mermaid)
%%{init: {"flowchart": {"wrappingWidth": 120, "nodeSpacing": 14, "rankSpacing": 20, "padding": 8}}}%%
flowchart TB
  IO["IO or deal received"] --> FE{"Spec complete and avails OK?"}
  FE -- "no" --> BACK["Return to sales with gaps"]
  FE -- "yes" --> TR["Traffic line items and creatives"]
  TR --> QA{"QA passes on test page?"}
  QA -- "no" --> FIX["Fix tags, macros, targeting"]
  FIX --> QA
  QA -- "yes" --> GO["Launch; verify first hour"]
  GO --> MON{"Pacing 95-110% and tracking OK?"}
  MON -- "no" --> ACT["Diagnose: targeting, priority, creative, discrepancy"]
  ACT --> MON
  MON -- "yes, flight ends" --> REC{"Gap with buyer within tolerance?"}
  REC -- "no" --> DISP["Reconcile; make-good or adjusted invoice"]
  REC -- "yes" --> INV["Invoice on controlling count"]
  DISP --> INV
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
  class IO,TR,GO,INV core
  class FE,QA,MON,REC decision
  class BACK,FIX,ACT,DISP risk

Text equivalent: An incomplete IO or one without avails returns to sales. Otherwise it is trafficked and tested, looping through fixes until QA passes, then launched and checked in the first hour. Pacing outside 95–110% or broken tracking triggers diagnosis. After the flight, a gap outside tolerance leads to reconciliation and a make-good or adjusted invoice; otherwise the invoice uses the controlling count.

Happy path, with owners. Sales submits the IO through the intake form; the trafficker checks it within one business day, builds, QAs and launches; the daily stand-up watches pacing; finance invoices on the controlling count.

Exception paths that deserve a written procedure.

  1. Incomplete IO (missing sizes, dates, counting terms): do not traffic “to be safe”; return it with the gaps listed.
  2. Creative fails QA (non-secure calls, broken macros, wrong size, heavy file): reject with the evidence; never edit a buyer’s tag without written consent.
  3. Under-delivery forecast (the run rate needed exceeds what the targeting can supply): tell the client early with options (broaden targeting, extend dates, raise priority, make-good).
  4. Discrepancy above tolerance: follow the worked runbook.
  5. Suspected IVT or malvertising: pause the line item or creative first, then investigate (see quality, security, privacy and compliance).

Same three records, different plumbing: MediaOps packages the programme and carries the ad markers; AdOps decides what fills each break and how it is counted.

  • VAST and VMAP. VAST is IAB Tech Lab’s “universal XML schema for serving ads to digital video players”;[35] VMAP lets a content owner that does not control the player define the ad breaks (timing, number, ad types); “What VMAP cannot do is define the ads themselves”.[36]
  • Client-side insertion (CSAI). At a cue the player requests VAST, follows Wrapper responses to an InLine response, plays the ad and fires the trackers itself.[37]
  • Server-side insertion (SSAI, “ad stitching”). A stitching service requests VAST, transcodes new creatives from their mezzanine files and stitches ads into one continuous stream. A creative it has not yet transcoded is skipped (VAST error 407), and it recognises creatives by <UniversalAdId>, so a changed creative needs a new ID. Tracking sent by the server comes from one IP address and can “look similar to invalid traffic”, so stitchers must forward X-Device-IP and X-Device-User-Agent; impression reporting “may vary by implementation”, while the MRC guidelines quoted in VAST favour client-initiated counting.[37] OpenRTB 2.6 tells buyers which mode applies with imp.ssai (0 unknown, 1 all client-side, 2 assets stitched but trackers client-side, 3 all server-side).[5]
  • Ad pods. OpenRTB 2.6 sells a break as a pod: structured (fixed slots and durations), dynamic (total duration and maximum number of ads known) or hybrid. Key fields: podid, podseq, slotinpod, poddur, maxseq, rqddurs (exact durations, to avoid “dead air” in live TV) and mincpmpersec, a floor per second for the dynamic part. The seller builds the final pod and may use none, some or all of a buyer’s bids.[5][38]
  • Measurement and trust. The Open Measurement SDK gives verification vendors one integration in apps and CTV;[39] MRC asks SIVT vendors to check spoofed CTV bundle IDs;[25] app-ads.txt also covers “connected television app stores”,[40] and the INVENTORYPARTNERDOMAIN variable is “expected to be used primarily for CTV/OTT inventory, where ‘inventory sharing’ is common”.[41]

Operational checks (an Atlas list): deliver creatives early enough to be transcoded; agree with the verification vendor the ssai mode and headers; set pod floors per second; write into the IO whether impressions count at the stitcher or player.

The programmatic supply chain and its trust mechanisms

Link to section “The programmatic supply chain and its trust mechanisms”

This part is the centre of the page. Programmatic buying lets software buy from sellers the buyer has never met, through intermediaries the publisher may not know. Deal terms, the public seller files, the SupplyChain object and the consent string make that chain checkable; invalid-traffic filtering catches what they miss.

Deal types: how much is agreed before the auction

Link to section “Deal types: how much is agreed before the auction”
Deal type Price Volume Who can buy How it appears in OpenRTB
Programmatic guaranteed (automated guaranteed) Fixed Guaranteed One buyer Deal ID; fixed deal price (deal at = 3)
Preferred deal Fixed Not guaranteed One buyer Deal ID; fixed deal price (deal at = 3)
Private marketplace (PMP) Auction Not guaranteed Invited buyers only Deal IDs in imp.pmp
Open auction Auction Not guaranteed Any buyer the exchange admits No deal ID; at 1 = first price, 2 = second price plus[5]

In OpenRTB, a deal’s at = 3 means its bidfloor “is the agreed upon deal price”, which fits both fixed-price rows.[5] Definitions follow the IAB/PwC revenue report (preferred deals: “pricing is guaranteed but inventory volume is not”).[3] The further down the table, the more a buyer depends on the supply-chain files below.

ads.txt and app-ads.txt: who may sell this inventory

Link to section “ads.txt and app-ads.txt: who may sell this inventory”

ads.txt (“Authorized Digital Sellers”) is a plain-text file at /ads.txt on a publisher’s root domain in which the publisher lists every advertising system and account that may sell its inventory.[26] Its mission, in IAB Tech Lab’s words, is to “Increase transparency in the programmatic advertising ecosystem”.[11] app-ads.txt applies the same idea to apps, “linking app store listings to app developer websites”: the buyer finds the file through the developer URL in the app store listing.[40]

Each record has three required fields and one optional field:[26]

# <system domain>, <account ID>, <DIRECT|RESELLER>, <optional cert ID>
ssp-one.example, 1001, DIRECT, abc123
ssp-one.example, 2002, RESELLER
OWNERDOMAIN=news.example
MANAGERDOMAIN=yieldpartner.example
CONTACT=adops@news.example
  • DIRECT means the publisher “directly controls the account” on that system; RESELLER means the publisher “has authorized another entity to control the account” and resell its space.[26]
  • The account ID “must contain the same value used in transactions”, typically publisher.id in OpenRTB.[26]
  • OWNERDOMAIN (recommended since version 1.1, finalised July 2022 and listed by IAB Tech Lab as August 2022) names the business that owns the site and should equal the domain that publisher has in sellers.json files. MANAGERDOMAIN (optional) names an exclusive or primary monetisation partner such as a sales house, at most one per country; for impressions the manager sells, the specification expects its domain on the first node of a complete SupplyChain object.[26][11]
  • The file must be served as text/plain. Redirects are followed only within the root domain plus one hop to a third party; consumers should cache for 7 days if no cache headers are set.[26]
  • On a 404, “the advertising system can assume no declarations exist and that no advertising system is unauthorized”, so a missing file authorises everyone; after any other error, crawlers keep the last file they retrieved successfully.[26]
  • A site that authorises nobody publishes a placeholder record rather than an empty file, so crawlers can tell it apart from an error page.[26]

Why it matters, in one number. Google and White Ops reported that 80.3% of the bid requests the 3ve fraud operation generated in early 2018 came from sellers not listed in the spoofed domain’s ads.txt; with universal enforcement, they wrote, over 80% of 3ve’s inventory “would be completely blocked” (see Scenario 1).[42]

What ads.txt cannot do. It does not name the account holder (sellers.json does), it does not list every intermediary (the SupplyChain object does), and it cannot stop bot traffic sold through a real, authorised account.

sellers.json and the SupplyChain object: who was paid along the way

Link to section “sellers.json and the SupplyChain object: who was paid along the way”

sellers.json is published by an SSP or exchange at /sellers.json on its own domain. For every seller_id it says who is paid and in what capacity:[27]

  • seller_type PUBLISHER: the named entity owns the site or app and is paid directly; INTERMEDIARY: it does not own the inventory or is not paid directly; BOTH: both kinds flow through the account.
  • seller_id is “the same ID that appears in an ads.txt file and in the SupplyChain.nodes array sid property”; each ID must map to a single paid entity; is_confidential may hide the name.

The SupplyChain object (often “schain”) travels inside each bid request and lists the entities “involved in the direct flow of payment” for that impression.[28] The rules are simple:

  • The originating seller creates it with complete = 1 and itself as the only node.
  • A reseller copies the object, keeps complete as it was, and appends its own node; a reseller that receives no object creates one with complete = 0.
  • In a complete chain, the first node is the owner’s system and seller ID, and the last node is the entity sending this request.
  • It sits in source.schain in OpenRTB 2.6 and later, source.ext.schain in 2.5.[28]

Both were finalised on 31 July 2019; IAB Tech Lab tells buyers to keep buying through ads.txt-authorised paths.[12]

Figure 5. How a buyer checks one bid request against the public files
noyesnoyesnoyesnoyesBid request:domain or app,publisher.id,schainSeller accountlisted in thedomain's ads.txt?Unauthorised: donot bidschain complete= 1?Incomplete chain:lower trust or skipEvery node foundin that system'ssellers.json?Unknownintermediary: skipor investigateFirst node =OWNER- orMANAGERDOMAIN?Resold ormislabelled: lowerpriority or reviewVerifiable path:bid on its merits
Diagram source (Mermaid)
%%{init: {"flowchart": {"wrappingWidth": 120, "nodeSpacing": 14, "rankSpacing": 20, "padding": 8}}}%%
flowchart TB
  BR["Bid request: domain or app, publisher.id, schain"] --> A1{"Seller account listed in the domain's ads.txt?"}
  A1 -- "no" --> X1["Unauthorised: do not bid"]
  A1 -- "yes" --> A2{"schain complete = 1?"}
  A2 -- "no" --> X2["Incomplete chain: lower trust or skip"]
  A2 -- "yes" --> A3{"Every node found in that system's sellers.json?"}
  A3 -- "no" --> X3["Unknown intermediary: skip or investigate"]
  A3 -- "yes" --> A4{"First node = OWNER- or MANAGERDOMAIN?"}
  A4 -- "no" --> X4["Resold or mislabelled: lower priority or review"]
  A4 -- "yes" --> OK["Verifiable path: bid on its merits"]
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
  class BR,OK core
  class A1,A2,A3,A4 decision
  class X1,X2,X3,X4 risk

Text equivalent: Four checks on a bid request. Is the seller account in the domain’s ads.txt? If not, do not bid. Is the chain complete? If not, trust is lower. Is every node in its system’s sellers.json? If not, an unknown intermediary is involved. Does the first node’s sellers.json domain equal the publisher’s OWNERDOMAIN, or the MANAGERDOMAIN declared for the user’s country? If not, treat the path as resold or mislabelled: lower its priority or review it. If all pass, the path is verifiable and the buyer bids on the impression’s merits.

The fourth check combines two rules in ads.txt v1.1. In complete chains the first node “should have sellers.domain that matches the OWNERDOMAIN”; but for “ad opportunities monetized by the manager”, the specification expects the MANAGERDOMAIN to be that of the node representing the originating publisher.[26] The 1.1 implementation guide turns this into a rule of thumb for the seller’s sellers.json domain: equal to OWNERDOMAIN, the seller is the owner; equal to MANAGERDOMAIN (for that country), the manager; anything else, a reseller. It also leaves the exact interpretation of MANAGERDOMAIN to each buyer.[41] Buyers apply these checks with different strictness, so publishers should make all four pass for the paths they rely on.

Link to section “Consent and privacy signals on ad requests”
  • IAB Europe’s Transparency and Consent Framework (TCF) is “an accountability tool that relies on standardisation to facilitate compliance with certain provisions of the ePrivacy Directive and the GDPR”. Version 2.3 (April 2025) made the “Disclosed Vendors” section mandatory; “TCF participants have until 28 February 2026 to adopt TCF v2.3”.[29]
  • Global Privacy Protocol (GPP), from IAB Tech Lab, carries IAB Europe TCF, IAB Canada TCF, US national and US state strings in one container; an MSPA-alignment update was in public comment until 11 September 2026, so check its final status.[30] OpenRTB 2.6 carries them in regs.gdpr and regs.gpp.[5]
  • Google’s requirement. AdSense, Ad Manager and AdMob publishers need a Google-certified CMP integrated with the TCF to serve personalised ads in the EEA and UK (from 16 January 2024) and Switzerland (from 31 July 2024); other traffic may get only non-personalised or limited ads. Google “does not check CMPs for full compliance with the TCF or applicable privacy laws”.[13]
  • Header bidding. Prebid splits the job. Its TCF consent management module reads the consent data and passes it to adapters (and is not legal advice).[43] Enforcement needs the separate, optional TCF control module (tcfControl, in the build), which “Actively restricts” user syncs, ID modules, bidders and other activities according to consent, requiring a legal basis for Purposes 1 and 2 by default.[32] The consent module’s page names TCF v2.0 and v2.2; check that your CMP and Prebid version handle the v2.3 strings users now send.
  • Children. Google does not send requests marked child-directed under the US COPPA law to Open Bidding partners;[23] check each partner’s handling of the flag.

The legal analysis (lawful basis, notices, retention) belongs to PrivacyOps; the CMP and tag-firing architecture to MarTechOps.

The Media Rating Council (MRC) separates invalid traffic (IVT) into two categories:[25]

  • General invalid traffic (GIVT): “traffic identified through routine means of filtration executed through application of lists or with other standardized parameter checks”, such as known invalid data-centre traffic and declared bots and spiders.
  • Sophisticated invalid traffic (SIVT): “more difficult to detect situations that require advanced analytics, multi-point corroboration/coordination, significant human intervention”, including domain and app misrepresentation and domain laundering; an April 2024 MRC memo adds checks of declared against actual property IDs, including CTV bundle IDs.[25]

Controls by layer:

Layer Control Source
Lists (GIVT) IAB Tech Lab Spiders and Robots list, used in a “dual pass” (valid browsers, then known robots); TAG’s monthly Data Center IP List [44][10]
Certification TAG Certified Against Fraud: 100% of monetisable transactions filtered for IVT by a qualified third party; publishers may meet this through a CAF-certified SSP [10]
Supply-chain files ads.txt / app-ads.txt enforcement, sellers.json and schain checks [26][27][28]
Measurement Accredited IVT detection and filtration; SIVT checks compare declared property IDs with where ads actually ran [25]
Behavioural heuristics Google and White Ops: if the fraud rate falls but click-through rate does not, “it’s likely that your solution isn’t quite working correctly” [42]

Counting rules: impressions and viewability

Link to section “Counting rules: impressions and viewability”
  • Impression. The IAB/MRC display guideline requires counting “as late as possible”: “The ad must be loaded and at minimum begin to render”; “count on decision” at the server is not acceptable, and the ad need not be visible.[8] Google Ad Manager states that it “counts an impression each time a creative is downloaded and has begun to load”.[45]
  • Viewable display impression. At least 50% of the ad’s pixels on an in-focus browser tab in the viewable space, for at least one continuous second after render; for ads of 242,500 pixels or more (970×250 and larger), 30% of pixels.[6]
  • Viewable video impression. 50% of pixels and 2 continuous seconds of play.[6]
  • Reporting. Disclose viewable, non-viewable and undetermined, and compute measured and viewable rates separately (metrics); MRC warns against calling the viewable share of all impressions the “viewable rate”.[6] The 2015 guideline is for desktop; MRC’s mobile guidance followed in June 2016.[7]
Practice Goal Trigger Owner Output Verification
Trafficking QA Ads render, track and target as sold Launch; creative swap Trafficker QA record Test-page evidence
Pacing management Goals met without free over-delivery Daily Trafficker Exceptions list 95–110% at flight end
Header-bidding tuning Competition without latency loss Monthly; new partner Yield mgr + engineer Timeout, bidder settings Revenue per session
Floors and yield Price without starving fill Weekly Yield mgr Pricing-rule notes Revenue per request before/after
ads.txt hygiene Only current, correctly labelled sellers Partner change; monthly Yield mgr + engineer Versioned ads.txt Lab 2 check passes
Discrepancy control Counts agree within tolerance Daily while live Trafficker + finance Discrepancy log Gap within tolerance
Creative security No malicious or non-compliant ads New creative; alerts Trafficker + SecOps Blocks, partner reports No repeat offenders
Ad experience Ads readers tolerate Layout change; quarterly Ad ops lead + product Ad-density rules Page speed, complaints
  1. Compare line items to the IO line by line: dates and time zone, goal and type, rate, targeting, frequency cap, sizes, competitive separation.
  2. Preview each creative: right size, secure (HTTPS) calls, correct click-through URL.
  3. Confirm every tracker fires once (impression, click, viewability, verification); a click tracker without an impression tracker causes “clicks but no impressions”.[45]
  4. Use the ad server’s matching creative type for third-party ad-server tags (in Google Ad Manager, the Campaign Manager 360 tag type: under 2% discrepancy expected instead of up to 20%).[21]
  5. Record the evidence, launch, and re-check delivery and trackers within the first hour.

Lab 1 computes pacing and the run rate needed. For Standard line items “Delivery pacing is adjusted to meet the defined goal”, Google notes,[22] but no ad server creates inventory the targeting excludes. When a line item is behind:

  1. Check eligibility (status, dates, approved creatives, targeting that matches real inventory) with the Troubleshoot tab and Delivery Inspector.[46][45]
  2. Check competition: higher-priority line items or sponsorships on the same inventory; over-tight frequency caps.
  3. Check the creative: a third-party creative that fails to load may count on one side only.
  4. Decide with the account manager: widen targeting, extend the flight, raise priority within policy, or plan a make-good.

Playbook: header-bidding set-up and tuning

Link to section “Playbook: header-bidding set-up and tuning”
  • Line items. Each price bucket needs a matching line item. Prebid rounds bids down into buckets passed in hb_pb; rounding affects only the ad-server auction, not the price paid. “Dense” needs 425 line items per bidder, “high” 2,001. For custom buckets, set the cap near your 95th-percentile bid, with finer increments where bids cluster.[47]
  • Send all bids or top bid. Sending all bids is Prebid’s default and helps reporting on every bidder; sending only the top bid needs far fewer line items.[48]
  • Timeouts. Prebid calls the auction timeout “a delicate balance”: too short loses bids, too long loses users before the ad call. It suggests a server-side timeout of about 50–75% of the auction timeout and measuring how long consent and identity modules take before bids are sent.[49] Consent waits count: with actionTimeout set, Prebid waits up to that long for a new user’s choice before the auction starts.[43]
  • Supply chain. If a managed-service provider runs your wrapper and handles payments, it must add its own schain node; since Prebid 10 the schain is treated as first-party data in ortb2.source.ext.schain.[50]

In Google Ad Manager (as of October 2026), pricing rules centralise floors for non-guaranteed demand, the higher of two overlapping floors applies, floors apply to the publisher’s net after Google’s share, and publishers “can set floors for available demand sources or specific programmatic bidders”.[34] Change one rule at a time on a slice of inventory and compare revenue per thousand requests, not eCPM alone (see the worked calculation). Expect floor behaviour to change when the US remedies take effect (see advanced topics).

Source Body Kind What it governs
ads.txt 1.1, app-ads.txt IAB Tech Lab Specification (voluntary; enforced by many platforms) Authorised sellers[26][40]
sellers.json 1.0, SupplyChain object 1.0 IAB Tech Lab Technical specification Seller identity; payment chain[27][28]
OpenRTB 2.6 IAB Tech Lab Protocol specification Bid requests and responses, notices, deals[5]
VAST 4.x, Open Measurement SDK IAB Tech Lab Specification / SDK Video ad serving; measurement in apps and CTV[35][39]
Impression, viewability, IVT, brand-safety guidelines MRC with IAB Measurement guidelines; basis of accreditation What counts, is viewable, is invalid, is safe[8][6][25][51]
Certified Against Fraud TAG Certification programme Anti-fraud controls[10]
TCF v2.3 IAB Europe Framework (Google requires it for personalised ads in EEA/UK/CH) Consent signalling[29][13]
GPP IAB Tech Lab Protocol Multi-jurisdiction privacy strings[30]
Standard Terms and Conditions v3.0 4A’s and IAB Contract template (2010; often renegotiated) Controlling counts, discrepancies, make-goods[31]
Better Ads Standards Coalition for Better Ads Industry standard based on consumer research Ad experiences users reject[33]
Digital Ad Operations Certification IAB Professional certification Individual competence[18]

None of these is law; laws apply on their own terms, and frameworks help show compliance without guaranteeing it.[13]

Tool category Examples (named in this page’s sources) Selection criteria Lock-in and risks
Publisher ad server Google Ad Manager[22] Scale, programmatic integrations, reporting API, video Deep lock-in; auction rules set by vendor and regulators[15]
Buy-side ad server Campaign Manager 360[21] DSP and verification integration Counts differ from publishers’
Header-bidding wrapper Prebid.js, Prebid Server[9] Adapters, consent and schain modules, build control Page weight; upgrades
SSP / exchange, DSP Many vendors; check each SSP’s sellers.json Demand quality, fees, IVT controls, TAG status, path enforcement Duplicate paths; opaque fees
Verification MRC-accredited vendors Accreditation scope (display, video, app, CTV) Extra tags; a second count
Consent management Google’s certified CMP list[13] TCF/GPP version support, performance Signal errors block demand
Supply-chain checks IAB Tech Lab’s open-source crawler, which it calls “only an example prototype crawler”, and parser[11]; Lab 2 Coverage, alerting Prototypes and unmaintained scripts give false comfort
Data and reporting Governed metrics (see AnalyticsOps) Log-level access, freshness Metric drift between teams

Automation that pays for itself. Bulk header-bidding line items (Lab 4); scheduled ads.txt and sellers.json checks (Lab 2); daily pacing and discrepancy exceptions (Labs 1 and 3); alerts on jumps in fill, eCPM, timeouts or IVT; wrapper configuration released like code (ReleaseOps).

Guard-rails. Automation that writes to the ad server can change site-wide revenue in one run: require a dry run, a cap on objects changed, an approver for priority and pricing changes, and a tested rollback.

Where language models help, with limits. Drafting a trafficking ticket from an emailed IO (a human checks every field); summarising delivery anomalies; sorting creatives for review. Not approving creatives, changing floors or settling discrepancies without a human. Standards for AI agents that trade inventory are emerging (advanced topics).

Reference implementation: operating artefacts

Link to section “Reference implementation: operating artefacts”

For ad ops the reference implementation is a set of operating artefacts plus a little configuration, assuming Google Ad Manager and Prebid.js; the ideas carry to other ad servers.

1. A naming convention that makes reports and bulk changes safe:

Order:      <Advertiser>_<Agency>_<IO number>_<YYYYMM>
Line item:  <IO line>_<Type>_<Format>_<Size>_<Targeting>_<Start YYYYMMDD>
            0420-03_STD_DISPLAY_300x250_SPORTS-UK_20261101
Header bid: HB_<bidder|ALL>_<bucket>      e.g. HB_ALL_02.35
Creative:   <Advertiser>_<Concept>_<Size>_<v#>

2. A trafficking intake form that refuses incomplete orders. Keep it as structured data so a script can check it.

Example status: syntax-checked (YAML), not executed against an ad server.

io_number: "IO-2026-0420"
advertiser: "Example Outdoor Ltd"
agency: "Example Media Agency"
flight: {start: "2026-11-01", end: "2026-11-30", time_zone: "Europe/London"}
lines:
  - line: "0420-03"
    type: standard            # sponsorship | standard | price_priority | pg_deal
    goal_impressions: 1500000
    rate_cpm: 18.00
    sizes: ["300x250", "320x50"]
    targeting: {section: "sports", geo: ["GB"]}
    frequency_cap: {impressions: 3, per: "day"}
measurement:
  controlling_count: "third_party"   # per the IO terms
  third_party_ad_server: "Campaign Manager 360"
  discrepancy_tolerance_pct: 10
  viewability_vendor: "MRC-accredited vendor named in IO"
creative_due: "2026-10-27"
contacts: {sales: "sales@publisher.example", agency: "trafficking@agency.example"}

3. A wrapper configuration reviewed like code. Values are placeholders; the keys follow Prebid’s documented setConfig options (bidderTimeout, priceGranularity, consentManagement, first-party ortb2 data carrying the schain), which are unchanged in the Prebid.js 10 and 11 documentation (11 is the current major release as of October 2026; 10 is maintained as “legacy”).[52][43][50][53] The configuration passes consent; Prebid enforces it only if the build includes tcfControl (for example gulp build --modules=consentManagementTcf,tcfControl,<bidders>).[32] Prebid 11 also adds a pre-auction wait for viewability measurement (auctionOptions.viewabilityMeasurement); include it in latency tests.[52]

Example status: syntax-checked with node --check, not executed in a browser (requires a page with Prebid.js and an ad server).

// Prebid.js 10/11 configuration sketch for a wrapper run by a managed-service provider.
// Signal passing only: enforcement needs the tcfControl module in the build.
const pbjs = (globalThis.pbjs = globalThis.pbjs || { que: [] });
pbjs.que.push(() => {
  pbjs.setConfig({
    bidderTimeout: 1000, // measure first; see the timeouts playbook
    priceGranularity: 'dense', // 425 line items per bidder in the ad server
    consentManagement: {
      // timeout: wait for the CMP's first answer. No actionTimeout here: with one
      // (Prebid's example: 10000), the auction waits that long for a new user's choice.
      gdpr: { cmpApi: 'iab', timeout: 500, defaultGdprScope: true },
    },
    ortb2: {
      source: {
        ext: {
          schain: {
            ver: '1.0',
            complete: 1,
            nodes: [{ asi: 'wrapper-provider.example', sid: 'PUB-123', hp: 1 }],
          },
        },
      },
    },
  });
});

4. A one-page QA checklist and a discrepancy log (see Checklists and Templates).

5. A supply-partner register: partner, ads.txt lines, sellers.json seller ID and type, contract owner, TAG status, last review, decision; the source for ads.txt and the quarterly review.

Four labs on synthetic data: Python 3.8 or later and a bash shell, standard library only, no accounts, no cost, no network access. Domains end in .example, which is reserved for documentation. Each lab was executed on 2026-10-23 exactly as printed (Python 3.8.10, Ubuntu Linux); the expected outputs are copied from those runs.

Objective. Calculate pacing, run rates, eCPM, CTR and the two MRC viewability rates for one guaranteed line item. Prerequisites. Python 3.8+. Time. 15 minutes. Risk. None.

  1. Create a folder: mkdir -p ~/adops-labs/lab1 && cd ~/adops-labs/lab1
  2. Save the script as pacing.py.

Example status: verified by execution on 2026-10-23 (Python 3.8.10).

"""Media math and pacing for one guaranteed line item (standard library only)."""
from datetime import date

GOAL = 1_500_000            # impressions sold on the insertion order
START, END = date(2026, 11, 1), date(2026, 11, 30)
TODAY = date(2026, 11, 13)  # report run in the morning: 12 full days delivered
DELIVERED = 520_000         # impressions counted by the ad server so far
CLICKS = 1_040
REVENUE = 9_360.00          # booked value of the delivered impressions (USD)
VIEWABLE, NON_VIEWABLE, UNDETERMINED = 286_000, 156_000, 78_000

flight_days = (END - START).days + 1
days_done = (TODAY - START).days
expected = GOAL * days_done / flight_days
pacing = DELIVERED / expected
remaining_days = flight_days - days_done
run_rate_needed = (GOAL - DELIVERED) / remaining_days
run_rate_so_far = DELIVERED / days_done

measured = VIEWABLE + NON_VIEWABLE
print(f"Flight {flight_days} days, {days_done} done, {remaining_days} left")
print(f"Expected to date      = {expected:,.0f} impressions")
print(f"Pacing                = {pacing:.1%}  (100% = on schedule)")
print(f"Daily run rate so far = {run_rate_so_far:,.0f}")
print(f"Daily run rate needed = {run_rate_needed:,.0f}")
print(f"eCPM                  = ${REVENUE / DELIVERED * 1000:.2f}")
print(f"CTR                   = {CLICKS / DELIVERED:.2%}")
print(f"Measured rate         = {measured / DELIVERED:.1%}")
print(f"Viewable rate         = {VIEWABLE / measured:.1%}")
print(f"Viewable share of all = {VIEWABLE / DELIVERED:.1%}  (not the viewable rate)")
  1. Run python3 pacing.py.

Expected output.

Flight 30 days, 12 done, 18 left
Expected to date      = 600,000 impressions
Pacing                = 86.7%  (100% = on schedule)
Daily run rate so far = 43,333
Daily run rate needed = 54,444
eCPM                  = $18.00
CTR                   = 0.20%
Measured rate         = 85.0%
Viewable rate         = 64.7%
Viewable share of all = 55.0%  (not the viewable rate)

Validation. By hand: 1,500,000 × 12 ÷ 30 = 600,000 expected; 520,000 ÷ 600,000 = 86.7%; (1,500,000 − 520,000) ÷ 18 = 54,444 per day, 26% above the current 43,333. Measured and viewable rates follow MRC’s formulas; reporting 55.0% as “the viewable rate” is the error MRC warns against.[6] A 26% run-rate increase rarely happens by itself: start the pacing playbook. Troubleshooting. Wrong pacing when run in the evening: decide whether TODAY counts as a delivered day and keep the convention. Teardown. rm -rf ~/adops-labs/lab1

Lab 2 (intermediate): lint ads.txt and cross-check sellers.json

Link to section “Lab 2 (intermediate): lint ads.txt and cross-check sellers.json”

Objective. Find syntax errors in an ads.txt file and relationship mismatches against SSPs’ sellers.json entries. Prerequisites. Python 3.8+, Lab 1 folder conventions. Time. 30 minutes. Risk. None; in real use, fetch only public files and respect each site’s crawl policies.

  1. Create the folder and the two input files (an ads.txt and a combined extract of three SSPs’ sellers.json files).

Example status: verified by execution on 2026-10-23 (bash, Linux).

mkdir -p ~/adops-labs/lab2 && cd ~/adops-labs/lab2
cat > ads.txt <<'EOF'
# ads.txt for news.example (synthetic lab data)
ssp-one.example, 1001, DIRECT, abc123
ssp-one.example, 2002, RESELLER
ssp-two.example, pub-77, DIRECT
ssp-two.example, pub-77, DIRECT
ssp-three.example, 9, PARTNER
reseller-x.example 55 RESELLER
ssp-four.example, 404, RESELLER
OWNERDOMAIN=news.example
CONTACT=adops@news.example
EOF
cat > sellers.json <<'EOF'
{
  "ssp-one.example": {"sellers": [
    {"seller_id": "1001", "seller_type": "PUBLISHER", "name": "News Example Ltd",
     "domain": "news.example"},
    {"seller_id": "2002", "seller_type": "INTERMEDIARY", "name": "Yield Partner Inc",
     "domain": "yieldpartner.example"}]},
  "ssp-two.example": {"sellers": [
    {"seller_id": "pub-77", "seller_type": "INTERMEDIARY", "name": "Yield Partner Inc",
     "domain": "yieldpartner.example"}]},
  "ssp-four.example": {"sellers": []}
}
EOF
  1. Save the checker as check_adstxt.py.

Example status: verified by execution on 2026-10-23 (Python 3.8.10).

"""Lint an ads.txt file and cross-check it against sellers.json extracts (stdlib only)."""
import json
import re

VALID_REL = {"DIRECT", "RESELLER"}
VAR = re.compile(r"^\s*([A-Za-z]+)\s*=\s*(.+?)\s*$")

def parse(path):
    records, variables, problems, seen = [], {}, [], set()
    with open(path, encoding="utf-8") as f:
        for n, raw in enumerate(f, 1):
            line = raw.split("#", 1)[0].strip()
            if not line:
                continue
            m = VAR.match(line)
            if m and "," not in line:
                variables.setdefault(m.group(1).upper(), []).append(m.group(2))
                continue
            fields = [x.strip() for x in line.split(",")]
            if len(fields) < 3:
                problems.append(f"line {n}: expected 3-4 comma-separated fields")
                continue
            domain, account, rel = fields[0].lower(), fields[1], fields[2].upper()
            if rel not in VALID_REL:
                problems.append(f"line {n}: relationship {fields[2]!r} is not "
                                "DIRECT or RESELLER")
                continue
            key = (domain, account, rel)
            if key in seen:
                problems.append(f"line {n}: duplicate record {domain}, {account}, {rel}")
                continue
            seen.add(key)
            records.append((n, domain, account, rel))
    return records, variables, problems

def cross_check(records, owner, sellers_by_system):
    findings = []
    for n, domain, account, rel in records:
        system = sellers_by_system.get(domain)
        if system is None:
            findings.append(f"line {n}: no sellers.json collected for {domain}")
            continue
        seller = next((s for s in system["sellers"] if s["seller_id"] == account), None)
        if seller is None:
            findings.append(f"line {n}: seller_id {account} not in {domain}/sellers.json")
            continue
        stype = seller["seller_type"].upper()
        if rel == "DIRECT" and (stype == "INTERMEDIARY" or seller.get("domain") != owner):
            findings.append(f"line {n}: DIRECT but sellers.json has {stype} "
                            f"{seller.get('domain')}")
        if rel == "RESELLER" and stype == "PUBLISHER":
            findings.append(f"line {n}: RESELLER but sellers.json says PUBLISHER")
    return findings

records, variables, problems = parse("ads.txt")
owner = (variables.get("OWNERDOMAIN") or ["(missing)"])[0].lower()
with open("sellers.json", encoding="utf-8") as f:
    sellers = json.load(f)
print(f"{len(records)} valid records; OWNERDOMAIN={owner}")
for p in problems:
    print("SYNTAX ", p)
for c in cross_check(records, owner, sellers):
    print("CHECK  ", c)
  1. Run python3 check_adstxt.py.

Expected output.

4 valid records; OWNERDOMAIN=news.example
SYNTAX  line 5: duplicate record ssp-two.example, pub-77, DIRECT
SYNTAX  line 6: relationship 'PARTNER' is not DIRECT or RESELLER
SYNTAX  line 7: expected 3-4 comma-separated fields
CHECK   line 4: DIRECT but sellers.json has INTERMEDIARY yieldpartner.example
CHECK   line 8: seller_id 404 not in ssp-four.example/sellers.json

Validation. Line 4 matters most: the publisher claims direct control, but the SSP says a yield partner is paid, so the line is probably RESELLER (or the SSP’s record is wrong; ask). Line 8 names an account the SSP does not list. Only DIRECT and RESELLER are defined today (“Other types may be added in the future”), so line 6 is invalid.[26] The specification does not address duplicate records; they signal unmanaged edits. Extension. In real use, fetch each SSP’s live /sellers.json, cache it and alert on changes. Troubleshooting. JSONDecodeError: re-create the file; a quotation mark was lost. Teardown. rm -rf ~/adops-labs/lab2

Lab 3 (intermediate): reconcile a discrepancy before invoicing

Link to section “Lab 3 (intermediate): reconcile a discrepancy before invoicing”

Objective. Compare a publisher’s counts with the agency’s third-party counts day by day and apply a 10% tolerance clause in the style of the 4A’s/IAB Standard Terms and Conditions v3.0.[31] Prerequisites. Python 3.8+. Time. 20 minutes. Risk. None; the script illustrates a clause, and your IO decides.

  1. Create the folder and the data.

Example status: verified by execution on 2026-10-23 (bash, Linux).

mkdir -p ~/adops-labs/lab3 && cd ~/adops-labs/lab3
cat > delivery.csv <<'EOF'
date,publisher_imps,thirdparty_imps
2026-11-01,51000,47900
2026-11-02,50500,47300
2026-11-03,52000,44100
2026-11-04,49800,41200
2026-11-05,50700,42000
2026-11-06,51200,47600
2026-11-07,50300,46900
EOF
  1. Save the script as reconcile.py.

Example status: verified by execution on 2026-10-23 (Python 3.8.10).

"""Discrepancy check in the style of the 4A's/IAB Standard Terms v3.0 (illustrative)."""
import csv

CPM = 12.00          # agreed rate, USD
THRESHOLD = 0.10     # 10% over the invoice period, per the T&Cs pattern
CONTROLLING = "thirdparty_imps"   # assumed: agency's ad server is the controlling count
OTHER = "publisher_imps"

with open("delivery.csv", newline="") as f:
    rows = list(csv.DictReader(f))

for r in rows:
    ctrl, other = int(r[CONTROLLING]), int(r[OTHER])
    gap = (other - ctrl) / other
    flag = "  <-- investigate" if gap > THRESHOLD else ""
    print(f"{r['date']}  pub={other:>6}  3p={ctrl:>6}  gap={gap:6.1%}{flag}")

ctrl_total = sum(int(r[CONTROLLING]) for r in rows)
other_total = sum(int(r[OTHER]) for r in rows)
period_gap = (other_total - ctrl_total) / other_total
print(f"Period: publisher={other_total:,} third-party={ctrl_total:,} "
      f"gap={period_gap:.1%}")
if period_gap > THRESHOLD:
    adjusted = round(ctrl_total * (1 + THRESHOLD))
    print(f"Over {THRESHOLD:.0%}: reconcile first. If unresolved, the agency may")
    print("treat it as under-delivery (make-good) or pay the controlling")
    print("count plus a 10% upward adjustment:")
    print(f"  invoice on {adjusted:,} imps = ${adjusted / 1000 * CPM:,.2f}")
else:
    print(f"Within {THRESHOLD:.0%}: invoice on controlling count "
          f"{ctrl_total:,} imps = ${ctrl_total / 1000 * CPM:,.2f}")
  1. Run python3 reconcile.py.

Expected output.

2026-11-01  pub= 51000  3p= 47900  gap=  6.1%
2026-11-02  pub= 50500  3p= 47300  gap=  6.3%
2026-11-03  pub= 52000  3p= 44100  gap= 15.2%  <-- investigate
2026-11-04  pub= 49800  3p= 41200  gap= 17.3%  <-- investigate
2026-11-05  pub= 50700  3p= 42000  gap= 17.2%  <-- investigate
2026-11-06  pub= 51200  3p= 47600  gap=  7.0%
2026-11-07  pub= 50300  3p= 46900  gap=  6.8%
Period: publisher=355,500 third-party=317,000 gap=10.8%
Over 10%: reconcile first. If unresolved, the agency may
treat it as under-delivery (make-good) or pay the controlling
count plus a 10% upward adjustment:
  invoice on 348,700 imps = $4,184.40

Validation. Three consecutive days jump from about 6–7% to 15–17%: something ran only on 3–5 November (a creative, a placement, a tag edit). A detail the template leaves open: the clause compares “the Controlling Measurement and the other measurement” without naming the denominator; the script divides by the publisher’s count (10.8%), while dividing by the controlling count gives 38,500 ÷ 317,000 = 12.1%. Write the formula into your IO. The baseline is a finding too: a steady 6–7% is far above Google’s expectation for the Campaign Manager 360 tag type (QA playbook step 4), so it gets its own ticket (runbook step 8). Troubleshooting. KeyError: keep the CSV column names. Teardown. rm -rf ~/adops-labs/lab3

Lab 4 (advanced): price buckets, line-item counts and a SupplyChain check

Link to section “Lab 4 (advanced): price buckets, line-item counts and a SupplyChain check”

Objective. Reproduce Prebid’s rounding of bids into price buckets, count the line items a granularity needs, and check SupplyChain objects for structural errors. Prerequisites. Python 3.8+; Labs 2 and 3. Time. 30 minutes. Risk. None.

  1. Create the folder: mkdir -p ~/adops-labs/lab4 && cd ~/adops-labs/lab4
  2. Save the script as buckets.py.

Example status: verified by execution on 2026-10-23 (Python 3.8.10).

"""Header-bidding price buckets and a SupplyChain (schain) check (stdlib only)."""
from decimal import Decimal, ROUND_FLOOR

# Prebid's documented "dense" preset: (increment, cap) ranges in CPM.
DENSE = [(Decimal("0.01"), Decimal("3.00")), (Decimal("0.05"), Decimal("8.00")),
         (Decimal("0.50"), Decimal("20.00"))]

def bucket(cpm, ranges=DENSE):
    cpm = Decimal(str(cpm))
    top = ranges[-1][1]
    if cpm >= top:
        return top                      # bids above the cap fall to the cap bucket
    for inc, cap in ranges:
        if cpm < cap:
            return (cpm / inc).to_integral_value(rounding=ROUND_FLOOR) * inc
    return top

def line_items(ranges=DENSE):
    total, floor = 0, Decimal("0")
    for inc, cap in ranges:
        total += int((cap - floor) / inc) + (1 if floor == 0 else 0)
        floor = cap
    return total

for bid in [0.237, 2.999, 3.07, 7.99, 12.74, 31.0]:
    print(f"bid {bid:>6} -> hb_pb {bucket(bid)}")
print("line items per bidder (dense):", line_items())

def check_schain(sc, final_seller):
    errs = []
    if sc.get("ver") != "1.0":
        errs.append("ver should be '1.0'")
    nodes = sc.get("nodes") or []
    if not nodes:
        errs.append("nodes is empty")
    for i, n in enumerate(nodes):
        for k in ("asi", "sid", "hp"):
            if k not in n:
                errs.append(f"node {i}: missing {k}")
        if n.get("hp") not in (None, 1):
            errs.append(f"node {i}: hp should always be 1 in v1.0")
    if nodes and (nodes[-1].get("asi"), nodes[-1].get("sid")) != final_seller:
        errs.append("last node is not the system sending this bid request")
    if sc.get("complete") == 1 and len(nodes) > 1:
        errs.append("review: reseller in a complete chain; "
                    "node 0 = owner or manager?")
    return errs or ["ok"]

schain = {"ver": "1.0", "complete": 1, "nodes": [
    {"asi": "ssp-one.example", "sid": "1001", "hp": 1},
    {"asi": "reseller-x.example", "sid": "aaa", "hp": 1}]}
print("good chain:", check_schain(schain, ("reseller-x.example", "aaa")))
schain_bad = {"ver": "1.0", "complete": 0,
              "nodes": [{"asi": "ssp-two.example", "sid": "9"}]}
print("bad chain:", check_schain(schain_bad, ("ssp-two.example", "9")))
  1. Run python3 buckets.py.

Expected output.

bid  0.237 -> hb_pb 0.23
bid  2.999 -> hb_pb 2.99
bid   3.07 -> hb_pb 3.05
bid   7.99 -> hb_pb 7.95
bid  12.74 -> hb_pb 12.50
bid   31.0 -> hb_pb 20.00
line items per bidder (dense): 425
good chain: ['review: reseller in a complete chain; node 0 = owner or manager?']
bad chain: ['node 0: missing hp']

Validation. The count of 425 matches Prebid’s published table for “dense” (301 + 100 + 24).[47] A $12.74 bid enters the ad-server auction as $12.50 and can lose to a $12.51 line item, yet is paid at $12.74 if it wins: Prebid notes that rounding “does not impact the price paid, only the auction on the ad server”.[47] The bad chain is missing hp, which the specification requires; for version 1.0 it “should always be 1”.[28] The good chain is flagged because a complete chain with a reseller should start with the owner or its declared manager (the fourth buyer check). Extension. Six bidders with “send all bids” need 6 × 425 = 2,550 line items, against one set of 425 when only the top bid is sent.[48] Troubleshooting. decimal.InvalidOperation: a bid was passed as text with a currency sign; pass numbers. Teardown. rm -rf ~/adops-labs

Two documented cases (fraud and spend tracing) and two illustrative ones (a header-bidding migration and an app publisher with a sales house); each is labelled.

Scenario 1: 3ve and the value of enforced ads.txt (documented)

Link to section “Scenario 1: 3ve and the value of enforced ads.txt (documented)”

Problem. 3ve, analysed by Google and White Ops in November 2018, controlled over a million IP addresses, produced more than 10,000 counterfeit domains and, at its peak, between 3 and 12 billion or more bid requests a day.[42] The takedown involved Google, the FBI and White Ops, and the US Department of Justice unsealed a 13-count indictment against eight people, as reported by The Hacker News.[54] What ad ops learns. Most of 3ve’s requests failed the ads.txt check (the 80.3% figure above); the authors also recommend scepticism of fraud metrics that look too good, and layered detection.[42] Trade-off. Strict enforcement also blocks legitimate supply with a stale ads.txt, so keep yours current. Pitfall. Bots laundered through an authorised account pass the check; IVT detection still matters.

Scenario 2: tracing programmatic spend (documented)

Link to section “Scenario 2: tracing programmatic spend (documented)”

Problem. In a 2020 UK study by ISBA, the Association of Online Publishers and PwC, 15% of programmatic spend could not be attributed (the “unknown delta”); only 31 million of 267 million impressions could be matched from DSP to SSP, a 12% match rate. Method and result. The follow-up (January 2023, covering 2022) used log-level data from all vendors: the match rate rose to 58%, the unknown delta fell to 3%, and 65% of spend reached media owners, up from 51%, as reported by VideoWeek.[55] Lesson for ad ops. Cross-company reconciliation needs shared identifiers and log-level data; without them a gap cannot even be classified as fee, waste or fraud.

Scenario 3: from waterfall to header bidding (illustrative)

Link to section “Scenario 3: from waterfall to header bidding (illustrative)”

Scenario 4: an app publisher monetised by a sales house (illustrative; rules documented)

Link to section “Scenario 4: an app publisher monetised by a sales house (illustrative; rules documented)”
KPI Definition and formula Data source Pitfall Cadence
Pacing Delivered ÷ expected to date (expected = goal × elapsed ÷ flight length) Ad server Even pacing assumed; adjust for planned weighting Daily
Run rate needed (Goal − delivered) ÷ days remaining Ad server Ignores inventory limits; compare with forecast avails Daily
Fill rate Impressions ÷ ad requests Ad server Separate intentional blanks from unfilled requests[46] Daily
eCPM Revenue ÷ impressions × 1000[19] Ad server, SSP reports Rises when fill falls; pair with revenue per 1,000 requests Daily / weekly
Revenue per 1,000 requests or sessions Revenue ÷ requests (or sessions) × 1000 Ad server, analytics Session definition must be stable Weekly
CTR Clicks ÷ impressions × 100[19] Ad server Should fall when IVT filtering improves[42] Weekly
Measured rate (Viewable + non-viewable) ÷ rendered impressions[6] Verification vendor A low measured rate makes the viewable rate unreliable Weekly
Viewable rate Viewable ÷ (viewable + non-viewable)[6] Verification vendor Not the same as viewable ÷ all impressions Weekly
IVT rate IVT impressions ÷ total measured impressions (state GIVT, SIVT or both) Verification vendor, SSP Vendors differ in SIVT methods; compare like with like[25] Weekly
Discrepancy (Count A − count B) ÷ the denominator named in the contract Both ad servers Small volumes give large percentages[21] Daily while live
Bid rate / win rate / timeout rate Bids ÷ requests; wins ÷ bids; timed-out requests ÷ requests, per bidder Wrapper analytics Timeouts cluster by geography and pre-auction modules[49] Weekly
Authorised-supply share Requests from ads.txt-authorised paths ÷ all requests for your domain Buyer or SSP reports Needs partners to share data Monthly

Worked calculation (illustrative numbers). A site serves 10,000,000 ad requests in a week; 8,800,000 are filled; revenue is $17,600. Fill rate = 88%; eCPM = $17,600 ÷ 8,800,000 × 1000 = $2.00; revenue per 1,000 requests = $1.76. After a floor increase, 7,000,000 impressions earn $16,100: eCPM rises to $2.30, but revenue per 1,000 requests falls to $1.61, an 8.5% loss on the same traffic. Revenue per request answers the business question; eCPM does not.

Figure 6. From ad request to billable, viewable impression: where volume is lost
unfilled: no eligible adinvalid traffic removedAd requests:10,000,000Filled impressions:8,800,000 (fill 88%)Valid after IVTfilteringMeasured forviewability(measured rate)Viewableimpressions(viewable rate)Unfilled impressionsGIVT and SIVT
Diagram source (Mermaid)
%%{init: {"flowchart": {"wrappingWidth": 140, "nodeSpacing": 12, "rankSpacing": 18, "padding": 8}}}%%
flowchart TB
  R["Ad requests: 10,000,000"] --> F["Filled impressions: 8,800,000 (fill 88%)"]
  F --> V["Valid after IVT filtering"]
  V --> M["Measured for viewability (measured rate)"]
  M --> VW["Viewable impressions (viewable rate)"]
  R -. "unfilled: no eligible ad" .-> U["Unfilled impressions"]
  F -. "invalid traffic removed" .-> I["GIVT and SIVT"]
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
  class R,F,V,M,VW core
  class U,I risk

Text equivalent: A vertical funnel. Ten million ad requests lead to 8.8 million filled impressions (88% fill); the rest are unfilled because no eligible ad existed. Filled impressions are filtered for general and sophisticated invalid traffic. Valid impressions that a verification vendor could measure determine the measured rate, and those found in view determine the viewable rate. Each step loses volume, and each step has its own denominator.

Targets. Set targets from your own baseline and contract terms, not from industry averages: pacing 95–110% at flight end for guaranteed lines (an Atlas suggestion), discrepancy within the IO tolerance, IVT and viewability thresholds as written in buyers’ terms. Vanity metrics. eCPM alone, “impressions served” without IVT filtering, and viewability without the measured rate.

Risk Likelihood / impact Signal Mitigation Owner
Malicious or non-secure creative Medium / high (users harmed on your domain) User reports, scanner alerts, redirects Scanning, blocklists, pause first Ad ops + SecOps
Unauthorised resellers of your inventory Medium / high Buyer reports of spoofed supply Accurate ads.txt, OWNERDOMAIN Yield mgr
Consent signal missing, wrong or not enforced Medium / high (regulatory) Personalised bids drop; CMP errors Certified CMP, tcfControl, tests per release Ad tech eng. + privacy owner
Child-directed inventory sent to ineligible partners Low / high Audit of request flags Tag child-directed sections; check partner handling[23] Ad ops lead
Billing on invalid traffic Medium / medium IVT rate above contract Accredited measurement, TAG-certified paths[10] Ad ops + finance
Over-sharing user data with bidders Medium / high Data-flow review findings Limit bidders, review adapters and ID modules Privacy owner
Ad server access misuse Low / high Unexpected bulk changes Least-privilege roles, MFA, change log review Ad ops lead

Brand safety and suitability. Buyers set avoidance categories and, as MRC puts it, “different tolerance levels for risk depending on brand suitability”. MRC’s verification supplement separates property-level classification (a whole site or app) from content-level classification (the page, video or post), and requires vendors to disclose when they classify by URL keywords alone, which is not content-level measurement.[51] For a publisher, a keyword or domain match is therefore a weaker signal than it looks: ask buyers which level their vendor measures before accepting a block, and leave the policy (which categories, which tolerance) to the advertiser’s MarketingOps owners.

Least privilege. Separate ad-server roles for trafficking, yield, administration and reporting; automation credentials reach only the objects they change. Evidence. Keep QA records, discrepancy logs, IOs and invoices as finance rules require, and ads.txt history in version control. Jurisdiction. Legal questions belong to PrivacyOps and LegalOps.

  • Intermediary fees. Every hop takes a share. The ANA’s programmatic work centres on made-for-advertising sites and streamlining supply-chain transaction costs;[4] ad ops helps by removing paths that add no unique demand.
  • People. Trafficking scales with IOs and creatives, not impressions; automate bulk work before hiring.
  • Line items. Granularity × bidders × formats multiplies objects (Lab 4); check your ad server’s limits first.
  • Latency. Each bidder and module delays the ad call; Prebid treats the timeout as a revenue trade-off.[49]
  • Sustainability. Fewer redundant bid requests mean less compute; no source opened here quantifies it (GreenOps).
  • Leading indicators: complete IOs at intake; QA first-pass rate; time to traffic; ads.txt check results; discrepancy baselines against vendor expectations.
  • Lagging indicators: make-good rate; disputed invoices; revenue per 1,000 requests; IVT rate; incidents by severity.
  • Independent checks: MRC-accredited measurement for billing; TAG certification where buyers require it; an annual audit of ad-server access, change logs and ads.txt history.
  • Feedback loop: a post-mortem with owned actions for every Sev 1 or Sev 2 incident.[19]
Symptom Likely cause Diagnosis Remediation and prevention
Guaranteed line item serves nothing Paused, creative not approved, wrong dates or time zone, targeting matches no inventory Troubleshoot tab, Delivery Inspector[46][45] Fix; add an “eligible to serve” QA check
Programmatic revenue drops site-wide overnight ads.txt rewritten without a partner’s line, wrong content type or off-domain redirect; CMP failure; wrapper error; new pricing rule curl -sI https://<your-domain>/ads.txt, diff with the last version; change log; consent test Roll back the last change; alert on ads.txt content and headers. (A deleted file authorises everyone under the specification;[26] buyers’ own policies may differ)
Header bids never win No line item matches hb_pb; wrapper granularity differs from line-item prices; wrong key names Compare page bucket values with line-item targeting Generate line items from the wrapper’s granularity config[47]
High bidder timeout rate Slow partners, distant data centres, long consent or identity waits Timeout rate by bidder and region; pre-auction time[49] Adjust timeout; drop or deprioritise slow bidders; shorten module waits
Fill falls after a yield change Floor above most bids; overlapping rules (the higher applies)[34] Before/after fill and revenue per 1,000 requests Roll back; test floors on a slice
Clicks but few impressions in third-party report Click tracker trafficked without an impression tracker Creative preview; tracker log[45] Re-traffic with both trackers; QA both
Discrepancy above tolerance (or a steady one above the vendor’s expectation) Creative type, latency, ad blockers, passbacks, third-party targeting or caps[21] Daily comparison; find the change date See the worked runbook below
Low viewable rate Below-the-fold slots, slow loading, or a low measured rate Check measured rate first, then viewability by slot[6] Fix measurement coverage, then layout and lazy loading
Users report redirects Malicious creative from a demand partner Capture creative and chain; identify partner and buyer Pause, block, report; tighten creative scanning
Buyer reports your domain sold by an unknown seller Spoofing or a stale reseller line Check ads.txt history and the seller’s sellers.json entry Remove stale lines; publish OWNERDOMAIN; ask buyer to block the path

Worked runbook: under-delivery and a 15% lower buyer count

Link to section “Worked runbook: under-delivery and a 15% lower buyer count”
Figure 7. Runbook flow for a campaign that is behind and disputed
noyesyesnoAlert: pacing below95% and gapabove toleranceSet severity; notifyaccount managerSame dates, timezone, line items;unfilled excluded?Re-run reportscorrectlyFind the changedate in daily dataCheck change logand creatives forthat dateFix: re-trafficcreative withcorrect type andtrackersRun rate neededachievable?Monitor daily untilgap and pacingrecoverAgree option withclient: extend,widen, make-goodPost-mortem:update QAchecklist
Diagram source (Mermaid)
%%{init: {"flowchart": {"wrappingWidth": 130, "nodeSpacing": 14, "rankSpacing": 20, "padding": 8}}}%%
flowchart TB
  T["Alert: pacing below 95% and gap above tolerance"] --> S["Set severity; notify account manager"]
  S --> B{"Same dates, time zone, line items; unfilled excluded?"}
  B -- "no" --> R1["Re-run reports correctly"]
  R1 --> B
  B -- "yes" --> C["Find the change date in daily data"]
  C --> L["Check change log and creatives for that date"]
  L --> FX["Fix: re-traffic creative with correct type and trackers"]
  FX --> P{"Run rate needed achievable?"}
  P -- "yes" --> MON["Monitor daily until gap and pacing recover"]
  P -- "no" --> OPT["Agree option with client: extend, widen, make-good"]
  OPT --> MON
  MON --> PM["Post-mortem: update QA checklist"]
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
  class T,R1 risk
  class B,P decision
  class S,C,L,FX,MON,OPT,PM core

Text equivalent: After the alert, set severity and notify the account manager. Confirm both reports cover the same dates, time zone and line items without unfilled impressions (re-run if not). Find the change date, check the change log and creatives, fix the cause. If the needed run rate is achievable, monitor; if not, agree an option with the client first. Finish with a post-mortem that updates the QA checklist.

  1. Triage (first 30 minutes). Severity 2: a guaranteed campaign is at risk and a dispute is forming. Tell the account manager now; investigate while the line item still runs.[21]
  2. Rule out report errors. Same dates and time zone, same line items, unfilled impressions excluded.[21]
  3. Find the change dates. Lab 3 shows a steady 6–7% gap on 1–2 and 6–7 November and 15–17% on 3–5 November. Something ran only on those three days. Do not treat 6–7% as normal: it is a second, older problem (step 8).
  4. Check the change log. A second creative version ran from 3 to 5 November. It was trafficked as a generic third-party creative instead of the Campaign Manager 360 tag type (expected gaps: QA playbook step 4). A test page also shows that tag calling another ad server (a passback chain), which increases the expected discrepancy.
  5. Fix and verify before it recurs. Traffic the 15 November rotation with the correct creative type and the agency’s current tag; check on a test page that the publisher’s and the agency’s impression trackers each fire once; confirm the next day’s gap falls back to at most the old 6–7%.
  6. Recover delivery. Lab 1 needs 54,444 impressions a day against 43,333 so far. If the forecast is short, offer the client a written choice (wider geography, a three-day extension, a make-good); never silently raise the line item above other guaranteed commitments.
  7. Settle the money. The three bad days push the week over a 10% tolerance. Share the daily evidence and propose the IO’s remedy (in the 4A’s/IAB template: reconcile, then a make-good or payment on the controlling count plus 10%).[31]
  8. Post-mortem. Root cause: a creative swap without full QA. Actions: creative changes use the launch checklist; the daily report flags step changes in the gap. Follow-up ticket for the 6–7% baseline (step 3): check that creative v1 uses the Campaign Manager 360 tag type, then compare geography, third-party targeting and frequency-cap settings with the agency.
Anti-pattern or myth Why it fails Warning sign Do instead
“eCPM went up, so yield went up” Higher floors raise eCPM and lower fill Revenue per 1,000 requests falls Report revenue per request alongside eCPM
Adding every reseller line a partner sends Duplicate paths, unknown intermediaries, buyer distrust ads.txt grows every quarter; few lines remove Partner register; require sellers.json evidence; remove unused lines
“Our ad server’s count is the truth” The contract names the controlling count[31] Arguments at month end Agree counting terms at intake
Calling viewable ÷ all impressions the “viewable rate” MRC defines it over measured impressions only[6] Viewability looks worse than vendors report Report measured rate and viewable rate
Header-bidding line items above guaranteed priority Guaranteed campaigns starve Make-goods rise after a wrapper launch Keep header bids at non-guaranteed priority
Fixing discrepancies at month end Evidence is gone; fewer options[21] Disputes only at invoice time Daily gap check
Accepting a steady discrepancy as “normal” A constant gap can hide a set-up fault (wrong creative type, targeting or caps)[21] Same gap on every campaign with one partner Compare with the vendor’s documented expectation
Switching off consent handling to “recover revenue” Regulatory exposure; Google limits ads from non-certified CMPs[13] Personalised demand returns but legal review is skipped Fix the CMP integration; involve the privacy owner
“ads.txt stops fraud” It stops unauthorised sellers, not bots through authorised accounts[42] IVT rises with clean ads.txt Layer IVT detection on top
Situation Recommended approach Why
Small site, one ad network that sells and serves No ad ops team: one owner, the network’s ads.txt line, a CMP, a monthly check The network traffics; you keep the file, signal and trend
Growing publisher, several SSPs, occasional direct deals Part-time owner plus a managed service for header bidding Prebid notes many companies hire managed services for header bidding[1]
Publisher with a direct sales team, guaranteed campaigns, several formats In-house ad ops (trafficking, yield, ad tech engineering) Contracts, make-goods and reconciliation need daily owners
Agency or large advertiser buying across many publishers Buy-side ad ops (trafficking, verification, reconciliation) Every placement adds tags, counts and invoices to check
Advertiser buying only through self-serve platforms (social, search) Marketing operations, not ad ops The platform runs delivery; see MarketingOps

When not to build an ad ops function. When one network serves a minor revenue line well; when you cannot staff daily monitoring (a half-run header-bidding stack can do worse than a simple, well-kept one); or when the business is leaving advertising. Hidden costs: partner contracts, page-performance work, privacy reviews of each bidder, out-of-hours incidents.

Exit criteria from a managed service. Bring work in-house when you can name owners for wrapper releases, ads.txt, consent integration and reconciliation, and when the managed-service fee exceeds the cost of those people plus tooling.

Figure 8. Choosing an operating model for advertising
no, we only buynoyesyesyesnonoyesyesnoDo you sell yourown adinventory?Buying outsideself-serveplatforms atscale?MarketingOpscovers itBuy-side ad ops:trafficking,verification,reconciliationDirect sales withguaranteedcampaigns?In-house ad opsand yield teamMore than one ortwo programmaticpartners?One owner andmonthly checks;the networkdeliversCan you staffdaily monitoringand engineering?Managed serviceplus accountableowner
Diagram source (Mermaid)
%%{init: {"flowchart": {"wrappingWidth": 120, "nodeSpacing": 14, "rankSpacing": 20, "padding": 8}}}%%
flowchart TB
  Q1{"Do you sell your own ad inventory?"}
  Q1 -- "no, we only buy" --> Q5{"Buying outside self-serve platforms at scale?"}
  Q5 -- "no" --> MKT["MarketingOps covers it"]
  Q5 -- "yes" --> BUY["Buy-side ad ops: trafficking, verification, reconciliation"]
  Q1 -- "yes" --> Q2{"Direct sales with guaranteed campaigns?"}
  Q2 -- "yes" --> INH["In-house ad ops and yield team"]
  Q2 -- "no" --> Q3{"More than one or two programmatic partners?"}
  Q3 -- "no" --> OWN["One owner and monthly checks; the network delivers"]
  Q3 -- "yes" --> Q4{"Can you staff daily monitoring and engineering?"}
  Q4 -- "yes" --> INH
  Q4 -- "no" --> MAN["Managed service plus accountable owner"]
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
  class Q1,Q2,Q3,Q4,Q5 decision
  class INH,BUY,MAN core
  class OWN,MKT neutral

Text equivalent: Buyers only: MarketingOps covers self-serve buying; buying beyond it at scale needs buy-side ad ops. Sellers with direct sales and guaranteed campaigns need an in-house ad ops and yield team. Without them, one or two programmatic partners need only one owner and monthly checks; more partners need an in-house team if you can staff daily monitoring and engineering, otherwise a managed service with an accountable internal owner.

For the build-or-buy choice inside the “managed service” branch, score each option from 1 to 5 against weighted criteria. The weights below are an Atlas suggestion for a mid-sized publisher; change them to match your situation.

Criterion Weight In-house Managed service
Control over partners, floors and data sharing 25% 5 3
Speed to a working, monitored stack 20% 2 5
Ongoing cost at current revenue 20% 2 4
Transparency of fees and supply paths 20% 5 3
Resilience to staff turnover 15% 2 4
Weighted score 100% 3.35 3.75

Calculation: in-house = 0.25×5 + 0.20×2 + 0.20×2 + 0.20×5 + 0.15×2 = 3.35; managed = 0.25×3 + 0.20×5 + 0.20×4 + 0.20×3 + 0.15×4 = 3.75. A publisher that weights control and transparency at 35% each would reach the opposite answer; the weights are the decision.

This is the Atlas’s own maturity model, not an industry standard; use it to find the next step.

Level Observable behaviour and evidence Exit criteria to the next level
1. Ad hoc Set-up by whoever is free; ads.txt edited when a partner asks; no QA records; month-end disputes Named owner; intake form; QA checklist in use
2. Repeatable Standard trafficking and QA records; daily pacing report; ads.txt history in version control Discrepancy log; partner register; consent tested
3. Defined RACI; change tickets for priorities, floors and wrapper; monthly ads.txt/sellers.json check Weekly revenue per request and IVT; post-mortems
4. Measured KPIs with targets; quarterly supply-path decisions; accredited measurement for billing Guarded automation; tested rollbacks
5. Optimising Experiment log for floors, timeouts, partners; incidents feed checklists; falling dispute rates Sustain; revisit after major market or regulatory change
Figure 9. Maturity progression and the artefact that unlocks each step
owner, intake form, QAchecklistdiscrepancy log, partnerregisterKPIs with targets,post-mortemsguarded automation,experiments1 Ad hoc2 Repeatable3 Defined4 Measured5 Optimising
Diagram source (Mermaid)
%%{init: {"flowchart": {"wrappingWidth": 110, "nodeSpacing": 12, "rankSpacing": 18, "padding": 8}}}%%
flowchart TB
  L1["1 Ad hoc"] -- "owner, intake form, QA checklist" --> L2["2 Repeatable"]
  L2 -- "discrepancy log, partner register" --> L3["3 Defined"]
  L3 -- "KPIs with targets, post-mortems" --> L4["4 Measured"]
  L4 -- "guarded automation, experiments" --> L5["5 Optimising"]
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  class L1,L2 neutral
  class L3,L4,L5 core

Text equivalent: Five levels in sequence; each arrow names what unlocks the next: owner, intake form and QA checklist (to Repeatable); discrepancy log and partner register (Defined); KPIs with targets and post-mortems (Measured); guarded automation and experiments (Optimising).

For a publisher moving from level 1 or 2 to level 3. Adapt the order to your biggest pain.

Window Actions Owner Milestone and metric
Days 1–30 Name the owner; partner register; ads.txt in version control with the Lab 2 check; intake form and QA checklist; daily pacing report Ad ops lead QA record for every new campaign; no unknown seller IDs
Days 31–60 Counting terms in the IO template; discrepancy log for third-party-served campaigns, baselines compared with vendor expectations; consent tests per release; change control Ad ops lead, finance, ad tech eng. No dispute without a daily log; no untracked pricing-rule change
Days 61–90 Weekly revenue per 1,000 requests, IVT, measured and viewable rates; first partner review; first post-mortem; scripted header-bidding line items if relevant Yield mgr, ad ops lead KPI pack in use; one supply path removed or justified; level-3 self-assessment

Dependencies: report access, a privacy owner, finance, engineering time. Quick wins: fix ads.txt errors; remove a dead partner; add the measured rate to viewability reports.

Quick start (new owner, first week)

  • I can log in to the ad server with my own named account and the right role
  • I have the list of every demand partner, its contract owner and its ads.txt line
  • /ads.txt (and app-ads.txt for apps) returns 200, text/plain, and includes OWNERDOMAIN
  • I know which CMP runs, whether Google certifies it, which TCF/GPP strings it sends, and whether Prebid enforces them
  • I can produce yesterday’s impressions, fill, eCPM and revenue per 1,000 requests

Pre-launch QA (every campaign and every creative change): the five steps of the trafficking QA playbook, plus counting terms (controlling count, tolerance, denominator) recorded in the ticket.

Header-bidding release

  • Wrapper version and modules recorded; configuration diffed and reviewed
  • Price granularity in the wrapper equals the line-item price set
  • Timeout tested on a traffic split; consent and identity module wait times measured
  • schain configured if a managed service handles payments
  • tcfControl in the build if Prebid must enforce consent; rules reviewed with the privacy owner
  • Rollback build available and tested

Monthly governance

  • ads.txt / app-ads.txt checked against partners’ sellers.json (Lab 2); unknown or mislabelled lines fixed
  • Discrepancy log reviewed; open disputes have owners
  • IVT, measured rate and viewable rate reviewed by partner and placement
  • Ad-server user list reviewed; leavers removed; API keys rotated as policy requires

Incident (revenue drop or malicious ad)

  • Severity set; account managers and finance informed
  • Last changes listed (pricing rules, priorities, wrapper, ads.txt, CMP, site release)
  • For malicious ads: creative paused and evidence captured before investigation
  • Post-mortem scheduled with actions and owners

Discrepancy log entry

Campaign / IO:        IO-2026-0420 line 0420-03
Parties and counts:   Publisher ad server vs agency ad server (controlling)
Period:               2026-11-01 to 2026-11-07
Tolerance in IO:      10% (denominator: publisher count)
Daily gap baseline:   6-7% (above the <2% expected for CM360 tags: ticket <id>)
Exception days:       03-05 Nov (15-17%)
Checks done:          dates/time zone, same line items, unfilled excluded, creative type
Root cause:           creative v2 trafficked as generic third-party tag with passback
Action and owner:     re-traffic v3 as CM360 tag type (trafficker), due 2026-11-14
Commercial outcome:   invoice 348,700 (controlling 317,000 + 10%); agreed <date>

Supply-partner review (one per partner per quarter)

Partner:              <SSP or reseller name>     Contract owner: <name>
ads.txt line(s):      <domain, account, DIRECT|RESELLER>
sellers.json entry:   seller_id <id>, domain <...>
                      seller_type <PUBLISHER|INTERMEDIARY|BOTH>
TAG status:           <certified programmes, date checked>
Last 90 days:         revenue share <%>, unique demand <% of its wins not seen via others>
                      bid rate <%>, timeout rate <%>, IVT rate <%>
Data sharing:         user IDs and modules enabled for this partner
Decision:             keep | renegotiate | remove (by <date>)   Approved by: <name>

Post-incident review (one page)

Title / severity:     <what happened>  Sev <1-3>
Impact:               revenue <amount or %>, campaigns affected, users affected
Timeline (UTC):       detected / mitigated / resolved
Root cause:           <technical cause> and <process gap that allowed it>
What went well:       <...>
Actions:              <action> - <owner> - <due date>   (one line each)
Checklist updated:    yes/no, which item

Sell-side versus buy-side: the sell side optimises yield (revenue per request, fill, sell-through), publishes ads.txt and disputes under-delivery; the buy side optimises delivery to plan (reach, viewability, IVT, cost), enforces ads.txt, sellers.json and schain, and disputes invalid or non-viewable impressions. Neighbouring Ops are compared in how AdOps relates to other Ops.

Direct versus programmatic versus header bidding versus server-side

Approach Strength Weakness Choose when
Direct guaranteed Price and relationship control Sales and trafficking effort A sales team and distinctive audiences
One SSP Simple Less competition Small sites
Client-side header bidding Competition, visible bids Page work, latency, line items Web, with engineering support
Server-side bidding Less page work Fewer browser signals; operator dependence Apps, CTV, latency-sensitive pages

Is AdOps the same as programmatic trading? No. Traders decide what to buy and at what price in a DSP; ad ops sets up and verifies the systems those trades flow through. In small teams one person does both.

Do I need ads.txt if I only use one ad network? Yes. Publish that network’s line exactly as it documents it; buyers’ platforms check the file. A site that authorises nobody should publish the placeholder record.[26]

Why do our numbers never match the agency’s? Counting points, IVT filters, geography, latency, ad blockers and passbacks differ; Google calls discrepancies “common and expected”.[21] The 10% tolerance often quoted is a clause in the 4A’s/IAB template, not a standard;[31] your IO decides.

If Chrome keeps third-party cookies, can we ignore privacy changes? No. Chrome kept user choice over third-party cookies,[14] but consent requirements such as Google’s own certified-CMP rule for the EEA, UK and Switzerland still apply;[13] the legal analysis is in PrivacyOps.

Should our header-bidding partner add reseller lines to our ads.txt? Only lines you can trace to a contract and a sellers.json entry, bringing demand you do not reach otherwise. Each line tells buyers the path is yours.

Established capability, still changing:

  • Connected TV. Ad pods, server-side insertion and the 2024 VAST CTV addendum are covered in video and CTV ad operations.[35]
  • Privacy signals. GPP keeps adding US state sections;[30] test signals on every CMP or wrapper upgrade.
  • Browser change. On 17 October 2025 Google announced the retirement of Topics, Protected Audience, Attribution Reporting and other Privacy Sandbox technologies, while Chrome keeps third-party cookie choice.[14] Decommission integrations built for the retired APIs.

Regulatory change with operational consequences:

  • The European Commission fined Google €2.95 billion in September 2025 for favouring AdX through DFP and its buying tools (for example, telling AdX the best competing bid in advance) and ordered it to end the self-preferencing.[15]

  • In the United States, a federal court held in April 2025 that Google monopolised open-web digital advertising markets.[16] On 2 September 2026 the judge rejected a divestiture of AdX and ordered “most of the parties’ proposed behavioral remedies”;[17] her opinion was unsealed by court order on 16 September,[56][57] and in early October the parties filed their proposed final judgments in one document, disputed language in brackets. AdExchanger reported “no major disagreements on the core remedies”:[24]

    • Agreed: AdX connected to Prebid; AdX answering bid requests from rival publisher ad servers on the terms it gives DFP; no reimplementation of first or last look; unified pricing rules, “which prevent publishers from setting different price floors for different buyers”, added to the banned tactics; Google Ads bidding through AdX or Prebid, not directly into DFP; separate DFP and AdX contracts; export of DFP configuration and historical bid data within three months of the order taking effect.
    • Disputed: six or twelve months for the integrations; global effect at once or up to 24 extra months abroad; when publishers get their DFP data; the six-year monitor’s powers.
    • Timing: nothing takes effect until 60 days after the judge signs an order; AdExchanger expects Google to appeal.

    No signed final judgment had been reported by 23 October 2026; the public docket copy lists none.[56] Check it before changing configurations: integrations, floors and data exports in the dominant publisher ad server will change on its schedule.

Active research and vendor-led change (treat with care):

  • AI agents in buying and selling. IAB Tech Lab released an agentic real-time framework (ARTF) for public comment in November 2025 and grouped its agentic work under the name AAMP in March 2026, with an agent registry, as summarised by IAB UK.[58] These are emerging standards; the operational questions (approval, logging, rollback) are those of any automation that writes to the ad server.
  • Attention metrics. MRC issued Attention Measurement Guidelines in November 2025;[7] treat them as additional signals, not replacements for impressions and viewability in contracts.
Stage Goal Do this
Prerequisites Read basic HTML and JavaScript; use spreadsheets fluently A short web-fundamentals course
1-day primer Explain how one impression is decided and counted Read the first three parts; draw the sequence diagram from memory
1 week Traffic and check like a professional Labs 1 and 3; your own QA checklist; Google’s discrepancy article[21]
30 days Own supply-chain trust Lab 2 on a public ads.txt and its partners’ sellers.json; the three specifications[26][27][28]
Advanced Yield and header bidding, buy-side verification, or CTV Lab 4; Prebid’s timeout and granularity docs;[49][47] MRC guidelines;[6][25] VAST 4.3[37]
Credential (optional) Show breadth IAB Digital Ad Operations Certification[18]

Portfolio projects. (1) An ads.txt monitor that alerts on content-type, redirect and seller changes. (2) A discrepancy dashboard that flags step changes and baselines above vendor expectations. (3) A timeout experiment write-up with revenue per session and page-speed results. (4) A partner review of a public ads.txt from public files only.

Topic Remember
Three records Promised (IO, deal) · decided (ad server, auctions) · counted (impressions, viewability, IVT, money)
Video and CTV Know the insertion mode (ssai) · pod floors per second · where impressions are counted
Trust files ads.txt / app-ads.txt (who may sell) · sellers.json (who is paid) · schain (every paid hop in this request)
Counting Impression = loaded and began to render · viewable display = 50% pixels, 1 s (video 2 s) · viewable rate = V ÷ (V + NV)
Discrepancy Normal up to the vendor’s documented level; agree controlling count, tolerance and denominator in the IO
Header bidding Ad server still decides · buckets need matching line items · timeout is a revenue trade-off
Consent Pass TCF/GPP strings; enforce in the wrapper (tcfControl); certified CMP for Google in EEA, UK, Switzerland
Fraud GIVT by lists, SIVT by analysis · ads.txt stops unauthorised sellers, not bots through real accounts
Watch-outs eCPM without revenue per request · stale reseller lines · header bids above guaranteed priority · month-end reconciliation
Next actions Owner and RACI · ads.txt in version control and checked monthly · daily pacing and discrepancy report · partner register

A judgement call: should we cut our ads.txt reseller lines from 60 to 15?

Link to section “A judgement call: should we cut our ads.txt reseller lines from 60 to 15?”

You should now be able to explain how one impression is decided, counted and billed; check ads.txt, sellers.json and schain; calculate pacing, eCPM, measured and viewable rates; investigate a discrepancy with a runbook; choose an operating model; and evaluate trade-offs such as floors versus fill.

1. A line item has delivered 300,000 of 900,000 impressions on day 10 of a 30-day flight. Is it on pace, and what daily run rate is needed?

Expected to date = 900,000 × 10 ÷ 30 = 300,000, so pacing is 100%. Run rate needed = 600,000 ÷ 20 = 30,000 a day, equal to the run rate so far (Lab 1 method).

2. A vendor reports 2,000,000 rendered impressions: 900,000 viewable, 600,000 non-viewable, 500,000 undetermined. What are the measured rate and viewable rate?

Measured rate = (900,000 + 600,000) ÷ 2,000,000 = 75%. Viewable rate = 900,000 ÷ 1,500,000 = 60%. Calling 45% (900,000 ÷ 2,000,000) the viewable rate is the error MRC warns against.

3. Your ads.txt says “ssp.example, 555, DIRECT”, but ssp.example’s sellers.json lists 555 as INTERMEDIARY with a sales house’s domain. What do you do?

Either the label is wrong (the sales house controls the account, so the line should be RESELLER) or the SSP’s record is wrong. Ask the SSP and the sales house, correct whichever is wrong, and record the decision in the partner register. Declaring the sales house as MANAGERDOMAIN does not change the label: its SSP account is still a RESELLER line. Buyers may treat the mismatch as a mislabelled path (Lab 2).

4. Why can a header bid of $4.38 lose in the ad server to a $4.36 line item when using 0.05 increments?

The bid enters the ad-server auction as the $4.35 bucket, so a $4.36 line item outranks it. Had it won, it would have been paid $4.38: rounding affects only the ad-server auction (Lab 4).

5. Raising floors increased eCPM from $2.00 to $2.30 and fill fell from 88% to 70% on the same 10 million requests. Did revenue rise?

No: $17,600 before, $16,100 after, an 8.5% fall (the worked calculation). Judge yield by revenue per request.

6. A guaranteed campaign is behind pace right after a header-bidding launch. Which setting do you check first?

The priority of the header-bidding line items. If they sit above guaranteed priority, they take impressions meant for the guaranteed line items. In Google Ad Manager, header bids belong in non-guaranteed types such as Price priority (priority 12).

7. Your EEA traffic’s personalised demand collapsed after a CMP change. What do you check?

Whether the new CMP is on Google’s certified list (required for personalised ads in the EEA, UK and Switzerland), whether it emits the current TCF string your partners read, whether Prebid’s consent module receives it before its timeout, and, if the build includes tcfControl, whether bidders are now being excluded because the new CMP’s vendor list lacks them. Involve the privacy owner before any workaround.

AdOps is old enough to have a certification and young enough to be rewritten by every new auction model, privacy rule and court remedy. What lasts is the core: three records (promised, decided, counted) that must agree, and a supply chain that anyone who pays can check. The specifications are short and public; the discipline is keeping them true every day. Most failures here are not exotic: a stale reseller line, a creative swapped without QA, a floor raised without watching fill, a “normal” discrepancy nobody questioned. And when one network and a well-kept ads.txt are enough, choosing that is good operations, not a lack of ambition.

All sources were opened and checked on 2026-10-23 (evidence ledger: research/adops-ledger.md). Product behaviour and regulatory status change often; re-check them.

Role and market

Supply-chain specifications (IAB Tech Lab)

Measurement and invalid traffic

Google Ad Manager (vendor documentation, as of October 2026)

Header bidding (Prebid.org documentation)

Privacy signals and browsers

Contracts, competition law and emerging standards

Further reading: Basic Prebid.js example, the official minimal integration with Google Publisher Tag.

Changelog. Re-verify Google Ad Manager behaviour, the US remedies judgment and privacy-signal versions by 2027-01-23.

  • 2026-10-23, first version (r0): research, evidence ledger, nine diagrams, four labs executed on synthetic data.
  • 2026-10-23, r1 revision after independent review: MANAGERDOMAIN added to the buyer’s fourth check and Scenario 4; US remedies updated to the October 2026 proposed final judgment; consent enforcement attributed to Prebid’s tcfControl; runbook treats the 6–7% baseline as a finding; new video and CTV section; brand-safety paragraph; glossary terms; Lab 4 re-run; repetition cut.
  • 2026-10-23, post-score polish (reviews/adops-polish.md): unsealing date sourced to the court docket (16 September, ECF 1860; no final judgment); discrepancy restatements cut.

  1. Introduction to Header Bidding — Prebid.org documentation. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎

  2. Digital Ad Revenue Climbs to Nearly $300B as IAB Celebrates 30 Year Anniversary — IAB press release, 2026-04-16. Checked 2026-10-23. ↩︎

  3. IAB/PwC Internet Advertising Revenue Report: Full-year 2025 results — IAB and PwC, April 2026 (PDF; full report read on an IAB Hong Kong mirror, survey definitions page). Checked 2026-10-23. ↩︎ ↩︎

  4. ANA Programmatic Media Supply Chain Transparency Study (landing page) — Association of National Advertisers, study issued December 2023. Checked 2026-10-23. ↩︎ ↩︎

  5. OpenRTB 2.6 specification — IAB Tech Lab (GitHub). Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  6. MRC Viewable Ad Impression Measurement Guidelines, Version 2.0 — Media Rating Council with IAB, 2015-08-18 (PDF). Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  7. Standards and Guidelines — Media Rating Council (list of guidelines with issue dates). Checked 2026-10-23. ↩︎ ↩︎ ↩︎

  8. Desktop Display Impression Measurement Guidelines (MMTF Final v1.1) — IAB Tech Lab and MRC, October 2017 (PDF). Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎

  9. About Prebid — Prebid.org. Checked 2026-10-23. ↩︎ ↩︎ ↩︎

  10. Fraud — Trustworthy Accountability Group (TAG). Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  11. ads.txt – Authorized Digital Sellers — IAB Tech Lab, updated 2026-09-14. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎

  12. sellers.json — IAB Tech Lab, updated 2026-10-07. Checked 2026-10-23. ↩︎ ↩︎

  13. Google consent management requirements for serving ads in the EEA, the UK, and Switzerland (for publishers) — Google Ad Manager Help. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  14. Update on Plans for Privacy Sandbox Technologies — Google, 2025-10-17. Checked 2026-10-23. ↩︎ ↩︎ ↩︎

  15. Commission fines Google €2.95 billion over abusive practices in online advertising technology — European Commission press release IP/25/1992, 2025-09-05. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  16. Department of Justice Prevails in Landmark Antitrust Case Against Google — US Department of Justice, 2025-04-17. Checked 2026-10-23. ↩︎ ↩︎

  17. Google Won’t Have To Break Up Its Ad Tech Business, Judge Brinkema Rules — AdExchanger (Victoria McNally), 2026-09-02. Checked 2026-10-23. ↩︎ ↩︎

  18. IAB Certification — Interactive Advertising Bureau, live page. Checked 2026-10-23. ↩︎ ↩︎ ↩︎

  19. IAB Digital Ad Operations Certification Study Guide — IAB, August 2017 (PDF). Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  20. Mobile Discrepancies: Exploring Common Root Causes — IAB Mobile Ad Ops Working Group, September 2013 (PDF). Checked 2026-10-23. ↩︎

  21. Investigate report discrepancies — Google Ad Manager Help. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  22. Line item types and priorities — Google Ad Manager Help. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  23. How Open Bidding works — Google Ad Manager Help. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎

  24. Google And The DOJ Filed Their Proposed Final Judgments In The Ad Tech Case – Here’s What They’re Still Arguing About — AdExchanger (Allison Schiff), 2026-10-05. Checked 2026-10-23. ↩︎ ↩︎

  25. 2024 IVT Interim Updates — Media Rating Council memo quoting the IVT 2.0 addendum, published 2024-04-24 (PDF). Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  26. ads.txt Specification Version 1.1 — IAB Tech Lab (GitHub), final release July 2022 (listed on IAB Tech Lab’s page as August 2022). Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  27. sellers.json specification — IAB Tech Lab (GitHub), version 1.0, July 2019. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  28. SupplyChain Object — IAB Tech Lab (GitHub, OpenRTB). Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  29. Transparency & Consent Framework — IAB Europe. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎

  30. Global Privacy Protocol — IAB Tech Lab. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  31. IAB/4A’s Standard Terms and Conditions for Internet Advertising, Version 3.0 — 4A’s and IAB, 2010 (PDF). Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  32. TCF Control Module — Prebid.org. Checked 2026-10-23. ↩︎ ↩︎ ↩︎

  33. Better Ads Standards — Coalition for Better Ads. Checked 2026-10-23. ↩︎ ↩︎

  34. Pricing rules — Google Ad Manager Help. Checked 2026-10-23. ↩︎ ↩︎ ↩︎

  35. VAST: Video Ad Serving Template — IAB Tech Lab. Checked 2026-10-23. ↩︎ ↩︎ ↩︎

  36. Digital Video Multiple Ad Playlist (VMAP) — IAB Tech Lab. Checked 2026-10-23. ↩︎

  37. VAST 4.3 specification — IAB Tech Lab (GitHub), sections 1.1.1–1.1.3. Checked 2026-10-23. ↩︎ ↩︎ ↩︎

  38. OpenRTB 2.x implementation guide, 7.6 Pod Bidding for Video and Audio — IAB Tech Lab (GitHub). Checked 2026-10-23. ↩︎

  39. Open Measurement SDK — IAB Tech Lab. Checked 2026-10-23. ↩︎ ↩︎

  40. Authorized Sellers for Apps (app-ads.txt) — IAB Tech Lab (GitHub). Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎

  41. ads.txt 1.1 Implementation Guide — IAB Tech Lab, released August 2022 (PDF). Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎

  42. The Hunt for 3ve — Google and White Ops, November 2018 (PDF). Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  43. Consent Management – TCF — Prebid.org. Checked 2026-10-23. ↩︎ ↩︎ ↩︎

  44. IAB Tech Lab Spiders and Robots List — IAB Tech Lab. Checked 2026-10-23. ↩︎

  45. Counting impressions and clicks — Google Ad Manager Help. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  46. Troubleshoot unfilled impressions in Ad Manager — Google Ad Manager Help. Checked 2026-10-23. ↩︎ ↩︎ ↩︎

  47. Price Granularity — Prebid.org. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  48. Send All Bids vs Top Price — Prebid.org. Checked 2026-10-23. ↩︎ ↩︎

  49. Prebid Timeouts — Prebid.org. Checked 2026-10-23. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  50. Supply Chain Object Module — Prebid.org. Checked 2026-10-23. ↩︎ ↩︎

  51. MRC Supplement to IAB Guidelines for the Conduct of Ad Verification: Enhanced Content Level Context and Brand Safety — MRC with 4A’s, ANA and IAB, September 2018 (PDF). Checked 2026-10-23. ↩︎ ↩︎

  52. pbjs.setConfig(options) — Prebid.org Publisher API reference. Checked 2026-10-23. ↩︎ ↩︎

  53. prebid.js package metadata (dist-tags) — npm registry: latest 11.43.0 (2026-10-19), legacy-10.x 10.29.2 (2026-10-06). Checked 2026-10-23. ↩︎

  54. Google and FBI shut down “3ve” ad-fraud operation — The Hacker News (Swati Khandelwal), 2018-11-28. Checked 2026-10-23. ↩︎

  55. Programmatic Advertising’s “Unknown Delta” Drops to Three Percent in Second ISBA/PwC Study — VideoWeek, 2023-01-18. Checked 2026-10-23. ↩︎

  56. United States v. Google LLC, 1:23-cv-00108 (E.D. Va.) — court docket, CourtListener RECAP copy: ECF 1860 (unsealing order, 2026-09-16); latest entry ECF 1861 (2026-10-02). Checked 2026-10-23. ↩︎ ↩︎

  57. The Court Just Unsealed Judge Brinkema’s Remedies Decision In The Google Ad Tech Antitrust Case — AdExchanger (Allison Schiff), 2026-09-16. Checked 2026-10-23. ↩︎

  58. Is the future agentic? Understanding new standards for AI in digital advertising — IAB UK, 2026-03-10. Checked 2026-10-23. ↩︎

Related Ops

MarTechOps

MarTechOps (marketing technology operations) designs and runs the plumbing of the marketing stack: how tools connect, how customer data and consent signals flow between them, which tags fire, and which tools the company keeps.

MarketingOps

MarketingOps (marketing operations) runs marketing as a dependable system: campaigns built and checked to a standard, leads captured with consent, scored and followed up by sales under an agreed service level, and pipeline measurement people can trust.

MediaOps

MediaOps is the operation of time-critical media pipelines: getting audio and video from source to screen through ingest, quality control, encoding, packaging and delivery, on time, within rights and at acceptable quality and cost.

PrivacyOps

PrivacyOps is a vendor-coined name for running a privacy programme as an operation: knowing which personal data you hold and why, and meeting each person's rights, the impact assessments and breach duties on time, with evidence.

AnalyticsOps

AnalyticsOps has no standard definition. On this page it means operating the layer where people consume data: versioned metric definitions, a semantic layer that serves them, and BI content that is certified, monitored and retired, so each important metric has one trusted definition.

RevOps

RevOps (revenue operations) aligns marketing, sales and customer success around one set of funnel definitions, one trusted customer record, governed hand-offs and one forecast, so the revenue process can be measured and improved end to end.

ContentOps

ContentOps (content operations) is the people, process and technology that turn a content plan into accurate, findable content at scale: modelled once, reviewed by named owners, published to every channel, measured, and retired when it goes stale.

WebOps

WebOps is a loose label for running websites: the path from DNS through CDN, TLS and web servers to the browser, operated by developers and marketers who share one site. No body defines it; vendors also use it as a product category.

Practise and get certified

From xOps School

Practise AdOps and get certified

The AdOps track on xOps School (part of the Coffee Pass) has study notes, a 25-question practice quiz with explanations, and a verifiable certificate when you score 70% or more. Sign in with Google to start.

For teams

Instructor-led training

Live batches for your team, with completion certificates and reports for L&D/HR.

Source: master.md (the editorial source of this page) · built 2026-10-23 · diagrams rendered with @mermaid-js/mermaid-cli 11.17.0 (mermaid 11.17.2)