On October 28, 2026, Digiday reported that most CTV bid requests still carry genre as free text or channel-level labels that do not match the program. IAB Tech Lab’s answer on the wire is not a longer string. It is a pair of integer arrays plus a version field: cat with cattax, and genres with gtax. If you treat 712 as “Sports” without reading which taxonomy table applies, you are guessing about inventory the buyer will block or bid on.
OpenRTB never promised that category codes were stable across releases. It promised that the receiver could look up the code in the table named by cattax or gtax. That lookup is the versioning story practitioners skip when they copy CMS tags into site.cat and call the request done.
What cat actually is
cat is an array of integers. Each integer is a row in an IAB Content Taxonomy spreadsheet, not a universal label. The taxonomy in use is declared separately, on the same object, in cattax.
You can attach categories at several layers on one bid request:
-
Site or App:
site.cat/app.catwith matchingsite.cattax/app.cattaxfor the property. -
Publisher:
publisher.catandpublisher.cattaxwhen the exchange models the seller separately. -
Content:
content.catandcontent.cattaxfor the specific video, article, or episode in view. -
Producer:
producer.catandproducer.cattaxwhen the production entity differs from the publisher. -
Bid response:
bid.catandbid.cattaxwhen the buyer echoes what they believe they bought.
OpenRTB 2.6 standardized cattax on all of those objects. A validator that only checks “is this integer in any taxonomy CSV” will pass codes that mean different verticals depending on which object omitted cattax.
The spec text is explicit: if no cattax is supplied, IAB Content Category Taxonomy 1.0 is assumed. That default is not a hint. It is the table your partner’s block list uses when they never upgraded from 1.0 mappings.
Genre is a different column, with a different default
Long-form video added a second labeling path on Content. Legacy genre remains free text. For programmatic genre targeting, OpenRTB 2.6-202501 added:
-
content.genres: integer array of genre ids from the taxonomy named bygtax. -
content.gtax: which AdCOM category taxonomy applies to those ids.
Implementation guidance in the OpenRTB 2.x implementation notes tells sellers to keep property-level categories in site.cat / app.cat, use content.genres for program genre, and declare gtax so DSPs know which table to read. The CTV genre list is a subset of Content Taxonomy 3.1, not a reuse of cat codes from 1.0.
Here is the asymmetry that breaks production QA. cattax defaults to Taxonomy 1.0. gtax defaults to Content Taxonomy 3.1 (AdCOM list value 9). On one Content object you can therefore ship 3.1 genre ids that receivers interpret correctly while 3.1 category ids in content.cat are read as 1.0 rows because content.cattax was omitted. Schema validation often stays green; the buyer’s suitability engine does not.
Example shape (one field is wrong on purpose):
"content": {
"title": "Friday Night Match",
"cat": [483],
"genres": [712],
"gtax": 9
}
Without content.cattax, 483 is a 1.0 category. With gtax: 9, 712 is read against 3.1’s CTV genre subset. A DSP blocking “Sports” in 1.0 does not see the same signal as a contextual buyer targeting genre 712 in 3.1. Loss reason 209 (category blocked) without the cattax from that request is unreadable noise.
How AdCOM lists tie the wire together
OpenRTB 2.6 moved enumerated lists into AdCOM. The Category Taxonomies list is what cattax and gtax index. Values cover Content Taxonomy 1.0, 2.0, 2.2, 3.0, 3.1, vendor tables, and custom taxonomies when both sides agree.
When Google Ad Manager started populating site.cat, site.pagecat, and app equivalents on Authorized Buyers traffic, it also set cattax to the 1.0 mapping on those requests. Header bidding paths that map CMS tags into ortb2.site.content without the same cattax create a split brain: ADX sees 1.0-labeled inventory while Prebid-sourced requests look valid but label a different vertical. Our write-up on GAM pagecat on ADX versus Prebid ortb2 walks through that hop mismatch.
Migrating to 2.2 or 3.1 category ids requires setting cattax to the matching list value everywhere you send cat, not only on content. Copying 2.2 ids into cat while leaving the default 1.0 assumption is how “valid” requests fail brand suitability silently.
The 2.6-202501 release is the dated snapshot that added gtax and genres. Partners on older OpenRTB builds may ignore those fields entirely and fall back to free-text genre, which is exactly the signal Digiday described as unreliable.
How you catch it
An LLM generating OpenRTB from a brief can emit plausible integers and still pick the wrong default table. The RPC or the exchange adapter can return OK while the downstream DSP reads a different category than the seller intended.
RTBlint is the deterministic check on the bid object: cattax enums, co-occurrence of cat with cattax, genres with gtax, and dated-snapshot fields for the OpenRTB version you declare. It is independent of IAB Tech Lab and of AgenticAdvertising.org; it does not replace their specs, and they do not require it. It is what you run when the agent’s JSON needs to match what the exchange actually parses.
$ rtblint validate --version 2.6-202606 bid.json
Use the OpenRTB version picker before you argue about a field: 2.6-202501 added genre taxonomy fields; 2.6-202606 added liveness attributes on Content that do not replace gtax.
Rule docs for the 202501 content additions live under content.gtax and content.genres. For broader AdCOM context on where cat and cattax sit in the object model, see the AdCOM reference. The guides index collects supply-path and OpenRTB migration notes when you need a checklist beyond one malformed attribute.
Where to go next
- Read the 2.6-202501 content object review before you map CTV genre ids.
- Align header bidding
ortb2category mapping with thecattaxyour ADX or SDK path sends. - When debugging blocks, log
cattaxandgtaxalongsidecatandgenreson the same request id. - Pick a dated snapshot in OpenRTB versions and validate fixtures against it in CI.
The integer was never the category. The selector beside it was.
Top comments (0)