VAST tracking events
A tracking event is a URL the player fires when something happens to the ad. The set of things that count as something is fixed by the spec, and it changed in every major VAST version. An event name outside the set for the version you declared is not an error the player reports. It is a URL nobody ever calls.
Tracking failures are the most expensive silent failure in VAST. The ad plays perfectly, the report comes back empty, and the natural conclusion is that nobody watched.
Where tracking events live
<Creative>
<Linear>
<Duration>00:00:30</Duration>
<TrackingEvents>
<Tracking event="start"><![CDATA[https://track.example.com/start]]></Tracking>
<Tracking event="progress" offset="00:00:10">
<![CDATA[https://track.example.com/10s]]>
</Tracking>
</TrackingEvents>
<MediaFiles>...</MediaFiles>
</Linear>
</Creative>VAST XML fragment only. This excerpt belongs inside a complete VAST document, so standalone validation will fail until it is wrapped in a full <VAST>response.
A <TrackingEvents> container appears inside <Linear>, inside <NonLinearAds>, and inside <Companion>. Which event names are legal depends on which container you are in as well as which version you declared, and the two constraints are independent.
Impressions are not tracking events. <Impression> is its own element at the InLine level, and it fires on ad display rather than on a playback milestone. Verification events live in their own container under <AdVerifications>, covered in the OMID reference.
Events by version
These are the sets VASTlint validates against. An event name valid in one column and absent from another is not a stylistic difference: a 4.1 player reading event="fullscreen" has no handler for it.
| Event | 2.0 | 3.0 | 4.0 | 4.1+ |
|---|---|---|---|---|
creativeView | Yes | Yes | Yes | Yes |
start | Yes | Yes | Yes | Yes |
firstQuartile, midpoint, thirdQuartile, complete | Yes | Yes | Yes | Yes |
mute, unmute, pause, rewind, resume | Yes | Yes | Yes | Yes |
close, acceptInvitation | Yes | Yes | Yes | Yes |
fullscreen | Yes | Yes | Removed | Removed |
exitFullscreen | No | Yes | Removed | Removed |
expand, collapse | Yes | Yes | Removed | Removed |
skip, progress | No | Yes | Yes | Yes |
acceptInvitationLinear, closeLinear | No | Yes | acceptInvitationLinear only | closeLinear only |
playerExpand, playerCollapse | No | No | Yes | Yes |
timeSpentViewing | No | No | Yes | Removed |
adExpand, adCollapse, minimize, overlayViewDuration, otherAdInteraction | No | No | Yes | Yes |
loaded | No | No | No | Yes |
interactiveStart | No | No | No | Yes |
The four events people confuse
Version tables do not help with the question that actually costs money, which is why an impression count and a start count disagree by fifteen percent. Four events sit near the beginning of playback and mean different things.
| Signal | Fires when | Counts |
|---|---|---|
<Impression> | The ad is displayed. Not a tracking event, its own InLine element. | The billable event in most contracts. |
loaded | The player has the media file and is ready. 4.1 and later. Playback has not begun. | Delivery of the asset, not exposure. |
creativeView | The creative is first displayed. For NonLinear this is the primary signal. | First render. On linear it often duplicates the impression. |
start | Linear playback actually begins, first frame rendered. | The number to compare against quartiles. |
A gap between impression and start is normal and usually means the player fetched the ad and the user left, the media file failed to load, or an SSAI stitcher counted the stitch rather than the playback. A gap between start and firstQuartile is a different problem and usually means the media file is stalling. Reporting that only carries impressions cannot tell those apart, which is the argument for shipping the full quartile set even on formats where nobody reads the middle values.
The 4.0 rename that still breaks tags
VAST 4.0 removed fullscreen and exitFullscreen and replaced them with playerExpand and playerCollapse. The rename was deliberate: the old names described a browser state, and the new ones describe a player state, which is the only thing that makes sense on a television.
Six years later, ad servers still emit the old names on documents declaring 4.1 and 4.2, because the template was written once and the version attribute was bumped separately. VASTlint reports it with a direct pointer at the replacement rather than a generic unknown event.
The same applies to expand and collapse, which became adExpand and adCollapse in 4.0, and to timeSpentViewing, which existed only in 4.0 and did not survive into 4.1.
progress and its offset
progress is the only event that takes a parameter. Its offset attribute accepts either a timestamp in HH:MM:SS form or a percentage, and it fires when playback reaches that point.
<Tracking event="progress" offset="00:00:15"><![CDATA[...]]></Tracking>
<Tracking event="progress" offset="75%"><![CDATA[...]]></Tracking>VAST XML fragment only. This excerpt belongs inside a complete VAST document, so standalone validation will fail until it is wrapped in a full <VAST>response.
Two things go wrong with it. An offset beyond the creative duration produces an event that can never fire, which looks identical in reporting to an event that failed. And an offset expressed as a bare number of seconds is neither of the two accepted forms, which is the same class of mistake as writing 30 instead of 00:00:30 in <Duration>.
Linear and NonLinear do not share an event set
Quartiles belong to linear playback. A <NonLinear> creative that is on screen while content plays has no quartile timeline of its own in the traditional sense, which is why the nonlinear vocabulary is different:
| Container | Events you should expect |
|---|---|
<Linear> | start, the four quartiles, progress, skip, player state events, closeLinear |
<NonLinearAds> | creativeView, overlayViewDuration, acceptInvitation, adExpand, adCollapse, minimize, close, otherAdInteraction |
<Companion> | creativeView, and in practice little else |
The CTV Ad Portfolio formats made this distinction financially relevant, because five of the six are NonLinear creatives carrying video. A pause ad measured with a linear quartile set reports nothing. Measure it with creativeView and overlayViewDuration, and make sure the creative declares a <Duration> so those events have a timeline. See the CTV Ad Portfolio reference.
Wrapper tracking is additive
Tracking events in a <Wrapper> do not replace the events in the InLine ad at the end of the chain. They accumulate. A four-hop chain where every hop adds a start tracker produces four requests on one start event, and that is correct behaviour, not duplication.
The practical consequence is that a long chain multiplies both your tracking volume and your exposure to a single failing endpoint. See the wrapper chains guide for depth limits and the failure modes that come with them.
What a check catches before launch
- Whether every
eventattribute names an event that exists in the declared version, and if not, which event replaced it. - Whether the event is legal in the container it appears in, so a quartile does not end up on a companion.
- Whether
progresscarries anoffset, and whether that offset is a well-formed timestamp or percentage. - Whether every tracking URL is HTTPS, since a mixed content tracker is dropped by the browser before it leaves.
- Whether macros inside tracking URLs are spelled correctly and encoded, which is covered in the macro reference.
- Whether the quartile set is complete, since a tag with an impression and no quartiles cannot distinguish a played ad from a fetched one.
No check on the document can tell you whether the endpoint on the other end counted anything. A tracker that returns 200 and discards the request looks the same as one that records it. That question belongs to your measurement vendor, and it only becomes worth asking once the document is correct.