A VAST 4.x InLine can play a clean MP4 and still be useless for frequency capping. The creative identity that buyers, sellers, and verification vendors are supposed to share lives in UniversalAdId, not in Creative id, not in Ad id, and not in the filename on the CDN. If that element is missing, stored in the wrong place for the declared version, or rewritten on every stitch, three systems will log three different creatives for one spot.
IAB Tech Lab made UniversalAdId required inside InLine Creative from VAST 4.0 onward. Wrappers do not carry it. SSAI packs that downgrade 4.x to 2.0 or 3.0 for an old player have no home for the element, so the identity is stripped even when media and duration survive. The session looks healthy in the player and broken in the frequency and competitive-separation reports.
The usual production bug is not a missing MP4. It is a template that still emits the VAST 4.0 idValue attribute, sets version="4.2", and leaves the element body empty. Parsers that only read text content see no identifier. This piece is the envelope check: where the ID has to live, which rule fires, and what XML validation cannot prove about Ad-ID itself.
Why creative identity is a money problem
Ad servers, DSPs, SSPs, and verification vendors each mint their own creative IDs. None of them match. UniversalAdId is the cross-system identifier, typically an Ad-ID registry code in the US or a Clearcast code in the UK. Frequency caps that are supposed to fire across two apps and a CTV channel need that one ID. Without it, a viewer who saw the same :15 three times looks like three unrelated exposures.
Pod dedup is the same join with a tighter clock. Two DSPs can win adjacent slots in one break with the same asset under different internal IDs. Competitive separation (two car brands out of the same break) has the same requirement: the pod logic has to know what the creatives actually are, not what each seller labeled them in its private namespace.
Audits and brand-safety reviews need a join key that survives a quarter of delivery logs from different vendors. At IAB Tech Lab's August 2026 Ad Ops workshop, buyer or advertiser demand and better frequency or separation controls each led at 33% when the room was asked what would make UniversalAdId worth enforcing. Trafficking will not put the element on every tag until those controls fail visibly.
Version downgrades are the quiet kill. A 4.2 InLine that is re-rendered as VAST 3.0 for a device that "does not support 4.x" drops UniversalAdId because the older schema has no such child. MediaFile still plays. The identity does not. That is the same class of failure as a version mismatch the player already printed, except nobody looks at identity when the video started.
The identifier lives on the InLine, after the wrappers
A Wrapper is allowed to be thin. Impression pixels on hop one are real. Duration is not on the wrapper. MediaFiles are not on the wrapper. UniversalAdId is not on the wrapper. Checking a pasted VASTAdTagURI document for Creative identity is a category error: the element you need is on the InLine at the bottom of the chain.
SSAI makes that worse because the stitcher is a new publisher of VAST. It may fetch the chain, choose renditions, and emit a new InLine to the player. If that generated document copies Creative id from the ad server and omits UniversalAdId, or copies idValue into a 4.2 document with empty text, the player never sees the registry code. Frequency and verification vendors that read the stitched tag, not the pre-stitch InLine, join on nothing.
The working split is the same as wrapper QA. File in git or a trafficking export you already unwrapped: validate the InLine. Partner URL: fetch it, then inspect hops until you see the InLine Creative. A green wrapper paste is compatible with a missing UniversalAdId three hops down.
What UniversalAdId XML validation can catch before playback
- Creative in a VAST 4.x InLine with no UniversalAdId.
- UniversalAdId missing idRegistry.
- VAST 4.0 document with no idValue attribute.
- VAST 4.1+ document with empty text content.
- VAST 4.1+ document that still uses idValue (warning; parsers that only read text content see an empty ID).
- idRegistry claiming Ad-ID while the value is an internal ad-server token.
VAST 4.0 still uses the idValue attribute
VAST 4.0 stores the identifier on idValue and keeps idRegistry as the required registry name. Text content may repeat the same code; when both are present they must agree. A 4.0 Creative with idRegistry and no idValue is a spec miss, not a style choice.
Keep 4.0 templates on 4.0. Relabeling version="4.1" without moving the value into text content is the migration bug IAB documented when 4.1 shipped. The schema removed idValue. A document that still has the attribute and an empty body is invalid as 4.1 even if it was valid as 4.0.
If you still have to emit 4.0 for a specific player, emit 4.0 honestly. Do not declare 4.2 and internally serialize 4.0. Downstream XML parsers will pick one shape. They will not merge attribute and text for you.
<Creative id="5480" sequence="1"> <UniversalAdId idRegistry="Ad-ID" idValue="CNPA0484000H">CNPA0484000H</UniversalAdId> <Linear> <Duration>00:00:15</Duration> <MediaFiles> <MediaFile delivery="progressive" type="video/mp4" width="1920" height="1080"> <![CDATA[https://cdn.example.com/video.mp4]]> </MediaFile> </MediaFiles> </Linear></Creative>VAST 4.1 moved the value into text. 4.2 allows more than one.
From VAST 4.1 the identifier is the element's text content. idRegistry stays required. Leftover idValue is a warning because strict parsers ignore the attribute and then find an empty body. VAST 4.2 also allows multiple UniversalAdId children on one Creative so a spot can carry Ad-ID and Clearcast at once.
That multiplicity is for registries, not for minting a new code per hop. Two UniversalAdId elements with the same registry and different values are a fight. Two elements with Ad-ID and clearcast.co.uk are the intended 4.2 case: one asset, two industry codes.
SSAI output that sets version="4.2", keeps idValue, and leaves the body empty is the document in the next block. It often still has a Creative id the ad server understands. The player plays. The join key is gone.
<Creative id="5480" sequence="1"> <UniversalAdId idRegistry="Ad-ID">CNPA0484000H</UniversalAdId> <UniversalAdId idRegistry="clearcast.co.uk">AAA/BBBB123/030</UniversalAdId> <Linear> <Duration>00:00:15</Duration> <MediaFiles> <MediaFile delivery="progressive" type="video/mp4" width="1920" height="1080"> <![CDATA[https://cdn.example.com/video.mp4]]> </MediaFile> </MediaFiles> </Linear></Creative><VAST version="4.2"> <Ad> <InLine> <Creatives> <Creative id="5480"> <UniversalAdId idRegistry="Ad-ID" idValue="CNPA0484000H"></UniversalAdId> <Linear> <Duration>00:00:15</Duration> <MediaFiles> <MediaFile delivery="progressive" type="video/mp4" width="1920" height="1080">https://cdn.example.com/video.mp4</MediaFile> </MediaFiles> </Linear> </Creative> </Creatives> </InLine> </Ad></VAST>Get VAST spec updates, platform guides, and release notes in your inbox.
What a player that only reads text content does with that 4.2 tag
The element is present, so a shallow "do we have UniversalAdId" check can pass. idRegistry is present, so a registry check can pass. The identifier the 4.1+ schema actually carries is the text node, and that node is empty. Frequency systems that parse 4.2 correctly treat the creative as unidentified.
Verification vendors that join on UniversalAdId will not match the pre-stitch InLine that still had idValue. Seller logs that key on Creative id will still show the campaign. The two reports disagree, and the disagreement looks like a measurement outage instead of a schema migration miss.
Fix the template: copy idValue into the element body, drop the attribute on 4.1+, and keep version honest. Then re-run the stitched output, not only the pre-stitch package.
What the UniversalAdId rule set should complain about
- VAST-4.0-universaladid-present because the Creative has no UniversalAdId on a 4.x InLine.
- VAST-4.0-universaladid-idregistry because idRegistry is missing.
- VAST-4.0-universaladid-idvalue because a 4.0 document omitted the idValue attribute.
- VAST-4.1-universaladid-content because a 4.1+ document has no text content.
- VAST-4.1-universaladid-idvalue-removed because idValue is still present on 4.1+.
UniversalAdId is not AdServingId, and not Creative id
Creative id identifies the creative inside one ad server. Ad id identifies the buy wrapping it, also private. Those attributes are useful in that vendor's UI. They are not a cross-platform join key. Copying Creative id into UniversalAdId and setting idRegistry="Ad-ID" claims a registry you do not have.
AdServingId, added in VAST 4.1, identifies one serving transaction so player, verification, and ad-server logs can join a single impression. It should be unique per response, typically a GUID minted at serve time. Regenerating UniversalAdId on every response turns the asset ID into a session ID. Use AdServingId for the session. Keep UniversalAdId stable across campaigns and hops.
If you do not have a registry code, idRegistry="unknown" with your durable internal creative ID is honest. It will not join Ad-ID graphs. It also will not poison them. Unknown plus a stable value is still better than a new GUID per stitch.
A pre-launch identity checklist
- Declared VAST version matches the UniversalAdId shape: idValue on 4.0, text content on 4.1+.
- InLine Creative has at least one UniversalAdId with a non-empty idRegistry.
- If idRegistry is Ad-ID or Clearcast, the value is a real registry code, not the ad-server Creative id.
- The same identifier is on the stitched InLine the player will see, not only on the pre-SSAI package.
- Wrappers in the chain are not the place you look for identity; inspect until the InLine.
- AdServingId, if present, changes per impression. UniversalAdId does not.
vastlint check creative.xml --format json vastlint check creative.xml --fail-on-warningWhat UniversalAdId XML validation cannot prove
A well-formed element is necessary. It is not a certificate that Ad-ID issued the code, or that two vendors share a join table. XML checks will not call the registry. They will not tell you the buyer actually caps on UniversalAdId rather than on a private creative ID.
They also will not tell you the stitcher preserved the element. Validate the document the player fetches, after SSAI, on the version the player declared. A clean package in the creative tool and a stripped InLine on the device are compatible.
If the player already printed a version error, start from the version mismatch post. Identity is downstream of a document the player refused to parse. If the video played and the frequency report looks like a new creative every session, this checklist is the one.
Related docs on vastlint
Why creative identity is a money problem, syntax by version, and common mistakes.
Creative must contain UniversalAdId on VAST 4.0+ InLine.
Text content is required in VAST 4.1+.
idValue removed, value moves to text content.
Migration notes, including multiple UniversalAdId elements.
Identity lives on the InLine. Fetch the chain before you declare the tag identified.
August 2026 workshop recap: UniversalAdId demand and frequency or separation controls each at 33%.
Primary specification for UniversalAdId.
Check the Creative identity
Paste the InLine the player will see, not only the wrapper. Look for UniversalAdId presence and the version-correct identifier shape.
Open the VAST validator