VASTlint
Back to blog
Agentic creative/10 min read

AdCP Can Declare a VAST Tag. It Cannot Check One.

The AdCP creative protocol has a first-class VAST asset: delivery type, version, tracking events, VPAID support, captions. Every one of those fields is a claim about a document nobody has parsed, and a generative creative agent produces documents that look right for the same reason it produces text that reads right. Which claims a parser settles, and why a validator is the feedback signal that closes the loop.

Author

Alex Sekowski

Published

July 29, 2026

Reading time

10 min read

AdCPAgenticAI creativeVAST validatorStandards

The manifest has a VAST asset type now

Most coverage of agentic advertising stops at the deal. An agent discovers inventory, negotiates terms, confirms a media buy, and the story ends there. It does not end there. Something has to ship a creative, and for video that something is a VAST tag, which is why the AdCP creative protocol carries a VAST asset as a first-class type alongside hosted video, images, and trackers.

The asset is deliberately thin. Only the discriminator is required. Everything else is optional metadata, and the schema is explicit about why there is no width or height on it: a VAST response can return several renditions at different dimensions, and the player picks one per device at serve time, so dimensional and codec constraints live on the format and requirements layer rather than on the asset.

Two delivery types are defined, and the distinction matters for validation. A url asset points at an endpoint that returns VAST XML. An inline asset carries the XML in a content string. In the first case nobody knows what the tag says until someone fetches it. In the second the bytes are right there in the manifest and still nobody has parsed them.

A VAST asset as it appears in a creative manifest
json
{  "assets": {    "video_main": {      "asset_type": "vast",      "delivery_type": "url",      "url": "https://adserver.example/vast?pl=[PLACEMENT]&cb=${CACHEBUSTER}",      "vast_version": "4.2",      "vpaid_enabled": false,      "duration_ms": 30000,      "tracking_events": ["impression", "start", "firstQuartile", "midpoint", "thirdQuartile", "complete"],      "captions_url": "https://cdn.example/spot.vtt"    }  }}

Every field there is a claim, not a fact

Read that asset as a set of assertions about a document nobody has opened. The version is asserted. The duration is asserted, and the schema even qualifies it with if known. The tracking event list is asserted. VPAID support is asserted. The captions URL is asserted to exist and to be a captions file.

None of those assertions are checked by accepting the manifest. A seller agent that validates the manifest against the JSON Schema has confirmed the shape of the claim and nothing about the tag. The gap is not a flaw in AdCP: a protocol for exchanging creative metadata is not a video ad parser, and it should not try to be. But it does mean the interesting failures all land outside the schema.

What only fetching and parsing the tag can settle

  • Whether the document is well-formed XML at all, or an HTML error page with a 200 status, which is the single most common thing a supposedly working ad tag endpoint returns.
  • Whether the declared vast_version matches the VAST attribute on the root element, and whether the tag uses elements from a version it does not declare.
  • Whether the tracking_events the manifest promised actually exist in the tag. A declared complete event with no Tracking node for it is a measurement gap the buyer will discover in reporting.
  • Whether Impression is present and non-empty. It is required by the specification and routinely omitted by templates that assume something downstream will inject it.
  • Whether Duration is formatted as HH:MM:SS rather than bare seconds, and whether it agrees with the duration_ms in the manifest.
  • Whether every media file and tracking URL is https. Players block mixed content outright, and this is invisible in a manifest that only carries the tag endpoint.
  • Whether a wrapper chain terminates in an InLine ad inside the depth the player allows, and whether each wrapper carries a VASTAdTagURI at all.
  • Whether the macros in the endpoint survive substitution. The schema accepts VAST-style square-bracket and dollar-brace placeholders as-is and tells buyers not to pre-encode the delimiters, because players match the literal token. A pipeline that percent-encodes them helpfully produces a tag that never resolves.

The version enum stops at 4.2

One detail is worth flagging for anyone building against the creative protocol today. The VAST asset requirements schema enumerates acceptable versions as 2.0, 3.0, 4.0, 4.1, and 4.2. VAST 4.3 has been final for years and is the version CTV signaling guidance assumes, and a 4.4 draft schema is already circulating.

The practical consequence is a mismatch that no side is wrong about. A seller declaring requirements through AdCP cannot express 4.3 today, so a buyer shipping a 4.3 tag either downgrades the declaration to 4.2 or leaves the field off entirely. Both options put a true statement about the creative outside the protocol, and both are indistinguishable from a genuine 4.2 tag when the manifest is all you have.

This is exactly the class of drift a parser resolves in one call. Read the root element, report the real version, compare it to what was declared, and stop guessing.

Trackers get decomposed, which is useful and lossy

AdCP does not stop at the tag. It also models individual trackers as their own asset types: a VAST tracker asset for a single Tracking event URL, a DAAST tracker for the audio equivalent, and a pixel tracker for renderer-fired beacons whose event enum mirrors the OpenRTB Native event registry. That decomposition is genuinely useful, because it lets a measurement vendor be attached per event rather than per creative.

It is also lossy in one direction. A decomposed tracker declares that a URL should fire on an event. It says nothing about whether the assembled VAST document places that URL in a node the player will actually read, whether the URL is duplicated across a wrapper chain so the event fires twice, or whether the event name survives the mapping between the flattened AdCP enum and the real VAST elements. Impression, Error, VideoClicks, and ViewableImpression live in dedicated VAST elements rather than under TrackingEvents, and the AdCP enum flattens them into one list by design.

Reassembly is where measurement breaks, and reassembly happens after the manifest is accepted.

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

A model that writes a VAST tag writes a plausible VAST tag

The reason this matters more in an agentic pipeline than it did in a human one is the failure mode of the thing now producing the tag. A creative agent generating VAST XML is doing pattern completion over documents it has seen. Pattern completion is very good at shape and indifferent to constraint, so what comes out looks right in the way a forged signature looks right.

The specific defects are predictable once you know that. Correct element names in the wrong nesting. Duration written as 30 because most numbers in most documents are bare numbers. A tracking event list copied from a different creative, so the manifest promises six events and the document contains four. Attributes that do not exist in any version of the specification but read like they should. Elements from 4.2 inside a document declaring 3.0, because the training data does not separate versions the way a parser does. A UniversalAdId left at the placeholder value that appears in every example tag ever published. http URLs, for the same reason.

None of those are stupid mistakes. They are exactly the mistakes a system optimising for plausibility makes, and they are invisible to every check that operates above the document: the JSON Schema validates the manifest, the manifest declares the tag is 4.2, and the tag says whatever it says.

Asking a second model to review the first one does not fix it either. A reviewing model has the same relationship to the specification as the writing model, which is that it has read a lot of tags and holds no ground truth. Two plausibility engines agreeing is not verification.

A validator is the feedback signal that closes the loop

What a parser gives an agent is the one thing the agent cannot produce for itself: a verdict that does not depend on how convincing the output looks. A VAST document either parses or it does not. The required elements are either present or absent. The declared version either matches the root element or it does not. Those are decidable questions with no interpretation in them, which makes them cheap to answer and impossible to argue with.

That turns generation into a loop instead of a gamble. The agent writes a tag, calls the validator, and gets back a list of rule ids with the line and the element each one fired on. It repairs those specific defects and calls again. Because the errors are structural rather than stylistic, the loop usually converges in one or two passes: a missing Impression node is not a matter of taste, and neither is a Duration format.

This is the same property that makes VAST validity usable as a verifiable reward when training a model, which we have written about separately. The argument here is the inference-time half of it. At training time a rule-based checker can score an output without a human in the loop; at generation time that same checker is the grounding signal that stops a plausible tag from shipping as a correct one. One verifier, two uses.

The asymmetry is what makes it worth wiring up. Generating creative is expensive and confident. Checking it is cheap and correct. Putting the cheap correct thing in front of the expensive confident thing is the whole design, and it is the same reason coding agents run tests instead of asking a model whether the code compiles.

Where the check belongs in the creative task loop

  1. build_creative or any generative step: validate what the creative agent produced before it becomes a manifest. A generated tag is a template output, and template outputs fail structurally in the same handful of ways every time.
  2. sync_creatives: this is the natural gate. The seller is already deciding whether to accept the creative, and it is the last point where a rejection costs nothing but a retry. Fetch the tag, parse it, and reject on structural failure rather than on schema shape alone.
  3. preview_creative: a preview that renders is weak evidence, because one player tolerating a tag says little about the next. A parse result is stronger evidence and cheaper to produce.
  4. get_creative_delivery: when delivery numbers look wrong, re-validate the tag as it is being served rather than the tag as it was submitted. Endpoints change behaviour without anyone editing a manifest.

Why this is the seller agent's problem

In a human workflow a broken tag gets caught by an ad ops person who has seen it before and knows what a missing Impression node does to a report. In an agentic workflow that person is not in the loop, and the agent on the other side has no memory of last quarter.

So the gate has to be somewhere in the protocol path, and sync_creatives is the obvious place: the seller is already deciding whether to accept the creative, and a rejection there costs a retry rather than a campaign. A seller that rejects on parse failure closes the loop even when the buyer's agent never opened it.

Both agentic stacks land in the same position on this. IAB Tech Lab's AAMP extends the existing rails and keeps VAST as the video envelope. AdCP defines new agent-native tasks and still ends at a VAST tag. Neither specification validates the creative payload, and neither claims to. That work belongs to whoever wants the impression to render.

Put the parse step in the agent loop

The agentic integration guide covers wiring the vastlint MCP server into AdCP creative pipelines, AAMP buyer agents, A2A orchestrators, and plain MCP clients, with working configuration for each.

Read the agentic integration guide

Sources and further reading

The vast asset type: delivery_type url or inline, vast_version, tracking_events, vpaid_enabled, accessibility URLs, provenance.

Creative manifests, asset types, formats, and the task reference for build_creative, sync_creatives, and preview_creative.

The IAB Tech Lab stack, which keeps VAST as the video delivery envelope under its agentic layers.

The layer map: what AdCP automates, and why the VAST tag is still the last mile.

What a version mismatch does in a real player, and why declared version and actual version drift apart.

Why a vpaid_enabled claim of true deserves a second look in 2026.

The training-time half of the same argument: why ad-tag validity is one of the few adtech signals a rule-based checker can score without a human or a learned judge.

What a verifier has to return to be usable in a training or repair loop: stable rule ids, positions, and dense partial credit.

Keep reading

Related stories

All posts