Blog · Protocol

A publisher captured bid request is not the request the DSP bid on.

An engineer at a publisher owns the page, the Prebid ad unit, and the request that left the browser or Prebid Server. They do not own the exchange adapter that runs after that hop, and they do not own Amazon TAM or Google Open Bidding. The JSON they can export is the artifact the tester can judge.

Paste the request you built, and set the dialect before you trust a pass

The bid request tester runs the same engine as the CLI, in the browser. Validation itself is local. Payloads you paste may be stored with device IDs, IPs, and consent strings stripped, so a capture that still contains device.ifa or a live IP belongs on rtblint validate on a laptop. The CLI, the library, and the gRPC sidecar do not send payloads. Local rtblint-mcp over stdio does not either. Hosted MCP at rtblint.org/mcp can store what an agent sends, and the tester opt-out does not cover that path.

Set the version to the snapshot you claim to send, usually a tracked 2.6 tag such as 2.6-202505, not a floating latest. Set the dialect to Spec JSON when the file uses integer flags. Set it to Protobuf JSON when the file came off a gRPC bidstream and flags are true and false. The specification types those flags as integers. The IAB protobuf schema types 28 of them as bool, including imp.secure, regs.gdpr, regs.coppa, source.fd, pmp.private_auction, and the supply-chain complete and hp flags. A payload that is legal in one dialect is a type error in the other, and the error is on the whole message.

Set the profile to Spec unless you are looking at a Prebid Server auction body. The Prebid Server profile requires each impression to name a bidder on imp.ext.prebid.bidder, a legacy imp.ext bidder object, or a stored request, and it refuses wseat and bseat. A spec pass on a Prebid Server payload can hide a missing bidder. A Prebid Server pass on a plain exchange request will fail that same request for fields the exchange is allowed to send. The profile is part of the question. A green badge with the wrong profile is a different auction.

The fields a publisher capture can actually get wrong

A bid request needs id and a non-empty imp array. Each impression needs its own id, unique inside that request, and at least one of banner, video, audio, or native. An impression object pasted where the array should be is invalid. Several media types on one impression are allowed. A bid still has to conform to exactly one of them.

The request must not carry more than one of site, app, and dooh. They answer the same question, and many bidders drop a request that carries two. The usual cause on a publisher stack is a merge: a stored site object plus a live app object, or a default site left in the Prebid Server stored request while the client sent an app. The capture you paste is the place that merge is visible. The DSP never tells you which object it kept.

at is an integer. 1 is first price. 2 is second price plus. Values of 500 or greater are exchange-specific. Omitted, the default is 2. A string "2" is a type error for a parser that follows the spec. The same string bug shows up on test and tmax. test defaults to 0. test of 1 marks the auction non-billable. A debug capture left on 1 is a request the exchange must not turn into spend, even when bids return.

Secure, placement, and the privacy flags moved

imp.secure is an integer. 1 means the impression requires HTTPS creative assets and markup. 0 means non-secure is acceptable. If the field is omitted, the secure state is unknown and non-secure HTTP support can be assumed. Sending true is the protobuf dialect, not Spec JSON. A publisher on an HTTPS page who omits the field has told bidders that HTTP creatives are allowed. The page being HTTPS does not fill the field.

imp.video.placement was deprecated in the 2.6-202303 update in favor of imp.video.plcmt. The lists do not map one to one. Plcmt 1 is instream, 2 is accompanying content, 3 is interstitial, 4 is no content or standalone. Copying the old integer into the new field classifies the impression as a different subtype. Buyers key instream off plcmt. A capture that still has only placement is a 2.5 video object wearing a 2.6 version string.

On 2.6, regs.gdpr is an integer on regs: 0 means no, 1 means yes, omitted means unknown. user.consent holds the TCF string. Both moved out of ext. A capture that still writes regs.ext.gdpr and user.ext.consent looks populated in the publisher debug log and empty to a 2.6 reader. regs.gpp and regs.gpp_sid arrived in 2.6-202211. A version pin older than that snapshot will not treat them as core fields. Pin the snapshot you actually emit.

The floor currency and the final-sale flag are on this request

imp.bidfloor is a CPM, default 0. imp.bidfloorcur is an ISO-4217 code, default USD. It does not inherit cur. A floor of 120 with no currency is 120 US dollars. Deal.bidfloorcur does not inherit imp.bidfloorcur either. A euro floor on the impression plus a bare deal floor is a dollar deal. The publisher who set the number in an ad-unit config and left the currency off has set a floor the bidder will honor and the yield report will not explain.

source.fd says who makes the final sale. 0 means the exchange receiving the request makes that decision. 1 means an upstream party still will, which is the honest value when header bidding sits in front of an ad server. Copying one path onto another is how a Prebid request and an Open Bidding request disagree about who clears. Read fd on the capture you have. Do not assume the next hop keeps it.

source.schain is the SupplyChain object on 2.6, promoted from source.ext.schain. Writing only the old path, or writing both paths with different nodes, is a move the checker reports as openrtb.field.moved when you are on a 2.6 snapshot. complete and hp are integer flags in Spec JSON. A publisher can see whether the chain they attached is well formed. They cannot see the node an exchange appends after the request leaves.

A spec pass on this file is silent about the other assemblies

Prebid, Amazon TAM, and Google Open Bidding each build a request. The publisher passes an ortb2 document on the Prebid path. TAM runs an auction on Amazon servers and does not take that document. Open Bidding builds the request inside Google Ad Manager. The same SSP called from Prebid and from a yield group is two requests. imp.id is per auction, often 1, 2, 3, and will not join those paths. source.tid joins them only when you set the same string on each. The comparison is in Prebid vs Amazon TAM vs Google Open Bidding. The tester result belongs to the file you pasted.

bcat, badv, and bapp are per request. A block list in the Prebid config is not a block list in TAM. Only one of acat or bcat should be present. cattax defaults to 1, Content Category Taxonomy 1.0. Taxonomy 2 or 3 IDs with cattax omitted are the wrong list. Ad-server exclusions do not become bcat unless the request builder copies them.

tmax is the exchange budget in milliseconds for the whole round trip, including network. The publisher often does not set it. When they do, a bidder that spends the entire value thinking has already timed out. Downstream hops may only decrease it. A capture taken before that decrease is a larger budget than the bidder received.

What an engineer at a publisher can finish from the capture

  • Export the request the page or Prebid Server actually sent. Redact device.ifa, IPs, and consent strings before a ticket. Use the CLI when the file still has them.
  • On the tester, set the 2.6 snapshot you emit, Spec JSON or Protobuf JSON to match the flags, and Spec or Prebid Server to match the body.
  • Confirm one of site, app, or dooh. Confirm imp is an array and each impression has an id and a media type.
  • Confirm imp.secure is 0 or 1 in Spec JSON, and that an HTTPS page sends 1 rather than omitting the field.
  • Confirm plcmt on video, regs.gdpr and user.consent on the core objects, and source.schain on 2.6.
  • Confirm bidfloorcur on every floor you intend to be non-USD, including each deal. A bare floor is USD.
  • Read source.fd and test on this file. Do not copy them onto a TAM or Open Bidding request you cannot see.

RTBlint is not an IAB validator and it is not affiliated with Prebid, Amazon, or Google. A pass means the artifact matches the snapshot, dialect, and profile you selected. It does not mean a DSP bid, and it does not mean the next hop sent this JSON.

Further reading