VASTlint
Back to blog
Viewability/11 min read

How to Validate ViewableImpression in VAST Tags

ViewableImpression URLs that are empty or HTTP-only fail the viewability join even when quartiles fire.

Author

Alex Sekowski

Published

September 19, 2026

Updated

September 19, 2026

Reading time

11 min read

ViewableImpressionVAST 4.0ViewabilityHow-toCTV

A CTV line item can show one hundred percent quartile completion in the DSP while the viewability vendor reports undetermined on the same impressions. The trafficking export lists ViewableImpression because someone migrated the tag to VAST 4.0. The player fired start, firstQuartile, midpoint, thirdQuartile, and complete on schedule. The viewability dashboard stayed flat because the URLs that were supposed to carry the MRC-style state never executed.

The production bug is rarely a player that refuses to measure viewability on principle. It is a ViewableImpression subtree that exists in XML but cannot complete the join: empty CDATA inside Viewable, NotViewable, or ViewUndetermined, HTTP-only asset hosts on HTTPS-only CTV apps, creative tracking pasted under ViewableImpression where the schema allows only the three viewability children, or a wrapper chain that drops the block while quartile TrackingEvents survive on the merged InLine.

IAB Tech Lab introduced ViewableImpression in VAST 4.0 as a dedicated container for viewability-state beacons, separate from the standard Impression URL and separate from quartile TrackingEvents. VAST 4.3 refined semantics and wrapper-chain handling for server-side insertion environments. This piece is the envelope check: where ViewableImpression lives, which child URLs must be fetchable before a player or measurement script can classify an impression, and what validation can catch while linear video still plays normally. It is not a substitute for OMID; AdVerifications remains the path for third-party viewability scripts.

Why hollow viewability beacons look like a player bug

Buyers reconcile viewability in a different pipeline than delivery. DSP and ad-server dashboards count starts and completes. Viewability vendors and in-house MRC-style reports count Viewable, NotViewable, and ViewUndetermined firings against their thresholds. When only quartiles arrive, delivery looks healthy and viewability looks broken, which sends engineers to firmware and SDK release notes before anyone pastes the resolved VAST.

That delay is expensive because quartile URLs and viewability URLs share the same trafficking template in many tools. A generator that copies the complete beacon into TrackingEvents and leaves ViewableImpression as an empty placeholder satisfies a spreadsheet column named viewability without giving the player anything to ping when OMID is absent.

SSAI paths make the gap worse. Stitchers that rebuild InLine from MediaFiles and Impression often preserve quartile TrackingEvents because those URLs are wired into transcode and pod timing. ViewableImpression is easier to drop as optional metadata from an upstream wrapper. The living-room device plays the spot and fires completes while the viewability join never receives a non-empty HTTPS URL.

Open auction metadata does not rescue the join. Sellers expose viewability measurement in deal notes or verification product flags while the VAST the player resolves carries ViewableImpression with blank children. The buy looked measured. The classification pipeline had nothing to classify.

What ViewableImpression actually measures

ViewableImpression is not the impression pixel. The InLine Impression element fires on load or on a player-defined threshold that may or may not align with MRC viewability rules. ViewableImpression holds up to three optional child URLs, Viewable, NotViewable, and ViewUndetermined, that fire when the player or an integrated measurement layer decides which state applies.

Quartile TrackingEvents measure media progress against Duration. They do not prove that fifty percent of pixels were in view for two continuous seconds. A tag can fire midpoint on a minimized outstream unit or on a CTV panel where the ad rendered off-screen. ViewableImpression is where the document declares separate endpoints for viewability classification, when the stack implements VAST 4.x viewability semantics at all.

The population that matters for QA is the resolved InLine the player parses after redirects and after SSAI, not the first wrapper hop and not the trafficking PDF. Validating ViewableImpression on hop one while the final chain omits the block is the same category error as validating UniversalAdId only on the entry URL.

OMID AdVerifications can run parallel to ViewableImpression or replace reliance on the three child URLs entirely, depending on the verification vendor and player. Many CTV stacks prefer OMID for third-party viewability while still accepting ViewableImpression for legacy server-side classification. Empty ViewableImpression plus missing AdVerifications leaves you with quartiles only, which is the failure mode this check targets.

Where ViewableImpression lives and what the player reads

In typical VAST 4.x linear traffic, ViewableImpression sits at InLine level alongside Impression, Error, Creatives, and Extensions, not inside Linear TrackingEvents. That placement matters because players walk different trees for creative progress beacons versus ad-level viewability containers. Pasting a Tracking event under ViewableImpression violates the schema and can cause strict parsers to reject the entire block.

Each allowed child is a URI element: CDATA holds the HTTPS URL the player or measurement layer should request when the corresponding viewability state is determined. Viewable fires when the ad meets the implemented viewability definition. NotViewable fires when measurement completes and the ad did not qualify. ViewUndetermined fires when the environment cannot decide, which is common on SSAI-only paths without client-side confirmation.

The optional id attribute on ViewableImpression helps when multiple viewability vendors appear in one document. Duplicate ids or duplicate Viewable URLs across wrapper hops can double-count in downstream logs if the player merges chains naively. VAST 4.3 guidance emphasizes clearer wrapper handling; operational QA still needs the merged view the device will see.

Nothing in ViewableImpression executes at ad-server response time beyond XML parsing. The player selects MediaFile, begins playback, evaluates viewability through OMID or native APIs if present, and then fires the appropriate child URL. If every child is empty or uses HTTP on a secure app, most stacks skip the viewability join silently and continue firing quartiles.

What ViewableImpression validation can catch before playback

  • ViewableImpression containing anything other than Viewable, NotViewable, and ViewUndetermined.
  • Empty CDATA on any populated viewability child (VAST-2.0-url-empty class findings on URI fields).
  • HTTP viewability beacon URLs on HTTPS-only CTV inventory.
  • Malformed URLs that fail URI parsing while quartile TrackingEvents on the same Linear still look valid.
  • ViewableImpression present on the trafficking export but absent on the unwrapped live InLine or post-SSAI output.
  • Viewability children present but every URL blank while Impression and complete still fire (spreadsheet false positive).
  • Creative TrackingEvents mistakenly nested under ViewableImpression instead of Linear TrackingEvents.
A ViewableImpression block that should pass validation
xml
<InLine>  <Impression><![CDATA[https://track.example.com/impression]]></Impression>  <ViewableImpression id="viewability-vendor-a">    <Viewable><![CDATA[https://track.example.com/viewable]]></Viewable>    <NotViewable><![CDATA[https://track.example.com/not-viewable]]></NotViewable>    <ViewUndetermined><![CDATA[https://track.example.com/undetermined]]></ViewUndetermined>  </ViewableImpression>  <Creatives>    <Creative>      <Linear>        <Duration>00:00:30</Duration>        <TrackingEvents>          <Tracking event="complete"><![CDATA[https://track.example.com/complete]]></Tracking>        </TrackingEvents>      </Linear>    </Creative>  </Creatives></InLine>

What a player does with that viewability envelope

After the InLine resolves, the player fires Impression according to its own rules, starts linear playback, and evaluates viewability through OMID verification scripts and/or native viewability APIs where implemented. When classification completes, it requests the matching Viewable, NotViewable, or ViewUndetermined URL.

Quartile and complete TrackingEvents on Linear proceed independently. A valid ViewableImpression block does not guarantee MRC-compliant numbers on every device; it guarantees the tag declared fetchable endpoints for the three states the VAST 4.x model expects.

If all three child URLs are non-empty HTTPS endpoints, downstream vendors can still disagree on counts when wrapper merges duplicate beacons or when SSAI fires server-side impressions without client confirmation. Envelope validation proves the document handed the stack a complete viewability contract in XML shape; it does not prove agreement between verification vendors.

Get VAST spec updates, platform guides, and release notes in your inbox.

A ViewableImpression block that lists viewability but should be flagged
xml
<ViewableImpression id="vi1">  <Viewable><![CDATA[]]></Viewable>  <NotViewable><![CDATA[http://track.example.com/not-viewable]]></NotViewable>  <Tracking event="viewable">    <![CDATA[https://track.example.com/wrong-node]]>  </Tracking></ViewableImpression>

Why that block still looks compliant in a spreadsheet

Trafficking exports often reduce viewability to a yes/no column: ViewableImpression present. The container exists and id is set, so the row looks migrated to VAST 4.0. Viewable CDATA is empty, so the primary state beacon has nothing to fetch even if the player implements native viewability.

NotViewable uses HTTP, which mixed-content rules block on many HTTPS CTV apps. The stray Tracking node under ViewableImpression is not a valid child; parsers that strict-reject the container drop the entire block, including the partially valid NotViewable entry, while Linear TrackingEvents elsewhere in the document continue to fire.

Fix order: move creative tracking back under Linear TrackingEvents, require non-empty HTTPS URIs on each viewability child you intend to use, remove unknown children, and re-run validation on the unwrapped InLine the player will see after SSAI.

ViewableImpression versus OMID and SSAI

MRC viewability on CTV often depends on OMID AdVerifications or on vendor SDKs embedded in the app, not on VAST child URLs alone. SSAI guidance from measurement bodies stresses that server-fired beacons cannot confirm viewability the way client-side confirmation can, which is why client beacons remain recommended even under stitchers.

ViewableImpression still matters for stacks that map native viewability results to the three URLs without loading a full verification script, and for legacy integrations that never adopted AdVerifications. Validating the container catches the case where someone added the 4.0 element for compliance optics but left every URL blank.

Do not treat a passing ViewableImpression check as OMID readiness. JavaScriptResource under Verification carries apiFramework omid, vendor codes, and executable URLs. That hop is documented in the AdVerifications how-to. This hop is the simpler tri-state beacon envelope.

A pre-launch ViewableImpression checklist

  • Declared VAST version is 4.0+ and ViewableImpression sits at InLine level on the document the player resolves.
  • Each viewability child you rely on has non-empty HTTPS CDATA and parses as a valid URI.
  • ViewableImpression contains only Viewable, NotViewable, and ViewUndetermined; quartile beacons stay under Linear TrackingEvents.
  • The unwrapped live chain, including post-SSAI output, still contains the same ViewableImpression subtree as the trafficking export.
  • If the buy requires third-party viewability, AdVerifications is present and OMID-capable, not only empty ViewableImpression placeholders.
  • Wrapper merge review: duplicate Viewable URLs across hops are intentional or deduped in player config.
  • Compare viewability vendor logs against resolved VAST when quartiles fire but viewability stays undetermined.

What viewability envelope validation cannot prove

A valid ViewableImpression block is necessary, not sufficient. XML checks do not download every beacon, confirm MRC thresholds on each CTV firmware build, or prove OMID scripts executed. They will not catch a stitcher that removes ViewableImpression after you validated the upstream chain.

They also will not replace vendor-side reconciliation when Impression fires from the ad server while viewability fires from the client. Timing skew between those hops is a measurement design problem, not something URI validation resolves.

If complete fires and viewability still reports undetermined, compare the resolved InLine against the trafficking export. If the video never starts, fix MediaFiles first. If the video plays and ViewableImpression is absent or hollow on the live chain, this checklist is the one.

Validate ViewableImpression in your VAST tag

Paste a tag or fetch a live URL. vastlint checks ViewableImpression shape, URI fields, and allowed children on the unwrapped chain before launch.

Open the VAST validator

Related docs on vastlint

IAB VAST standardIAB Tech Lab

VAST 4.0 ViewableImpression for viewability-state tracking URLs.

ViewableImpression may only contain Viewable, NotViewable, and ViewUndetermined.

Empty URI fields on viewability children fail silently at fetch time.

ViewableImpression introduction and example tri-state beacons.

Keep reading

Related stories

All posts