VASTlint

VAST 4.4 and the CTV Ad Portfolio: what changed in NonLinear ads

On 17 July 2026 a file called vast_4.4.xsd was merged into the IAB Tech Lab VAST repository, from a branch named 4.4Development. Five days later, on 22 July, the IAB finalised the CTV Ad Portfolio signaling guidance.

Both matter, and they sit at different points in the same process. The signaling guidance is a finished standard you can build against today. The schema is explicitly labelled a working draft, which is the right label for something still being discussed, and it means tooling should treat the two sources differently.

ArtifactStatusGoverns
CTV Ad Portfolio signaling guidanceFinal, 22 July 2026AdCOM enumerations, Native placement types, VAST prose
vast_4.4.xsdDraftThe XML content model

The schema says so itself. Its annotation reads: “IAB VAST (Video Ad Serving Template), Version 4.4 - DRAFT for working group discussion.” It landed alongside the CTV Ad Portfolio work, and the parts the finalised guidance depends on are the parts you can rely on now.

The short answer on versioning

Keep version="4.2" or "4.3" on production tags for now. You lose nothing by doing so: every VAST example in the final guidance declares version="4.2" while using the new NonLinear content model, which matches the deployed base. The constructs below are meant to be accepted on the versions the ecosystem already runs.

VASTlint recognises version="4.4" as a valid version rather than an unknown string, and reports VAST-4.4-version-attribute at info severity as a note about deployment timing.

Why any of this exists: the CTV Ad Portfolio

Since 2024 the IAB's Ad Format Hero initiative has been collecting CTV format submissions and consolidating them. Six formats came out the other side, all of which had been running in market for years on bespoke, per-publisher integrations:

FormatRequest objectResponse
Pauseimp.videoNonLinear VAST
Screensaverimp.videoNonLinear VAST
Overlayimp.videoNonLinear VAST
Squeezebackimp.videoNonLinear VAST
In-Sceneimp.videoNonLinear VAST
Menu / tileimp.nativeNative API, VAST only inside video.vasttag

Five of the six land in <NonLinearAds>. That is why a node which had barely moved since VAST 2.0 suddenly grew a content model. Menu ads go through the Native object and never touch it.

The stated design goal is worth quoting, because it explains the signaling design: “no one should be able to accidentally purchase a pause ad.” Buyers opt in explicitly. That is what the new AdCOM values are for.

Why the industry wanted this

US CTV ad spend is heading for roughly $38 billion in 2026, up from about $33 billion the year before, and 2026 is the year CTV upfront spend ($17.73B) passes primetime linear TV upfront spend ($16.98B) for the first time.

Almost all of that is pre-roll and mid-roll. Interruptive inventory. Ad load is the one lever streamers cannot keep pulling, because subscribers notice and subscribers leave. The CTV Ad Portfolio formats monetise moments that were previously worth nothing: the viewer hitting pause, the idle home screen, the lower third during play. New inventory with no added interruption.

The pricing supports it. Hulu pause ads reportedly clear in the $25 to $40 CPM range, alongside standard programmatic premium AVOD at $25 to $45. A pause ad clears at roughly what a mid-roll clears at, on inventory that costs the publisher nothing in viewer tolerance. What was missing was a way to buy it at scale rather than through a bespoke integration per publisher.

What changed in the content model

NonLinear can carry MediaFiles

This is the substantive change. <NonLinear> previously accepted only <StaticResource>, <IFrameResource> and <HTMLResource>. It now accepts the same <MediaFiles> container as <Linear>, holding MediaFile, Mezzanine, InteractiveCreativeFile and ClosedCaptionFiles.

Before this, a pause ad was a JPEG. Now it can be a 15-second MP4, a cinemagraph, or an interactive SIMID unit with a video fallback.

<NonLinear width="1920" height="1080">
  <Duration>00:00:15</Duration>
  <MediaFiles>
    <MediaFile delivery="progressive" type="video/mp4" width="1920" height="1080">
      <![CDATA[https://cdn.example.com/pause.mp4]]>
    </MediaFile>
  </MediaFiles>
</NonLinear>

NonLinear can carry Duration

<Duration> is what makes quartile and overlayViewDuration tracking possible for these formats. The guidance keeps it optional, with a specific carve-out: static image creative used for pause, screensaver and overlay may genuinely not have a known duration at response time, and in those cases the publisher expresses it in the bid request instead.

A video MediaFile has a known duration by definition. Ship one without a <Duration> and the ad renders while your measurement stack sees nothing. VASTlint flags that as VAST-4.4-nonlinear-video-no-duration.

NonLinearAds can carry Icons

<Icons> previously appeared under <Linear>, <Companion> and <CompanionAds>. It now also appears under <NonLinearAds>, so a pause ad can carry an AdChoices disclosure.

NonLinearCustomClick returns

NonLinearCustomClick appears in none of the published 2.0.1, 3.0 or 4.2 XSDs. The 4.4 draft reintroduces it as a URI element with an optional id, alongside <NonLinearClickThrough> and <NonLinearClickTracking>.

SIMID moves inside MediaFiles

The guidance is direct:

This is the preferred VAST 4.4 pattern for secure interactive NonLinear creative. The prior pattern of using <IFrameResource apiFramework="SIMID"> is not recommended for CTV Ad Portfolio NonLinear ads.
<NonLinear width="480" height="70">
  <MediaFiles>
    <MediaFile delivery="progressive" type="video/mp4" width="480" height="70">
      <![CDATA[https://cdn.example.com/fallback.mp4]]>
    </MediaFile>
    <InteractiveCreativeFile type="text/html" apiFramework="SIMID">
      <![CDATA[https://cdn.example.com/simid.html]]>
    </InteractiveCreativeFile>
  </MediaFiles>
</NonLinear>

The point of the split is that <MediaFile> is a renderable asset and <InteractiveCreativeFile> is a separate interactive layer. A player that cannot execute SIMID renders the MediaFile. A player with neither fires the error URI and the impression is gone. SIMID support across CTV devices is nowhere near universal, so an interactive-only NonLinear will no-fill a large share of your install base without telling you. VASTlint catches that as VAST-4.4-nonlinear-no-renderable-asset.

See SIMID explained for the wider interactive picture.

AdCOM signals round-tripped into Extensions

This is the part most implementations will get wrong, because it is a VAST change driven entirely by an OpenRTB problem.

The bid request describes the opportunity using AdCOM enumerations on plcmt, pos and playbackmethod. But the VAST response often travels further than the bid object does. An SSAI stitcher or a measurement vendor downstream of the original RTB transaction never sees the bid. So the DSP echoes the format context back into the creative itself, one signal per <Extension>, marked ext="adcom":

<Extensions>
  <Extension type="plcmt" ext="adcom"><plcmt>7</plcmt></Extension>
  <Extension type="pos" ext="adcom"><pos>14</pos></Extension>
  <Extension type="playbackmethod" ext="adcom"><playbackmethod>2</playbackmethod></Extension>
  <Extension type="attr" ext="adcom"><attr>23</attr></Extension>
</Extensions>

plcmt: which format this is

Values 1 to 4 predate the CTV Ad Portfolio.

ValueFormat
5Pause
6Screensaver
7Overlay
8Squeezeback
9In-Scene

playbackmethod: how it starts

Values 1 to 7 predate it.

ValueMeaning
8Pause, sound on
9Pause, sound off
10Screensaver, sound on
11Screensaver, sound off

Overlay, Squeezeback and In-Scene reuse the existing 1 and 2.

pos: where on screen

Extended past the old 0 to 7 ceiling to describe on-screen treatment. The guidance gives per-format value sets rather than a single table: 7 and 8 for fullscreen and partial-screen Pause and Screensaver, 5/9/10/14/15 for Overlay geometry depending on shape, and 11/12/13/16/17 for Squeezeback layouts. The observed ceiling is 17.

attr: what the creative actually does

Three new Creative Attributes, and commercially this is the most interesting part of the whole release:

ValueMeaning
21Static Visual. No perceptible motion, even when delivered as a video file.
22Limited Motion (Cinemagraph). Subtle or localized motion in an otherwise static composition.
23Full-Motion Video. Continuous, scene-level motion.

A publisher whose pause placement renders an MP4 as a still frame can now set battr: [22, 23] to block motion creative while still accepting MP4 delivery. That distinction, between what a placement technically supports and what experience it expects, was not expressible in OpenRTB before. DSPs declare the delivered experience with attr so the publisher can check the two match.

QR codes in CreativeExtension

Pause, screensaver and idle-state placements are the natural home for a QR code: the viewer is not mid-content and has a phone within reach. The draft schema adds four typed elements:

<CreativeExtension type="tl_qrcode">
  <QrCodeScanUrl><![CDATA[https://brand.example.com/qr]]></QrCodeScanUrl>
  <QrCodeImageUrl><![CDATA[https://cdn.example.com/qr.png]]></QrCodeImageUrl>
  <QrCodePosition xPosition="10%" yPosition="70%"/>
  <QrCodeSize size="15%"/>
</CreativeExtension>

The trap: <Icon> types its xPosition and yPosition as vastIntegerOrPercent_type, so xPosition="120" is perfectly valid there. <QrCodePosition> types them as vastPercent_type. The same markup is invalid. CTV screens vary too much in resolution for pixel coordinates to mean anything, which is presumably the reasoning. If you generate QR blocks by adapting existing icon markup, this will bite you, and VAST-4.4-qrcode-position-percent exists because of it.

How VASTlint treats the draft

The schema is still in working-group discussion, so there are two places where VASTlint waits rather than enforces. Neither is a problem with the draft; both are the kind of detail that gets settled during discussion.

The draft is scoped to the CTV work. Some 4.2 elements are not carried over: AltText, BlockedAdCategories, Expires, IconClickFallbackImage and IconClickFallbackImages. Nothing in the CTV Ad Portfolio guidance touches any of them, so VASTlint reads their absence as scope rather than deprecation. Model 4.4 as 4.3's content model plus the additions above, and keep using those elements.

The draft is stricter on Extension than 4.2. It makes @type required and restricts custom children to the ##other namespace, where the 4.2 XSD leaves type optional and uses processContents="skip". Enforcing the stricter form today would flag a lot of deployed vendor extensions, so VASTlint waits for the schema to settle.

Three example typos, contributed back

Small things in the sample markup, worth knowing if you are copying from Signaling-Implementation-Guidelines.md. We contributed these back so they get cleaned up:

  • The Screensaver cinemagraph example closes its <Extensions> block with a stray extra </Extension>. That example does not parse.
  • One Overlay example carries <attr>20</attr>. The same document defines only 21, 22 and 23 as motion attributes.
  • One template is still <VAST version="INSERT VAST VERSION">.

Validating CTV Ad Portfolio tags today

VASTlint ships 17 rules for this, in a dedicated CTV Ad Portfolio category. Severities follow the split at the top of this page: rules derived from the finalised signaling guidance carry normal weight, rules derived only from the draft schema stay at warning or info, and the only errors are constructs that are malformed under any version, such as a non-integer AdCOM payload or a QR position in pixels.

The new NonLinear content model is accepted on any VAST 4.x document, not just on 4.4, precisely because IAB's own examples ship it on 4.2 tags. Validating a conforming CTV Ad Portfolio tag produces no structural errors regardless of which version it declares.

Paste a tag into the VAST validator, point the URL tester at a live endpoint, or run vastlint check in CI.


Further reading