VAST Tag Integration: CTV Platform Setup
Integrate LtvAdx VAST 4.2 ad tags into your CTV app, FAST channel, or streaming platform. The ad decision happens at request time — on-device or via SSAI — with full quartile tracking and HouseholdID targeting.
How it works
Your CTV player or ad client calls the LtvAdx VAST endpoint at the start of each ad break. LtvAdx runs the 20-filter waterfall in under 10ms, selects the winning creative, and returns a VAST 4.2 XML document with media files, tracking events, and optional companion ads. The player fires quartile beacons (first quartile, midpoint, third quartile, complete) directly from the device.
SSAI vs client-side VAST
Use this guide for client-side VAST integrations where your player fetches the tag directly. For server-side ad insertion (SSAI) where ads are stitched into the HLS/DASH manifest before the player receives it, see the SSAI Integration guide.
Step 1 — Create a channel and ad break in the dashboard
- Sign in to the LtvAdx dashboard. Navigate to Publisher Portal → Channels & Apps.
- Create a channel. Set the platform (Roku, Fire TV, Samsung Tizen, LG webOS, Android TV, or Linear), content model (AVOD, FAST, BVOD, or LINEAR_TV), IAB category, and floor CPM.
- Under Ad Breaks, define your break slots — PRE_ROLL, MID_ROLL, or POST_ROLL — with pod size and max duration.
- Copy the Channel ID and Ad Break ID from the dashboard. You will need these in your VAST URL.
Step 2 — Construct the VAST tag URL
The VAST tag URL accepts the following parameters. Required parameters are marked with *.
| Parameter | Type | Description |
|---|---|---|
| channelId * | string | Channel ID from the dashboard (e.g. chan_abc123). |
| adBreakId * | string | Ad break slot ID (e.g. midroll_1). |
| platform * | enum | ROKU | FIRE_TV | SAMSUNG_TIZEN | LG_WEBOS | ANDROID_TV | LINEAR |
| householdId | string | LtvAdx HouseholdID from the Identity API. Enables freq cap and targeting. |
| duration | integer | Break duration in seconds (e.g. 30). Used to filter creatives by length. |
| contentGenre | string | IAB content genre (e.g. news, sports). Used for contextual targeting. |
| country | string | ISO 3166-1 alpha-2 (e.g. US, GB). Falls back to IP geo if omitted. |
| ifa | string | Advertising ID (RIDA on Roku, IFA on Android). Sent to winning DSP via schain. |
| lmt | 0|1 | Limit Ad Tracking flag. Pass 1 if the user has opted out of ad tracking. |
Example VAST URL for a Roku channel:
GET https://ads.ltvadx.com/vast
?channelId=chan_fast_news_01
&adBreakId=midroll_1
&platform=ROKU
&householdId=hh_a1b2c3
&duration=30
&contentGenre=news
&ifa=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
&lmt=0Step 3 — Handle the VAST 4.2 response
LtvAdx returns a standard VAST 4.2 XML document. Configure your player's ad client (the IMA SDK, Brightcove, JW Player, or your custom VAST parser) to parse and play the response:
<VAST version="4.2">
<Ad id="ltv_99821" sequence="1">
<InLine>
<AdSystem version="2.0">LtvAdx</AdSystem>
<AdTitle>Brand Campaign — 30s</AdTitle>
<Impression id="ltv_imp">
<![CDATA[https://ads.ltvadx.com/track/impression?req=...&sig=...]]>
</Impression>
<Creatives>
<Creative id="cre_xyz" sequence="1">
<Linear skipoffset="00:00:05">
<Duration>00:00:30</Duration>
<TrackingEvents>
<Tracking event="start">
<![CDATA[https://ads.ltvadx.com/track/start?req=...]]>
</Tracking>
<Tracking event="firstQuartile">
<![CDATA[https://ads.ltvadx.com/track/q1?req=...]]>
</Tracking>
<Tracking event="midpoint">
<![CDATA[https://ads.ltvadx.com/track/q2?req=...]]>
</Tracking>
<Tracking event="thirdQuartile">
<![CDATA[https://ads.ltvadx.com/track/q3?req=...]]>
</Tracking>
<Tracking event="complete">
<![CDATA[https://ads.ltvadx.com/track/complete?req=...]]>
</Tracking>
</TrackingEvents>
<VideoClicks>
<ClickThrough id="ltv_ct">
<![CDATA[https://brand.com/landing]]>
</ClickThrough>
</VideoClicks>
<MediaFiles>
<MediaFile id="mf1" delivery="progressive"
type="video/mp4" width="1920" height="1080"
bitrate="3500" codec="H.264">
<![CDATA[https://cdn.ltvadx.com/ads/30s_1080p.mp4]]>
</MediaFile>
<MediaFile id="mf2" delivery="progressive"
type="video/mp4" width="1280" height="720"
bitrate="1500" codec="H.264">
<![CDATA[https://cdn.ltvadx.com/ads/30s_720p.mp4]]>
</MediaFile>
</MediaFiles>
</Linear>
</Creative>
</Creatives>
</InLine>
</Ad>
</VAST>Step 4 — Platform-specific integration notes
Roku (BrightScript / SceneGraph)
Use the roVideoPlayer or SceneGraph Video node with the adUrl field. Populate householdId from your server-side identity lookup and ifa from GetRIDA().
' BrightScript — build VAST URL
Function buildVastUrl(channelId As String, hhId As String) As String
rida = CreateObject("roDeviceInfo").GetRIDA()
url = "https://ads.ltvadx.com/vast"
url += "?channelId=" + channelId
url += "&adBreakId=midroll_1"
url += "&platform=ROKU"
url += "&householdId=" + hhId
url += "&ifa=" + rida
Return url
End FunctionAndroid TV / Fire TV
Use the Google IMA SDK for Android TV. Set the ad tag URL in AdsRequest.setAdTagUrl(). Retrieve ifa from Settings.Secure.getString(contentResolver, "advertising_id").
// Kotlin — IMA SDK for Android TV / Fire TV
val ifa = Settings.Secure.getString(contentResolver, "advertising_id") ?: ""
val vastUrl = "https://ads.ltvadx.com/vast" +
"?channelId=chan_fast_news_01" +
"&adBreakId=midroll_1" +
"&platform=ANDROID_TV" +
"&householdId=$householdId" +
"&ifa=$ifa" +
"&duration=30"
val adsRequest = AdsRequest().apply { adTagUrl = vastUrl }
adsLoader.requestAds(adsRequest)Samsung Tizen / LG webOS
Use the Shaka Player or Video.js IMA plugin. Pass platform=SAMSUNG_TIZEN or platform=LG_WEBOS. Advertising IDs on smart TV platforms should follow the respective TV OS APIs.
Step 5 — VMAP for multi-break content
For long-form content with multiple ad breaks, use the VMAP endpoint instead of individual VAST calls. LtvAdx returns a VMAP 1.0.1 playlist with break offsets and embedded VAST tags per pod:
GET https://ads.ltvadx.com/vmap
?channelId=chan_fast_news_01
&platform=ROKU
&householdId=hh_a1b2c3
&contentDuration=3600
&maxBreaks=4
<!-- Response: VMAP 1.0.1 with break offsets -->
<vmap:VMAP xmlns:vmap="http://www.iab.net/videosuite/vmap" version="1.0">
<vmap:AdBreak timeOffset="00:00:00" breakType="linear" breakId="preroll">
<vmap:AdSource allowMultipleAds="true">
<vmap:VASTAdData>... (inline VAST for pre-roll) ...</vmap:VASTAdData>
</vmap:AdSource>
</vmap:AdBreak>
<vmap:AdBreak timeOffset="00:15:00" breakType="linear" breakId="midroll_1">
<vmap:AdSource allowMultipleAds="true">
<vmap:VASTAdData>... (inline VAST for mid-roll 1) ...</vmap:VASTAdData>
</vmap:AdSource>
</vmap:AdBreak>
</vmap:VMAP>Testing your integration
- Use the VAST Tag Generator tool in the dashboard (Publisher Portal → VAST Tag Generator) to copy a pre-filled URL for your channel.
- Test the URL in a browser — you should receive VAST XML. A 200 response with empty
<VAST>means no line items are eligible for this channel yet. - Create a test campaign and line item in the advertiser portal targeting your channel, then retest.
- Fire the URL from your device and confirm a
startandcompleteevent appear in Publisher Portal → Reports.
Empty VAST responses
An empty VAST response (<VAST version="4.2"></VAST>) means no eligible creative was found. Common causes: no active line items targeting this channel, floor CPM above all bids, or RTB not enabled. Check your waterfall configuration in the dashboard.
Tracking and reporting
All events — impression, start, Q1, Q2, Q3, complete, skip, click — are tracked automatically via the beacon URLs in the VAST response. Reports appear in the dashboard within 2 minutes of playback. See the VCR & Impression Tracking guide for the full event schema.