Blocking buyers and advertisers with wseat, bseat, badv, and bapp
wseat and bseat decide which buyer seats may bid, badv blocks advertisers by domain, and bapp blocks promoted apps by store ID. Each list is checked against a field the bidder fills in: SeatBid.seat, Bid.adomain, and Bid.bundle. The lists work only when the bidder filters before it prices and labels every bid it returns.
The lists and the fields they are checked against
| Request field | Kind | Since | Checked against | Loss reason |
|---|---|---|---|---|
wseat | Seat allow list for the request | 2.0 | SeatBid.seat | 104 |
bseat | Seat block list for the request | 2.5 | SeatBid.seat | 104 |
badv | Advertiser domain block list | 2.0 | Bid.adomain | 205 |
bapp | App store ID block list | 2.4 | Bid.bundle | 206 |
Deal.wseat | Seat allow list for one deal | 2.3 | SeatBid.seat | Exchange policy |
Deal.wadomain | Advertiser allow list for one deal | 2.3 | Bid.adomain | Exchange policy |
The loss reasons are the codes the current loss list defines for these filters: 104 Buyer Seat Blocked, 205 Advertiser Exclusions, and 206 App Store ID Exclusions. An exchange reports them only if it sends loss notices. The loss reason guide has the full table.
Seat lists: wseat and bseat
A seat is a buyer account at the bidder, usually an agency or an advertiser. Seat IDs are not standardized, so the exchange and the bidder have to agree on them before any request carries one. wseat admits only the listed seats. bseat admits everyone except the listed seats. The specification says a request should use at most one of them, and that omitting both means no seat restriction. RTBlint reports a request that sends both with openrtb.fields.mutually_exclusive.
{
"id": "a41f7c02-9b6e-4d3a-8f15-2e7c90d4b6a8",
"at": 1,
"tmax": 300,
"cur": ["USD"],
"wseat": ["agency-412", "agency-977"],
"badv": ["rival-outdoor.com", "fastloans.example"],
"bapp": ["com.rivalgames.trailrunner", "1489203312"],
"imp": [
{
"id": "1",
"banner": { "format": [{ "w": 320, "h": 50 }] },
"bidfloor": 0.6,
"pmp": {
"private_auction": 0,
"deals": [
{
"id": "dl-summer-outdoor",
"bidfloor": 4.0,
"wseat": ["agency-412"],
"wadomain": ["northpeak-outfitters.com"]
}
]
}
}
],
"app": {
"id": "app-118",
"bundle": "com.trailguide.hikes",
"publisher": { "id": "pub-208" }
}
}Request-level lists cover every impression in the request. Deal.wseat narrows one deal: in this request either allowed seat may bid in the open auction, but only agency-412 may bid on dl-summer-outdoor, and only for northpeak-outfitters.com. The deals and PMP guide covers the rest of the Deal object.
The bidder answers with SeatBid.seat. When a request and its response are validated together, RTBlint reports a seat outside wseat or inside bseat as openrtb.seatbid.seat_not_allowed. A SeatBid with no seat gives the exchange nothing to compare, and what happens to it then is exchange policy.
Advertiser blocks: badv and adomain
badv lists advertisers by domain, and the specification's example is a bare domain such as ford.com. The bidder reports the advertiser in Bid.adomain. It is an array so a rotating creative can list every advertiser it may show, though an exchange can insist on exactly one. On a deal bid, the exchange also checks adomain against Deal.wadomain when the deal has one.
The specification defines no matching rules for subdomains, case, or URLs. Send the advertiser's bare domain, not the click-through URL. If an exchange compares strings exactly, https://www.rival-outdoor.com/ does not equal rival-outdoor.com, and the block fails. A bid with no adomain cannot be checked at all. Some exchanges reject it; Prebid Server's ORTB2 Blocking module makes that a setting, block-unknown-adomain, which acts only when enforce-blocks is on.
App blocks: bapp and bundle
bapp lists app store IDs. For Google Play the ID is the package name, such as com.rivalgames.trailrunner. For the Apple App Store it is the numeric ID. CTV stores follow the IAB Tech Lab guidelines for store-assigned app IDs. The bidder reports the promoted app in Bid.bundle, in the same format.
Do not confuse Bid.bundle with App.bundle. The request field identifies the app where the ad will show. bapp and Bid.bundle concern the app the ad promotes, for example a rival game's install ad that a game publisher wants kept out. On the current loss list, a store ID exclusion is 206 and a bundle exclusion is 215; the 2.5 list used 206 for bundles.
What honoring the lists means for a bidder
- Read the lists before pricing. Drop campaigns whose seat is outside
wseat, insidebseat, or, for a deal, outsideDeal.wseat. - Drop creatives whose advertiser domain is in
badv. On a deal withwadomain, keep only the listed advertisers. - Drop app promotions whose store ID is in
bapp. - Label every bid: the agreed seat ID in
seat, the advertiser domain inadomain, and the store ID inbundlewhen the ad promotes an app. - Watch loss notices. A steady stream of 104, 205, or 206 means the filters in steps 1 to 3 are missing or stale. Those bids could never win, so the compute behind them was wasted.
{
"id": "a41f7c02-9b6e-4d3a-8f15-2e7c90d4b6a8",
"cur": "USD",
"seatbid": [
{
"seat": "agency-412",
"bid": [
{
"id": "b-7",
"impid": "1",
"price": 4.25,
"dealid": "dl-summer-outdoor",
"adm": "<div class=\"ad\">...</div>",
"adomain": ["northpeak-outfitters.com"],
"bundle": "com.northpeak.trailapp",
"crid": "cr-2210",
"w": 320,
"h": 50,
"mtype": 1
}
]
}
]
}This bid passes every list in the request above: the seat is allowed on the deal, the advertiser is the one the deal names, and the promoted app is not in bapp.
Prebid Server and seat lists
Prebid Server does not accept seat lists. Its auction documentation says it returns HTTP 400 for a request that defines wseat or bseat, and RTBlint's Prebid Server profile reports both fields as forbidden. Prebid Server chooses which bidders see an impression from the bidder codes under imp.ext.prebid.bidder instead. In its responses, SeatBid.seat carries the bidder code, so agency seat information is gone by then. That is why the ORTB2 Blocking module supports badv, bcat, bapp, btype, and battr but not bseat. With enforce-blocks on, it checks adomain, cat, bundle, and attr in bidder responses. The Prebid Server extensions guide shows the request shape.
Mistakes that let blocked demand through
- No
adomainon an exchange that accepts unlabelled bids. The advertiser is never compared withbadv. - A URL or a
wwwhost inadomain. Exact string matching misses thebadventry. - One domain for a rotating creative. The other advertisers in the rotation are never checked.
- No
bundleon an app install ad.bapphas nothing to match. - A package-style name for an iOS app. A
bappentry holding the numeric Apple ID never equalscom.example.app. - Both
wseatandbseat. A bidder that honors only one of them may bid from a seat the other list excludes.
To see a seat check fail, load the mismatched pair sample in the tester. Its seat is outside the request's wseat, and the tester reports openrtb.seatbid.seat_not_allowed.
Sources
Questions people ask
How do wseat and bseat differ?
wseat is an allow list: only the listed buyer seats may bid. bseat is a block list: every seat may bid except the listed ones. The specification says a request should use at most one of them, and omitting both means no seat restriction. The bidder names its seat in SeatBid.seat so the exchange can check it.
What should Bid.adomain contain?
The advertiser's domain, written the way the specification's example writes it: a bare domain such as ford.com, with no scheme or path. It is an array so a rotating creative can list every advertiser it may show, although an exchange can require exactly one. Without it the exchange cannot check the bid against badv.
Does Prebid Server support wseat and bseat?
No. The Prebid Server auction documentation says it returns HTTP 400 for requests that define wseat or bseat. It controls which bidders see an impression through the bidder codes in imp.ext.prebid.bidder, and in its responses SeatBid.seat carries the bidder code.