diff --git a/ads/concepts/breaks.mdx b/ads/concepts/breaks.mdx
index 3a2193b62de8..25e71368814d 100644
--- a/ads/concepts/breaks.mdx
+++ b/ads/concepts/breaks.mdx
@@ -25,6 +25,7 @@ The [timebase of the channel](./channels.mdx#timebase) defines how a break is sc
- **`wallclock`** — the break's `start` is a UTC ISO 8601 timestamp (for example `"2026-07-16T12:15:00.000Z"`). Players match it against the wallclock timeline carried by the stream. `start` is optional on wallclock channels: omitting it creates a [cued break](#cued-breaks).
- **`pts`** — the break's `start` is a non-negative number: a presentation timestamp on the encoder's timeline. `start` is required on PTS channels.
+- **`mediatime`** — the break's `start` is a non-negative number of seconds from the start of the asset (for example `600`). `start` is required on `mediatime` channels, which are always [VOD](../workflows/vod/index.mdx) channels.
`duration` is always required and is expressed in seconds. It is the **maximum** duration of the break.
@@ -396,6 +397,8 @@ A `static` asset is a media resource the player retrieves directly — a video o
}
```
+See [Static assets](../workflows/live/static-assets.mdx) for the supported creative formats and layouts in the OptiView Ads SDK.
+
#### VAST assets
A `vast` asset points to an ad server that responds with a VAST XML document describing how the ad should be played. The `uri` is the VAST tag URL, and the `mimeType` should be `application/xml` or `text/xml`:
@@ -409,6 +412,8 @@ A `vast` asset points to an ad server that responds with a VAST XML document des
}
```
+A `vast` asset can also carry `assetParameters`, which the player appends to the VAST tag URL as query parameters. See [VAST](../workflows/live/vast.mdx) for how the OptiView Ads SDK plays VAST tags, and the layouts they support.
+
#### Vendor assets
A `vendor` asset is delivered through an [ad vendor integration](../integrations/index.mdx), such as Google Ad Manager. The vendor decides the ad content; you identify the vendor and pass the vendor-specific parameters:
diff --git a/ads/concepts/channels.mdx b/ads/concepts/channels.mdx
index bef0e3796a34..7ec215a7cc32 100644
--- a/ads/concepts/channels.mdx
+++ b/ads/concepts/channels.mdx
@@ -5,7 +5,7 @@ sidebar_label: Channels
# Channels
-A channel represents one live stream in OptiView Ads. It is the place where you decide how ads behave for that stream: how break start times are interpreted, which breaks are announced to players and when, and whether ad markers in the stream are detected automatically.
+A channel represents one live stream or one video-on-demand (VOD) asset in OptiView Ads. It is the place where you decide how ads behave for that content: how break start times are interpreted, which breaks are announced to players and when, and whether ad markers in the stream are detected automatically.
Everything else in OptiView Ads hangs off a channel. Origins, marker rules, breaks, events, templates, and integrations are all created for — or looked up through — a channel.
@@ -30,14 +30,22 @@ Every channel has a customer-facing `id`. We recommend using a UUID as the chann
These channel settings determine how break start times are interpreted and how breaks are delivered to players through the [Break Manifest](./break-manifest.mdx).
+### Stream type
+
+The `streamType` determines the use case of the channel:
+
+- **`live`** (default) — a live stream. Players poll the Break Manifest continuously and receive breaks as they become relevant. See [Live](../workflows/live/index.mdx).
+- **`vod`** — an on-demand asset. The Break Manifest lists all breaks upfront and players fetch it once. VOD channels have no events. See [VOD](../workflows/vod/index.mdx).
+
### Timebase
The `timebase` determines which timeline break start times are expressed on:
-- **`wallclock`** — breaks are scheduled with a UTC timestamp that is matched against the wallclock timeline carried by the stream itself. Use this when the stream carries wallclock timing: in HLS this comes from `EXT-X-PROGRAM-DATE-TIME` tags, in DASH from the MPD's `availabilityStartTime` combined with the segment timeline (optionally synchronized through a `UTCTiming` element).
-- **`pts`** — breaks are scheduled with a presentation timestamp (PTS) on the encoder's timeline. The player retrieves the PTS value from the media segments to know where it is on that timeline. Use this when your workflow schedules breaks against encoder PTS values rather than wallclock time.
+- **`wallclock`** — breaks are scheduled with a UTC timestamp that is matched against the wallclock timeline carried by the stream itself. Use this when the stream carries wallclock timing: in HLS this comes from `EXT-X-PROGRAM-DATE-TIME` tags, in DASH from the MPD's `availabilityStartTime` combined with the segment timeline (optionally synchronized through a `UTCTiming` element). Available for live and VOD channels.
+- **`pts`** — breaks are scheduled with a presentation timestamp (PTS) on the encoder's timeline. The player retrieves the PTS value from the media segments to know where it is on that timeline. Use this when your workflow schedules breaks against encoder PTS values rather than wallclock time. Live channels only.
+- **`mediatime`** — breaks are scheduled with a position in the asset, in seconds from its start (for example `600` for 10 minutes in). VOD channels only.
-Choose the timebase when creating the channel; all breaks on the channel use the same timebase.
+Choose the stream type and the timebase when creating the channel; both are fixed afterwards, and all breaks on the channel use the same timebase.
### DVR window
diff --git a/ads/getting-started/index.mdx b/ads/getting-started/index.mdx
index 422eef8d56f2..9f98da987adb 100644
--- a/ads/getting-started/index.mdx
+++ b/ads/getting-started/index.mdx
@@ -10,7 +10,7 @@ import RebrandingNotice from '../callouts/_rebranding_notice.md';
-OptiView Ads monetizes your live stream with Server-Guided Ad Insertion (SGAI): you schedule ad breaks on a channel, and the player renders them on top of your content — without touching your media stream. This guide takes you from an empty organization to seeing your first ad break play out on your device.
+OptiView Ads monetizes your live stream with Server-Guided Ad Insertion (SGAI): you schedule ad breaks on a channel, and the player renders them on top of your content — without touching your media stream. This guide takes you from an empty organization to seeing your first ad break play out on your device. It uses a live channel; to monetize on-demand assets, the same steps apply to a [VOD channel](/ads/workflows/vod/) with breaks at positions in the asset.
The examples use the US region (`https://us.ads.optiview.dolby.com` and `https://us.markers.optiview.dolby.com`). For the EU region, replace `us.` with `eu.`.
@@ -84,9 +84,11 @@ await sdk.startSession({
Follow the platform guide for [Web](/ads/player-integration/optiview-ads-sdk/web), [Android](/ads/player-integration/optiview-ads-sdk/android), [iOS](/ads/player-integration/optiview-ads-sdk/ios), or [React Native](/ads/player-integration/optiview-ads-sdk/react-native). If you use the OptiView Player, the legacy [OptiView Player integration](/ads/player-integration/optiview-player/) is also available.
+This integration plays every [ad workflow](/ads/workflows/): static assets from your CDN, VAST tags from your ad server, and Google Ad Manager — on live and VOD channels. Static assets need nothing more; for VAST or Google Ad Manager on Web, the workflow page lists the Google IMA script to add to your page.
+
## 4. Schedule a break
-A [break](/ads/concepts/breaks) describes an ad opportunity: when it starts, how long it lasts, and which ad experience plays. The simplest break plays a single full-screen ad from a VAST tag.
+A [break](/ads/concepts/breaks) describes an ad opportunity: when it starts, how long it lasts, and which ad experience plays. The simplest break plays a single full-screen video ad from your CDN, as a [static asset](/ads/workflows/live/static-assets).
Dashboard: open the channel → **Breaks → Schedule now**. Or via the API, scheduling a 30-second break a few minutes from now:
@@ -102,16 +104,18 @@ curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels//bre
"format": "single",
"assets": [
{
- "type": "vast",
+ "type": "static",
"mediaType": "video",
- "uri": "https://cdn.example.com/vast.xml"
+ "uri": "https://cdn.example.com/ads/spot.m3u8"
}
]
}
}'
```
-Replace `` with an ISO 8601 UTC timestamp a few minutes in the future, for example `2026-01-01T12:00:00.000Z`.
+Replace `` with an ISO 8601 UTC timestamp a few minutes in the future, for example `2026-01-01T12:00:00.000Z`, and the `uri` with the URL of an ad video on your CDN.
+
+To let an ad server choose the ad instead, use a [VAST](/ads/workflows/live/vast) asset or [Google Ad Manager](/ads/workflows/live/google-ad-manager).
Once you schedule breaks regularly, [templates](/ads/concepts/templates) let you preconfigure the break payload so scheduling becomes a one-liner.
@@ -121,7 +125,7 @@ Start playback of your stream on your device. Shortly before the scheduled start
You can follow along on both sides:
-- **In the dashboard or API:** the break's [lifecycle status](/ads/concepts/breaks#break-lifecycle) moves from `PREPARING` to `READY`, and to `SIGNALED` once it is announced to players.
+- **In the dashboard or API:** the break's [lifecycle status](/ads/concepts/breaks#break-lifecycle) is `READY`, and moves to `SIGNALED` once it is announced to players.
- **In the player:** the ad plays at the scheduled time and your content resumes afterwards.
If the break does not play, verify that the Break Manifest URL from step 2 returns the break, and that your stream carries the time metadata required by the channel's timebase.
@@ -131,6 +135,7 @@ If the break does not play, verify that the Break Manifest URL from step 2 retur
| Resource | Description |
| ------------------------------------------------------------- | ----------------------------------------------------------------- |
| [Player integration](/ads/player-integration/) | The full integration guides for the SDK and the OptiView Player. |
+| [Ad workflows](/ads/workflows/) | Static assets, VAST, and Google Ad Manager with the SDK. |
| [Breaks](/ads/concepts/breaks) | Everything a break can do: layouts, variants, controls, punching. |
| [Origins and Break Detection](/ads/concepts/marker-detection) | Turn the ad markers in your stream into breaks automatically. |
| [Google Ad Manager](/ads/integrations/google/) | Let Google Ad Manager decision the ads that fill your breaks. |
diff --git a/ads/how-to-guides/ad-tag-parameters.mdx b/ads/how-to-guides/ad-tag-parameters.mdx
index 3da2b4206c5c..c93028a8a8ce 100644
--- a/ads/how-to-guides/ad-tag-parameters.mdx
+++ b/ads/how-to-guides/ad-tag-parameters.mdx
@@ -4,13 +4,30 @@ sidebar_position: 3
# Ad targeting parameters
-When an ad server such as Google Ad Manager decisions a break, you can pass key-value parameters with the request to personalize the ads — for example the content genre, the placement type, or custom targeting values. In OptiView Ads, these parameters are called **`assetParameters`**. For Google, they map directly to the [ad tag parameters](https://support.google.com/admanager/answer/10678356) (`adTagParameters`) on the ad request.
+When an ad server decides which ad fills a break, you can pass key-value parameters with the ad request to personalize the ads — for example the content genre, the placement type, or custom targeting values. In OptiView Ads, these parameters are called **`assetParameters`**. How they reach the ad server depends on the [ad workflow](../workflows/index.mdx):
-You can set `assetParameters` in two places: on the break in the **backend**, and in the **OptiView Ads SDK** on the client. Client-side values overwrite the backend values when the same key is set in both.
+- **[VAST](../workflows/live/vast.mdx):** the SDK appends them to the VAST tag URL as URL-encoded query parameters.
+- **[Google Ad Manager](../workflows/live/google-ad-manager.mdx):** they are sent to Google as ad tag parameters (`adTagParameters`). See Google's [list of all recognized ad tag parameters](https://support.google.com/admanager/answer/10678356#expand-all).
+- **[Static assets](../workflows/live/static-assets.mdx):** the SDK appends them as query parameters to the URL of the creative it requests.
+
+You can set `assetParameters` in two places: on the break in the **backend**, and in the **OptiView Ads SDK** on the client.
## Asset parameters in the backend
-When you [schedule a break](../concepts/breaks.mdx#scheduling) with a vendor asset, you can add an `assetParameters` map to the asset:
+When you [schedule a break](../concepts/breaks.mdx#scheduling) with a VAST or vendor asset, you can add an `assetParameters` map to the asset. For a VAST asset:
+
+```json
+{
+ "type": "vast",
+ "mediaType": "video",
+ "uri": "https://adserver.example.com/vast?slot=midroll",
+ "assetParameters": {
+ "content_genre": "sports"
+ }
+}
+```
+
+For a Google Pod Serving vendor asset:
```json
{
@@ -26,22 +43,24 @@ When you [schedule a break](../concepts/breaks.mdx#scheduling) with a vendor ass
}
```
-OptiView Ads forwards these parameters to the ad server when the break is announced, and also delivers them to the player through the [Break Manifest](../concepts/break-manifest.mdx) so they are included on the ad request. See [Google Pod Serving](../integrations/google/pod-serving.mdx#required-google-parameters) for the parameters Google requires.
+OptiView Ads delivers these parameters to the player through the [Break Manifest](../concepts/break-manifest.mdx), so they are included on the ad request. For Google, they are also forwarded to Google when the break is announced; see [Google Pod Serving](../integrations/google/pod-serving.mdx#required-google-parameters) for the parameters Google requires.
-## Overwriting from the OptiView Ads SDK
+## Asset parameters in the OptiView Ads SDK
-The OptiView Ads SDK can supply its own `assetParameters` and overwrite the backend values. There are two client-side layers:
+The OptiView Ads SDK can add its own `assetParameters`. There are two client-side layers:
- **Session parameters** — passed to `startSession()`, they apply to every break in the session.
- **Live updates** — passed to `updateAssetParameters()` during an active session, for values that change while the viewer is watching.
-The three layers are merged **per key**, from lowest to highest precedence:
+## How the layers are merged
+
+The layers are merged **per key**, from lowest to highest precedence:
-1. The break's `assetParameters` from the backend.
-2. The session's `assetParameters` from `startSession()`.
-3. The latest `updateAssetParameters()` call.
+1. The session's `assetParameters` from `startSession()`.
+2. The latest `updateAssetParameters()` call.
+3. The break's `assetParameters` from the backend.
-Overwriting one key never discards the others, and live updates apply to **future** breaks only: a break whose ad request has already been sent cannot be retargeted.
+A value set on the break wins over the same key set in the SDK, so set each key in one place only. Overwriting one key never discards the others, and live updates apply to **future** breaks only: a break whose ad request has already been sent cannot be retargeted.
## Backend or client?
@@ -50,6 +69,12 @@ As a rule of thumb:
- **Set through the backend** the values that describe the break or the content and are the same for every viewer — such as `description_url`, the placement declaration, and campaign or content targeting that is known when the break is scheduled.
- **Set through the SDK** the values that depend on the viewer, the device, or the moment of playback — such as device identifiers, consent state, autoplay/mute state (`vpa`, `vpmute`), or audience context that changes during the session.
+## Macros
+
+A parameter value can contain macros: `$NAME$` tokens that the SDK replaces with a value from the device when it builds the ad request. For example, `"size": "$OPTIVIEW_PLAYER_WIDTH$x$OPTIVIEW_PLAYER_HEIGHT$"` is sent as `size=1920x1080`. The SDK has built-in macros for the player size, the ad duration, and the user agent, and your app can register its own.
+
+See [Asset parameter macros](./asset-parameter-macros.mdx) for how macros work, the built-in macros, registering and updating your own, and leaving out a parameter with an empty macro.
+
## Platform examples
The examples below start a session with session-level `assetParameters` and update them mid-session. See the [OptiView Ads SDK](../player-integration/optiview-ads-sdk/index.mdx) documentation for the full integration.
@@ -120,6 +145,9 @@ ads.updateAssetParameters({ cust_params: 'sport=basketball' });
| Resource | Description |
| -------------------------------------------------------------------- | ----------------------------------------------- |
+| [Asset parameter macros](./asset-parameter-macros.mdx) | Device values in parameter values. |
+| [VAST](../workflows/live/vast.mdx) | Playing VAST tags with the SDK. |
+| [Google Ad Manager](../workflows/live/google-ad-manager.mdx) | Playing Google pods with the SDK. |
| [Google Pod Serving](../integrations/google/pod-serving.mdx) | Required Google parameters and pod decisioning. |
-| [Breaks](../concepts/breaks.mdx) | Scheduling breaks with vendor assets. |
+| [Breaks](../concepts/breaks.mdx) | Scheduling breaks with VAST and vendor assets. |
| [OptiView Ads SDK](../player-integration/optiview-ads-sdk/index.mdx) | Integrating the SDK with your player. |
diff --git a/ads/how-to-guides/asset-parameter-macros.mdx b/ads/how-to-guides/asset-parameter-macros.mdx
new file mode 100644
index 000000000000..a6db413376e0
--- /dev/null
+++ b/ads/how-to-guides/asset-parameter-macros.mdx
@@ -0,0 +1,271 @@
+---
+sidebar_position: 3.5
+sidebar_label: Asset parameter macros
+---
+
+# Asset parameter macros
+
+A break is defined once in the backend, but some targeting values are only known on the viewer's device: the size of the player, the length of the ad slot, the viewer's team, or a consent string. **Macros** bridge that gap. A macro is a placeholder token inside an [asset parameter](./ad-tag-parameters.mdx) value. The OptiView Ads SDK replaces it with the actual value on the device, at the moment it builds the ad request.
+
+## How macros work
+
+A macro is a token of the form `$NAME$`: a `$`, a name made of letters, digits, and underscores, and a closing `$`. Put one or more tokens anywhere in a parameter value, on the break, in the session configuration, or in an `updateAssetParameters()` call:
+
+```json
+{
+ "type": "vast",
+ "mediaType": "video",
+ "uri": "https://adserver.example.com/vast?slot=midroll",
+ "assetParameters": {
+ "size": "$OPTIVIEW_PLAYER_WIDTH$x$OPTIVIEW_PLAYER_HEIGHT$",
+ "team": "$TEAM$"
+ }
+}
+```
+
+When the SDK requests this VAST tag on a 1920×1080 player, for a viewer whose app registered `$TEAM$` as `ajax`, the request becomes:
+
+```text
+https://adserver.example.com/vast?slot=midroll&size=1920x1080&team=ajax
+```
+
+The rules:
+
+- **Resolved per ad request.** The SDK fills in the macros every time it builds an ad request, so a value that changes during the session, such as the player size, is always current.
+- **Whole tokens only.** Matching is case-sensitive and uses the full token: `$APP$` never matches inside `$APP_NAME$`.
+- **Several tokens per value.** A value can combine text and any number of tokens, as `size` does above.
+- **A lone `$` is left alone.** A `$` without a closing `$`, and forms such as `$$` or `${NAME}`, are not macros and are sent unchanged.
+
+## Where macros apply
+
+The SDK resolves macros in every asset parameter it sends, for every ad source:
+
+| Ad source | Where the resolved parameters go |
+| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Static assets | Appended as query parameters to the URL the SDK requests: the asset's `uri`, a static companion, or a pause ad. For HLS and DASH, only the master playlist or MPD request carries them. |
+| VAST | Appended as query parameters to the VAST tag URL. |
+| Google Ad Manager | Sent to Google as ad tag parameters (`adTagParameters`). |
+
+:::caution[Signed static asset URLs]
+Do not use asset parameters on a static asset with a signed URL or a URL that carries an access token. The added query parameters can invalidate the signature. Parameters set in the SDK are added to every static asset request in the session.
+:::
+
+## Built-in macros
+
+The SDK provides these macros on every platform, unless noted:
+
+| Macro | Value |
+| ----------------------------------- | ------------------------------------------------------------------------------------- |
+| `$OPTIVIEW_PLAYER_WIDTH$` | Width of the ad container, in logical pixels. |
+| `$OPTIVIEW_PLAYER_HEIGHT$` | Height of the ad container, in logical pixels. |
+| `$OPTIVIEW_BREAK_DURATION_SECONDS$` | Duration of the ad, in seconds: the asset's `duration`, otherwise the break's. |
+| `$OPTIVIEW_BREAK_DURATION_MS$` | The same duration, in whole milliseconds. |
+| `$OPTIVIEW_USER_AGENT$` | The user agent of the device. Not available on iOS and tvOS: register your own value. |
+
+The names are also exported by the SDK, so you do not have to type them: `OPTIVIEW_ASSET_PARAMETER_MACROS` on Web and React Native, `OptiViewAssetParameterMacros` on Android, iOS, and tvOS.
+
+## Register your own macros
+
+Register your own macros with `assetParameterMacros` in the session configuration. Each macro is keyed by its full token, including both `$` signs, and its value is either fixed or a callback. The SDK calls a callback each time it builds an ad request, so it can return the current value.
+
+To override a built-in macro, register the same name: your value wins.
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+
+
+
+```typescript
+await sdk.startSession({
+ manifestUrl: 'https://us.markers.optiview.dolby.com/manifest/v1/ORG-ID/channels/CHANNEL-ID',
+ assetParameterMacros: {
+ $SEGMENT$: 'sports',
+ $TEAM$: () => currentTeam(),
+ },
+});
+```
+
+
+
+
+```kotlin
+sdk.startSession(
+ SessionConfig(
+ manifestUrl = "https://us.markers.optiview.dolby.com/manifest/v1/ORG-ID/channels/CHANNEL-ID",
+ assetParameterMacros = mapOf(
+ "\$SEGMENT\$" to { "sports" },
+ "\$TEAM\$" to { currentTeam() },
+ ),
+ )
+)
+```
+
+A fixed value is a callback that always returns the same string.
+
+
+
+
+```swift
+try await sdk.startSession(
+ SessionConfig(
+ manifestUrl: "https://us.markers.optiview.dolby.com/manifest/v1/ORG-ID/channels/CHANNEL-ID",
+ assetParameterMacros: [
+ "$SEGMENT$": { "sports" },
+ "$TEAM$": { currentTeam() },
+ "$OPTIVIEW_USER_AGENT$": { userAgent },
+ ]
+ )
+)
+```
+
+A fixed value is a closure that always returns the same string. iOS and tvOS have no built-in user agent, so register `$OPTIVIEW_USER_AGENT$` yourself if your ad server needs it.
+
+
+
+
+```typescript
+await ads.startSession({
+ manifestUrl: 'https://us.markers.optiview.dolby.com/manifest/v1/ORG-ID/channels/CHANNEL-ID',
+ assetParameterMacros: {
+ $SEGMENT$: 'sports',
+ $TEAM$: () => currentTeam(),
+ },
+});
+```
+
+
+
+
+Registered macros are cleared when the session ends.
+
+### Update macros during a session
+
+Call `updateAssetParameterMacros()` to change macros while the session runs, for example when the viewer switches teams. The call merges the given names into the session's macros; names you leave out keep their current value. The new values apply to the next ad request: an ad that has already been requested is not requested again.
+
+
+
+
+```typescript
+sdk.updateAssetParameterMacros({ $TEAM$: 'psv' });
+```
+
+
+
+
+```kotlin
+sdk.updateAssetParameterMacros(mapOf("\$TEAM\$" to { "psv" }))
+```
+
+
+
+
+```swift
+sdk.updateAssetParameterMacros(["$TEAM$": { "psv" }])
+```
+
+
+
+
+```typescript
+await ads.updateAssetParameterMacros({ $TEAM$: 'psv' });
+```
+
+
+
+
+To remove a macro you registered, pass `undefined` for it on Web and React Native, or `null` on Android and `nil` on iOS. A built-in macro with the same name then applies again.
+
+## Leave out a parameter with an empty macro
+
+Sometimes a parameter must not be sent at all, for example a consent string before the viewer has given consent. Mark the macro **empty**, and the SDK leaves out every parameter that uses it:
+
+
+
+
+```typescript
+await sdk.startSession({
+ manifestUrl: 'https://us.markers.optiview.dolby.com/manifest/v1/ORG-ID/channels/CHANNEL-ID',
+ assetParameterMacros: {
+ $CONSENT$: () => (hasConsent() ? consentString() : null),
+ },
+});
+```
+
+On Web and React Native, `null` marks a macro empty.
+
+
+
+
+```kotlin
+sdk.startSession(
+ SessionConfig(
+ manifestUrl = "https://us.markers.optiview.dolby.com/manifest/v1/ORG-ID/channels/CHANNEL-ID",
+ assetParameterMacroValues = mapOf(
+ "\$CONSENT\$" to {
+ if (hasConsent()) AssetParameterMacroValue.Text(consentString())
+ else AssetParameterMacroValue.Empty
+ },
+ ),
+ )
+)
+```
+
+On Android, register the macro in `assetParameterMacroValues` and return `AssetParameterMacroValue.Empty`. Use `updateAssetParameterMacroValues()` to change such a macro during the session.
+
+
+
+
+```swift
+try await sdk.startSession(
+ SessionConfig(
+ manifestUrl: "https://us.markers.optiview.dolby.com/manifest/v1/ORG-ID/channels/CHANNEL-ID",
+ assetParameterMacroValues: [
+ "$CONSENT$": { hasConsent() ? .text(consentString()) : .empty },
+ ]
+ )
+)
+```
+
+On iOS and tvOS, register the macro in `assetParameterMacroValues` and return `.empty`. Use `updateAssetParameterMacros(values:)` to change such a macro during the session.
+
+
+
+
+```typescript
+await ads.startSession({
+ manifestUrl: 'https://us.markers.optiview.dolby.com/manifest/v1/ORG-ID/channels/CHANNEL-ID',
+ assetParameterMacros: {
+ $CONSENT$: () => (hasConsent() ? consentString() : null),
+ },
+});
+```
+
+
+
+
+What is left out depends on the value:
+
+| Parameter value | What the SDK leaves out |
+| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
+| A plain value, such as `"gdpr_consent": "$CONSENT$"` | The whole parameter. |
+| Google Ad Manager `cust_params`, such as `"genre=sports&consent=$CONSENT$"` | Only the `&`-separated pair with the empty macro: `cust_params=genre=sports` is still sent. If no pair is left, the whole parameter. |
+| A static asset value with `&`-separated pairs | Only the pairs with the empty macro, as for `cust_params`. |
+
+For VAST, every value counts as a plain value, also one with `&`-separated pairs: the whole parameter is left out.
+
+## Tokens the SDK cannot fill in
+
+A token without a value is never removed: the SDK sends the parameter with the token as it is.
+
+- **Your ad server's own macros.** A token that is neither built in nor registered, such as `$PUBLISHER_ID$`, is sent unchanged. Your ad server can then expand it on its side.
+- **A built-in macro without a value**, such as `$OPTIVIEW_USER_AGENT$` on iOS, an unknown `$OPTIVIEW_*` name, or a registered macro whose callback returns no value, is also sent unchanged. The SDK reports it with the `DA-ASSET-MACRO-UNKNOWN` diagnostic, so you can spot a missing value during development.
+
+## Related resources
+
+| Resource | Description |
+| -------------------------------------------------------------------- | -------------------------------------------------------------- |
+| [Ad targeting parameters](./ad-tag-parameters.mdx) | Where asset parameters are set, and how the layers are merged. |
+| [Static assets on live](../workflows/live/static-assets.mdx) | Playing creatives from your CDN. |
+| [Your ad server (VAST) on live](../workflows/live/vast.mdx) | Playing VAST tags with the SDK. |
+| [Google Ad Manager on live](../workflows/live/google-ad-manager.mdx) | Playing Google pods with the SDK. |
diff --git a/ads/how-to-guides/workflow-integration.md b/ads/how-to-guides/workflow-integration.md
index 2a4ce2ec6717..91d36e2d56fe 100644
--- a/ads/how-to-guides/workflow-integration.md
+++ b/ads/how-to-guides/workflow-integration.md
@@ -41,7 +41,7 @@ Ready-made adapters are available for the OptiView Player (THEOplayer), HLS.js,
2. OptiView Ads reads the stream, detects the markers, and publishes the breaks in the channel's Break Manifest.
3. The OptiView Ads SDK polls the Break Manifest and renders the breaks in your player.
-That is the whole integration for server-guided ad insertion (SGAI). For platforms where a client-side SDK is not an option — such as Roku or older connected TV devices — OptiView Ads also supports server-side ad insertion (SSAI) through [Google DAI](../integrations/google/dai.mdx).
+That is the whole integration for server-guided ad insertion (SGAI). For platforms where a client-side SDK is not an option — such as Roku or older connected TV devices — OptiView Ads also supports server-side ad insertion (SSAI) through [Google DAI](../integrations/google/dai.mdx). See [Ad workflows](../workflows/index.mdx) for how SGAI and SSAI combine on one channel, and how live and VOD differ.
For an end-to-end walkthrough — from creating a channel to seeing a break play out — follow the [Getting started](../getting-started/index.mdx) guide.
diff --git a/ads/index.mdx b/ads/index.mdx
index 4172b8b874be..bce69653c365 100644
--- a/ads/index.mdx
+++ b/ads/index.mdx
@@ -9,7 +9,7 @@ import SidebarDocCardList from '@site/src/components/SidebarDocCardList';
import ThemedImage from '@theme/ThemedImage';
import useBaseUrl from '@docusaurus/useBaseUrl';
-OptiView Ads is an ad insertion service for LIVE content (VOD support coming soon), utilizing Server-Guided Ad Insertion (SGAI) as well as Server-Side Ad Insertion (SSAI). On these pages, you'll learn how to get started with OptiView Ads, model your live streams, schedule ad breaks, and configure the OptiView Ads SDK — the recommended integration approach.
+OptiView Ads is a hybrid ad insertion platform for **live** streams and **video-on-demand (VOD)** assets: the same channel and the same breaks can be delivered with Server-Guided Ad Insertion (SGAI) or Server-Side Ad Insertion (SSAI). Ads come from creatives on your own CDN, from VAST tags on your ad server, or from Google Ad Manager. On these pages, you'll learn how to get started with OptiView Ads, choose the [ad workflow](./workflows/index.mdx) that fits your content, schedule ad breaks, and configure the OptiView Ads SDK.
By logically redistributing responsibilities in the advertisement workflow, OptiView Ads:
@@ -19,7 +19,12 @@ By logically redistributing responsibilities in the advertisement workflow, Opti
- Maximizes workflow efficiency by reducing prefetching and over-allocation of inventory.
- Enables more relevant ads through a high degree of personalization.
-OptiView Ads models each live stream as a **channel**. A channel monitors one or more **origins**, detects or schedules **breaks**, and can use reusable **templates** for consistent ad experiences. It generates a **Break Manifest** for the OptiView Ads SDK to poll and render.
+OptiView Ads models each live stream or VOD asset as a **channel**. A channel schedules **breaks**, or detects them from the ad markers in the stream or asset on its **origins**, and can use reusable **templates** for consistent ad experiences. It generates a **Break Manifest** for the OptiView Ads SDK to read and render.
+
+| Use case | What it is for | Ad sources |
+| ---------------------------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [Live](./workflows/live/index.mdx) | Linear channels and live events: breaks on the live timeline, scheduled, punched, or detected. | [Static assets](./workflows/live/static-assets.mdx), [Your ad server (VAST)](./workflows/live/vast.mdx), [Google Ad Manager](./workflows/live/google-ad-manager.mdx) |
+| [VOD](./workflows/vod/index.mdx) | On-demand assets: pre-roll, mid-rolls at positions in the asset, post-roll, and pause ads. | [Static assets](./workflows/vod/static-assets.mdx), [Your ad server (VAST)](./workflows/vod/vast.mdx), [Google Ad Manager](./workflows/vod/google-ad-manager.mdx) |
-Besides mid-roll breaks on the live timeline, breaks can also be [triggered by player events](./concepts/breaks.mdx#event-based-triggers): **pre-roll** (when playback starts), **pause** (while the viewer is paused), and **post-roll** (when playback ends) — each with an optional delay.
+Besides mid-roll breaks on the live timeline or at positions in a VOD asset, breaks can also be [triggered by player events](./concepts/breaks.mdx#event-based-triggers): **pre-roll** (when playback starts), **pause** (while the viewer is paused), and **post-roll** (when playback ends) — each with an optional delay.
diff --git a/ads/integrations/google/pod-serving.mdx b/ads/integrations/google/pod-serving.mdx
index 3d0d39accd80..9ad87bb1aada 100644
--- a/ads/integrations/google/pod-serving.mdx
+++ b/ads/integrations/google/pod-serving.mdx
@@ -77,7 +77,7 @@ Google performs the decisioning, so no asset URI is needed: once Google has deci
The optional `assetParameters` map contains the [Google ad tag parameters](https://support.google.com/admanager/answer/10678356) (`adTagParameters`) for the break. OptiView Ads forwards them to Google when the break is announced, and delivers them to the player through the Break Manifest so they are included on the pod request.
-Players can extend these parameters: the OptiView player SDKs accept `adTagParameters` on the ad description (`TheoAdDescription`). When the same parameter is set both on the break and on the player, the player-side value wins. See [Ad tag parameters](../../how-to-guides/ad-tag-parameters.mdx) for the player-side API on each platform.
+Players can extend these parameters. The OptiView Ads SDK accepts `assetParameters` in its session configuration; when the same parameter is set both on the break and in the SDK, the break's value wins. See [Ad targeting parameters](../../how-to-guides/ad-tag-parameters.mdx) for the SDK API on each platform.
As a rule of thumb:
@@ -147,12 +147,12 @@ Additionally, creating a pod break that starts too close to the live point is re
## Related resources
-| Resource | Relationship |
-| -------------------------------------------------------------- | ------------------------------------------------------------------------ |
-| [Google Ad Manager](./index.mdx) | The prerequisites shared by both Google delivery modes. |
-| [Google DAI](./dai.mdx) | Server-side (SSAI) delivery for platforms without server-guided support. |
-| [Channels](../../concepts/channels.mdx) | Hold the custom asset key used for Google Pod Serving. |
-| [Breaks](../../concepts/breaks.mdx) | Carry the vendor asset that Google uses to make ad decisions. |
-| [Break Manifest](../../concepts/break-manifest.mdx) | The manifest that announces the channel's breaks to players. |
-| [Ad tag parameters](../../how-to-guides/ad-tag-parameters.mdx) | Passing ad tag parameters from the player. |
-| [Player integration](../../player-integration/index.mdx) | Set up a player with pod serving. |
+| Resource | Relationship |
+| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
+| [Google Ad Manager](./index.mdx) | The prerequisites shared by both Google delivery modes. |
+| [Google DAI](./dai.mdx) | Server-side (SSAI) delivery for platforms without server-guided support. |
+| [Channels](../../concepts/channels.mdx) | Hold the custom asset key used for Google Pod Serving. |
+| [Breaks](../../concepts/breaks.mdx) | Carry the vendor asset that Google uses to make ad decisions. |
+| [Break Manifest](../../concepts/break-manifest.mdx) | The manifest that announces the channel's breaks to players. |
+| [Ad tag parameters](../../how-to-guides/ad-tag-parameters.mdx) | Passing ad tag parameters from the player. |
+| [Google Pod Serving with the SDK](../../workflows/live/google-ad-manager.mdx) | Set up the OptiView Ads SDK in your app for pod serving. |
diff --git a/ads/integrations/index.mdx b/ads/integrations/index.mdx
index 13f2eb75c1a6..137dd4228d14 100644
--- a/ads/integrations/index.mdx
+++ b/ads/integrations/index.mdx
@@ -7,6 +7,8 @@ sidebar_label: Integrations
An integration connects OptiView Ads to an ad decisioning or "pod" serving vendor. Vendors are supported by providing [assets of type Vendor](../concepts/breaks.mdx#vendor-assets) in a break variant: the vendor makes the ad decisions, and OptiView Ads delivers the result to players. [Google Ad Manager 360](./google/index.mdx) is the first supported integration.
+You do not need an integration to play [static assets](../workflows/live/static-assets.mdx) from your CDN or [VAST](../workflows/live/vast.mdx) tags from your own ad server: those breaks work on any channel.
+
## Supported vendors
| Vendor | Enum value | Delivery |
diff --git a/ads/player-integration/index.mdx b/ads/player-integration/index.mdx
index f17182f6b40b..5c4fa38e104b 100644
--- a/ads/player-integration/index.mdx
+++ b/ads/player-integration/index.mdx
@@ -9,7 +9,7 @@ To play the ad breaks you schedule with OptiView Ads, your application needs a p
## OptiView Ads SDK (recommended)
-The [OptiView Ads SDK](./optiview-ads-sdk/index.mdx) lets you **bring your own player**. It handles the Break Manifest logic and ad tracking/analytics for you, while your application keeps full control over content playback. It is available for Web, Android, iOS, and React Native, with ready-made adapters for popular players.
+The [OptiView Ads SDK](./optiview-ads-sdk/index.mdx) lets you **bring your own player**. It handles the Break Manifest logic and ad tracking/analytics for you, while your application keeps full control over content playback. It is available for Web, Android, iOS, and React Native, with ready-made adapters for popular players, and plays every [ad workflow](./optiview-ads-sdk/index.mdx#ad-workflows): static assets, VAST tags, and Google Ad Manager.
This is the recommended path for any new integration.
diff --git a/ads/player-integration/optiview-ads-sdk/android.mdx b/ads/player-integration/optiview-ads-sdk/android.mdx
index 28c97d7d87ff..2841c1fc73e2 100644
--- a/ads/player-integration/optiview-ads-sdk/android.mdx
+++ b/ads/player-integration/optiview-ads-sdk/android.mdx
@@ -10,7 +10,7 @@ On Android and Android TV, the OptiView Ads SDK is a native Kotlin library that
## Prerequisites
1. Retrieve the channel's Break Manifest URL. See [Retrieving the required values](./index.mdx#retrieving-the-required-values).
-2. For [Google Pod Serving](../../integrations/google/pod-serving.mdx), have your Google Ad Manager `networkCode` and the channel's `customAssetKey` at hand.
+2. Check which [ad workflows](./index.mdx#ad-workflows) your breaks use. On Android, static assets, VAST, and Google Ad Manager all work with the installation below; nothing extra is needed.
3. Install the SDK together with the adapter for your player (see [Installation](#installation) and the player sections below).
## Installation
@@ -27,7 +27,7 @@ dependencyResolutionManagement {
}
```
-Then add the runtime to your app module. It brings in the core and SDK layers, Media3 and the Google IMA SDK transitively; replace `` with the SDK release you want to use:
+Then add the runtime to your app module. It brings in the core and SDK layers, Media3, and the Google IMA SDK (used to play [VAST](../../workflows/live/vast.mdx) and [Google Ad Manager](../../workflows/live/google-ad-manager.mdx) breaks) transitively; replace `` with the SDK release you want to use:
```kotlin
dependencies {
diff --git a/ads/player-integration/optiview-ads-sdk/index.mdx b/ads/player-integration/optiview-ads-sdk/index.mdx
index 75286c76d360..694ba9bfd722 100644
--- a/ads/player-integration/optiview-ads-sdk/index.mdx
+++ b/ads/player-integration/optiview-ads-sdk/index.mdx
@@ -7,7 +7,7 @@ sidebar_label: OptiView Ads SDK
The OptiView Ads SDK is the recommended way to integrate OptiView Ads into your application. It is **player-agnostic**: you bring your own player, and the SDK takes care of the OptiView Ads specifics:
-- **Break Manifest logic:** the SDK polls the channel's [Break Manifest](../../concepts/break-manifest.mdx) and schedules the announced breaks against your player's timeline. You do not implement any polling or scheduling yourself.
+- **Break Manifest logic:** the SDK reads the channel's [Break Manifest](../../concepts/break-manifest.mdx) — polling it on a live stream, fetching it once for a VOD asset — and schedules the announced breaks against your player's timeline. You do not implement any polling or scheduling yourself.
- **Ad playback:** the SDK renders the scheduled ads, including the different [break layouts](../../concepts/breaks.mdx#layouts), on top of your content player.
- **Ad tracking and analytics:** the SDK reports the ad impressions and tracking events for the ads it plays.
@@ -15,7 +15,19 @@ The SDK is available for [Web](./web.mdx), [Android](./android.mdx), [iOS](./ios
## How the SDK works with your player
-The SDK never talks to your player directly. All player interaction goes through a small **player adapter** interface, which exposes the playback state the SDK needs — most importantly the stream's timing information (such as the `EXT-X-PROGRAM-DATE-TIME` position on a live HLS stream) that breaks are scheduled against. The SDK ships ready-made adapters for popular players on each platform, and you can implement the adapter interface yourself for any other player.
+The SDK never talks to your player directly. All player interaction goes through a small **player adapter** interface, which exposes the playback state the SDK needs — most importantly the stream's timing information that breaks are scheduled against: the `EXT-X-PROGRAM-DATE-TIME` position on a live HLS stream, or the playback position in a VOD asset. The SDK ships ready-made adapters for popular players on each platform, and you can implement the adapter interface yourself for any other player.
+
+## Ad workflows
+
+The SDK plays whatever the breaks on your channel carry, for [live](../../workflows/live/index.mdx) and [VOD](../../workflows/vod/index.mdx) channels alike, and picks the [delivery mode](../../workflows/index.mdx#hybrid-ad-insertion-sgai-and-ssai) — server-guided or server-side — that the channel sets for the device. Each [asset](../../concepts/breaks.mdx#asset-model) in a break uses one of three ad sources, and one channel can mix them:
+
+| Ad source | Live | VOD | What the break carries | What your app needs on top of the platform integration |
+| --------------------- | -------------------------------------------------- | ------------------------------------------------ | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- |
+| Static assets | [Live](../../workflows/live/static-assets.mdx) | [VOD](../../workflows/vod/static-assets.mdx) | Direct URLs to video or image creatives, hosted on your CDN. | Nothing. |
+| Your ad server (VAST) | [Live](../../workflows/live/vast.mdx) | [VOD](../../workflows/vod/vast.mdx) | A VAST tag URL from your ad server. | On Web, Google's client-side IMA SDK (`ima3.js`) on the page. Nothing on Android, iOS, React Native. |
+| Google Ad Manager | [Live](../../workflows/live/google-ad-manager.mdx) | [VOD](../../workflows/vod/google-ad-manager.mdx) | An ad pod decisioned by Google Ad Manager. | On Web, Google's IMA DAI SDK (`ima3_dai.js`) on the page. Nothing on Android, iOS, React Native. |
+
+The platform guides cover the integration that every workflow shares: installing the SDK, wrapping your player, and starting a session. The [Ad workflows](../../workflows/index.mdx) section describes how to set up breaks for each ad source on live and VOD, which layouts it supports, and how to troubleshoot it.
## Configuration
@@ -24,32 +36,36 @@ Configuration is split into two levels:
- **SDK configuration** is provided once, when you create the SDK instance. It describes your player and how ads should be presented.
- **Session configuration** is provided per channel, when you start a monetization session. It identifies the channel to monetize.
+Neither level holds ad server URLs or accounts. What to play, and where to fetch it from, comes from the channel's Break Manifest — including the delivery mode and the settings for [Google Ad Manager](../../workflows/live/google-ad-manager.mdx).
+
### SDK configuration
-| Property | Description |
-| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `player` | The adapter that wraps your content player. **Required.** |
-| `container` | The element or view in which the SDK renders ads, stacked above your content player. **Required.** |
-| `mode` | The ad insertion model: `sgai` (server-guided, the default) or `ssai` (server-side, for streams where the ads are stitched into the media stream). |
-| `gam` | Google Ad Manager configuration for [Google Pod Serving](../../integrations/google/pod-serving.mdx): contains your `networkCode`. |
-| `debug` | Enables verbose logging during development. |
+| Property | Description |
+| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `player` | The adapter that wraps your content player. **Required.** |
+| `container` | The element or view in which the SDK renders ads, stacked above your content player. **Required.** |
+| `mode` | Optional. The ad insertion model: `sgai` (server-guided) or `ssai` (server-side, for streams where the ads are stitched into the media stream). By default the SDK uses the mode the channel's delivery rules set for the device, and `sgai` when there are none; set it only to override the channel. |
+| `gam` | Optional. Google Ad Manager configuration. Google Ad Manager starts automatically when the Break Manifest carries a Google configuration. See [Google Ad Manager](../../workflows/live/google-ad-manager.mdx). |
+| `debug` | Enables verbose logging during development. |
The SDK also exposes optional presentation settings — such as ad preloading, break chaining, and tune-in behavior — with sensible defaults. The platform pages describe the platform-specific options.
### Session configuration
-| Property | Description |
-| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `manifestUrl` | The channel's Break Manifest URL. **Required.** See [Retrieving the required values](#retrieving-the-required-values). |
-| `customAssetKey` | The Google Pod Serving custom asset key of the channel. Required to play Google Ad Manager breaks. See [Google Pod Serving](../../integrations/google/pod-serving.mdx). |
-| `assetParameters` | Session-level ad parameters, merged with the break's `assetParameters` and forwarded to Google as `adTagParameters`. Use these for player-side values such as device identifiers. See [Ad tag parameters](../../integrations/google/pod-serving.mdx#ad-tag-parameters). |
+| Property | Description |
+| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `manifestUrl` | The channel's Break Manifest URL. **Required.** See [Retrieving the required values](#retrieving-the-required-values). |
+| `assetParameters` | Session-level ad targeting parameters: appended to static asset and VAST tag URLs, or sent to Google as `adTagParameters`. See [Ad targeting parameters](../../how-to-guides/ad-tag-parameters.mdx). |
## Retrieving the required values
+The Break Manifest URL is the only value your app needs:
+
- **Break Manifest URL (`manifestUrl`):** every channel exposes its Break Manifest on a public endpoint. The URL follows the pattern `https://us.markers.optiview.dolby.com/manifest/v1/{orgId}/channels/{channelId}` (replace `us.` with `eu.` for the EU region), using your organization ID and the channel's ID. See the [Break Manifest endpoint](../../concepts/break-manifest.mdx#endpoint) for details.
-- **Google `networkCode` and `customAssetKey`:** these come from your Google Ad Manager setup. See the [Google Ad Manager prerequisites](../../integrations/google/index.mdx) and [Google Pod Serving](../../integrations/google/pod-serving.mdx).
-## Choose a platform
+You do not configure ad server values in the app. VAST tag URLs are set on the breaks, and for Google Ad Manager the network code and the Google asset keys are configured on the channel and delivered to the SDK through the Break Manifest.
+
+## Next steps
import DocCardList from '@theme/DocCardList';
@@ -57,9 +73,9 @@ import DocCardList from '@theme/DocCardList';
## Related resources
-| Resource | Description |
-| --------------------------------------------------------------- | ------------------------------------------------------------- |
-| [Break Manifest](../../concepts/break-manifest.mdx) | The contract between the OptiView Ads backend and the player. |
-| [Channels](../../concepts/channels.mdx) | The channel whose Break Manifest the SDK polls. |
-| [Breaks](../../concepts/breaks.mdx) | The breaks the SDK schedules and plays. |
-| [Google Pod Serving](../../integrations/google/pod-serving.mdx) | Google Ad Manager configuration for pod-served breaks. |
+| Resource | Description |
+| -------------------------------------------------------------------- | ------------------------------------------------------------- |
+| [Break Manifest](../../concepts/break-manifest.mdx) | The contract between the OptiView Ads backend and the player. |
+| [Channels](../../concepts/channels.mdx) | The channel whose Break Manifest the SDK polls. |
+| [Breaks](../../concepts/breaks.mdx) | The breaks the SDK schedules and plays. |
+| [Ad targeting parameters](../../how-to-guides/ad-tag-parameters.mdx) | Passing targeting parameters to VAST tags and Google. |
diff --git a/ads/player-integration/optiview-ads-sdk/ios.mdx b/ads/player-integration/optiview-ads-sdk/ios.mdx
index 6cbdcd2f4a44..5ec916aff2d6 100644
--- a/ads/player-integration/optiview-ads-sdk/ios.mdx
+++ b/ads/player-integration/optiview-ads-sdk/ios.mdx
@@ -10,7 +10,7 @@ On iOS and tvOS, the OptiView Ads SDK is a native Swift library that works with
## Prerequisites
1. Retrieve the channel's Break Manifest URL. See [Retrieving the required values](./index.mdx#retrieving-the-required-values).
-2. For [Google Pod Serving](../../integrations/google/pod-serving.mdx), have your Google Ad Manager `networkCode` and the channel's `customAssetKey` at hand.
+2. Check which [ad workflows](./index.mdx#ad-workflows) your breaks use. On iOS and tvOS, static assets, VAST, and Google Ad Manager all work with the installation below; nothing extra is needed.
3. Install the SDK together with the adapter for your player (see [Installation](#installation) and the player sections below).
## Installation
@@ -30,11 +30,11 @@ target 'MyApp' do
end
```
-The runtime pod also brings in the Google IMA SDK.
+The runtime pod also brings in the Google IMA SDK, which the SDK uses to play [VAST](../../workflows/live/vast.mdx) and [Google Ad Manager](../../workflows/live/google-ad-manager.mdx) breaks.
### Swift Package Manager
-Add the package from [https://github.com/Dolby-OptiView/optiview-ads-sdk-spm](https://github.com/Dolby-OptiView/optiview-ads-sdk-spm) and link the `OptiViewAdsRuntime` product. Because the frameworks are binary targets, also add the Google IMA package for each platform you build for:
+Add the package from [https://github.com/Dolby-OptiView/optiview-ads-sdk-spm](https://github.com/Dolby-OptiView/optiview-ads-sdk-spm) and link the `OptiViewAdsRuntime` product. Because the frameworks are binary targets, also add the Google IMA package for each platform you build for. The runtime always links against Google IMA, so the package is required even if your breaks only use static assets:
```swift
dependencies: [
diff --git a/ads/player-integration/optiview-ads-sdk/react-native.mdx b/ads/player-integration/optiview-ads-sdk/react-native.mdx
index c2cb9d5ea433..e269d8bd7c97 100644
--- a/ads/player-integration/optiview-ads-sdk/react-native.mdx
+++ b/ads/player-integration/optiview-ads-sdk/react-native.mdx
@@ -10,7 +10,7 @@ On React Native, the OptiView Ads SDK bridges the native Android and iOS SDKs be
## Prerequisites
1. Retrieve the channel's Break Manifest URL. See [Retrieving the required values](./index.mdx#retrieving-the-required-values).
-2. For [Google Pod Serving](../../integrations/google/pod-serving.mdx), have your Google Ad Manager `networkCode` and the channel's `customAssetKey` at hand.
+2. Check which [ad workflows](./index.mdx#ad-workflows) your breaks use. On React Native, static assets, VAST, and Google Ad Manager all work with the installation below; nothing extra is needed.
3. Install the SDK and make its native dependencies resolvable for your platforms (see [Installation](#installation)).
## Installation
@@ -57,7 +57,7 @@ import { createOptiViewAds } from '@dolby-optiview/ads-sdk-react-native';
const onPlayerReady = async (player) => {
player.source = { sources: [{ src: CONTENT_URL, type: 'application/x-mpegurl' }] };
- const ads = createOptiViewAds(player, { gam: { networkCode: 'NETWORK-CODE' } });
+ const ads = createOptiViewAds(player, {});
await ads.startSession({
manifestUrl: 'https://us.markers.optiview.dolby.com/manifest/v1/ORG-ID/channels/CHANNEL-ID',
});
@@ -92,7 +92,7 @@ const { player, onVideoEvent } = useReactVideoHandle();
useEffect(() => {
if (!player) return;
- const ads = createOptiViewAds(player, { gam: { networkCode: 'NETWORK-CODE' } });
+ const ads = createOptiViewAds(player, {});
void ads.startSession({ manifestUrl: MANIFEST_URL });
return () => void ads.destroy();
}, [player]);
diff --git a/ads/player-integration/optiview-ads-sdk/web.mdx b/ads/player-integration/optiview-ads-sdk/web.mdx
index ed81358afe84..b1d8fb4168b9 100644
--- a/ads/player-integration/optiview-ads-sdk/web.mdx
+++ b/ads/player-integration/optiview-ads-sdk/web.mdx
@@ -10,7 +10,7 @@ On Web, the OptiView Ads SDK works with any HLS-capable player through a player
## Prerequisites
1. Retrieve the channel's Break Manifest URL. See [Retrieving the required values](./index.mdx#retrieving-the-required-values).
-2. For [Google Pod Serving](../../integrations/google/pod-serving.mdx), have your Google Ad Manager `networkCode` and the channel's `customAssetKey` at hand.
+2. Check which [ad workflows](./index.mdx#ad-workflows) your breaks use. Static assets need nothing extra; [VAST](../../workflows/live/vast.mdx) and [Google Ad Manager](../../workflows/live/google-ad-manager.mdx) each need a Google IMA script on the page.
3. Install the SDK together with the adapter for your player (see the player sections below).
## Integration
@@ -24,7 +24,6 @@ import { THEOplayerAdapter } from '@dolby-optiview/ads-sdk-adapter-theoplayer';
const sdk = new OptiViewAds({
player: new THEOplayerAdapter(player, container),
container: document.getElementById('container'),
- gam: { networkCode: 'NETWORK-CODE' },
});
await sdk.startSession({
@@ -34,6 +33,8 @@ await sdk.startSession({
From this point the SDK polls the [Break Manifest](../../concepts/break-manifest.mdx), schedules the breaks against your player's timeline, plays the ads, and reports the impressions. To verify the integration, schedule a break through the API or dashboard and confirm that it plays out.
+This integration is the same for every ad workflow. If your breaks use VAST or Google Ad Manager, also load the Google IMA script that the [VAST](../../workflows/live/vast.mdx#what-your-app-needs) or [Google Ad Manager](../../workflows/live/google-ad-manager.mdx#what-your-app-needs) page lists.
+
All configuration options, session settings, and ad events are documented in the [Web API reference](pathname:///ads/v2/api-reference/web/), starting from the [`OptiViewAds`](pathname:///ads/v2/api-reference/web/classes/OptiViewAds.html) class.
## Supported players
diff --git a/ads/workflows/_shared/_gam-app.mdx b/ads/workflows/_shared/_gam-app.mdx
new file mode 100644
index 000000000000..389f93bb2ef6
--- /dev/null
+++ b/ads/workflows/_shared/_gam-app.mdx
@@ -0,0 +1,16 @@
+The SDK reads the Google settings — your network code and the channel's Google asset key — from the channel's Break Manifest, and starts the Google stream session automatically when the session starts. You do not configure them in the app, so your app and the backend always agree on which Google stream the ads belong to.
+
+| Platform | Requirement |
+| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Web | Load Google's IMA DAI SDK on the page, before your application script (see below). |
+| Android | Nothing extra: the SDK runtime includes the Google IMA SDK. |
+| iOS and tvOS | Nothing extra: the runtime pod includes the Google IMA SDK. With Swift Package Manager, add the Google IMA packages as described in [iOS](../../player-integration/optiview-ads-sdk/ios.mdx#swift-package-manager). |
+| React Native | Nothing extra: the native SDKs include the Google IMA SDK. |
+
+On Web, add the IMA DAI loader to your page:
+
+```html
+
+```
+
+If your channel also has VAST breaks, load `ima3.js` as well; the two scripts work side by side.
diff --git a/ads/workflows/_shared/_gam-ssai-session.mdx b/ads/workflows/_shared/_gam-ssai-session.mdx
new file mode 100644
index 000000000000..8593a2090931
--- /dev/null
+++ b/ads/workflows/_shared/_gam-ssai-session.mdx
@@ -0,0 +1 @@
+When the channel steers a device to SSAI, the SDK requests the stitched stream from Google and loads it into your content player instead of your original stream URL. Your app keeps the same integration and starts the session with the same Break Manifest URL. On Web, use a content player adapter that exposes its video element, such as the HLS.js, Shaka, or native video adapter. In a server-side session, the SDK still plays the [event-triggered breaks](../../concepts/breaks.mdx#event-based-triggers) — pre-roll, pause, and post-roll — on the device.
diff --git a/ads/workflows/_shared/_gam-targeting.mdx b/ads/workflows/_shared/_gam-targeting.mdx
new file mode 100644
index 000000000000..168a93e154c6
--- /dev/null
+++ b/ads/workflows/_shared/_gam-targeting.mdx
@@ -0,0 +1,6 @@
+For Google, `assetParameters` are sent as Google [ad tag parameters](https://support.google.com/admanager/answer/10678356) (`adTagParameters`). As a rule of thumb:
+
+- Set the values that describe your content on the break, such as `description_url` and `plcmt`. See [Required Google parameters](../../integrations/google/pod-serving.mdx#required-google-parameters).
+- Set the values that depend on the playback environment in the SDK, such as `vpa` and `vpmute` and, in apps, the [device and app identity parameters](../../integrations/google/pod-serving.mdx#additional-parameters-for-app-inventory).
+
+When the same key is set in both places, the value on the break wins. See [Ad targeting parameters](../../how-to-guides/ad-tag-parameters.mdx) for the merge rules and platform examples.
diff --git a/ads/workflows/_shared/_gam-troubleshooting.mdx b/ads/workflows/_shared/_gam-troubleshooting.mdx
new file mode 100644
index 000000000000..55113dccd5c6
--- /dev/null
+++ b/ads/workflows/_shared/_gam-troubleshooting.mdx
@@ -0,0 +1,7 @@
+| Diagnostic | What it means | What to do |
+| ------------------------ | ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
+| `DA-GAM-CONFIG-RESOLVED` | Informational: the Google stream session uses the network code and asset key in the diagnostic. | Check that they match your Google stream. |
+| `DA-GAM-CONFIG-MISSING` | The Break Manifest carries Google breaks but no usable Google configuration. | Check that the channel is connected to Google. |
+| `DA-GAM-SESSION-FAILED` | The Google stream session could not be started. | On Web, load `ima3_dai.js` on the page. Otherwise, check the error message in the diagnostic. |
+
+Breaks that fail in the backend, for example because Google could not decision them in time, end up in the `ERROR` state and never reach the SDK. See [Troubleshooting](../../integrations/google/pod-serving.mdx#troubleshooting) on the Google Pod Serving integration page for the possible reasons.
diff --git a/ads/workflows/_shared/_static-assets-formats.mdx b/ads/workflows/_shared/_static-assets-formats.mdx
new file mode 100644
index 000000000000..9b75a3f4ae8a
--- /dev/null
+++ b/ads/workflows/_shared/_static-assets-formats.mdx
@@ -0,0 +1,5 @@
+- **HLS (`.m3u8`)** is the recommended format for video creatives on every platform. On connected TVs in particular, the SDK can load an HLS creative ahead of the break, so the ad starts faster.
+- **Progressive MP4** also works. On Web, the SDK plays MP4 creatives through the browser's own video element.
+- **Images** are used for overlays, companions, L-shape backdrops, and pause ads.
+
+Set `mimeType` on the asset when you know it. The SDK then knows upfront how to play the creative, instead of having to find out, which makes ad preparation faster.
diff --git a/ads/workflows/_shared/_static-assets-layouts.mdx b/ads/workflows/_shared/_static-assets-layouts.mdx
new file mode 100644
index 000000000000..61e29571fa97
--- /dev/null
+++ b/ads/workflows/_shared/_static-assets-layouts.mdx
@@ -0,0 +1,13 @@
+Static assets work in every [layout](../../concepts/breaks.mdx#layouts):
+
+| Layout | Static asset |
+| --------------- | ----------------------------------------------------------------------------- |
+| Single | A video that replaces the content. |
+| Double box | A video next to your content, with an image or video `companion` behind both. |
+| L-shape ad | A video, with an image or video `companion` that fills the L-shaped backdrop. |
+| L-shape content | An image that fills the L-shaped backdrop while your content keeps playing. |
+| Overlay | An image or video on top of your content. |
+
+An image stays on screen for the asset's `duration`, or for the break's `duration` when the asset has none.
+
+Static assets can also be used for [pause ads](../../concepts/breaks.mdx#pause): an image, or a progressive MP4 video that plays muted, shown while the viewer is paused.
diff --git a/ads/workflows/_shared/_static-assets-troubleshooting.mdx b/ads/workflows/_shared/_static-assets-troubleshooting.mdx
new file mode 100644
index 000000000000..287f0377da19
--- /dev/null
+++ b/ads/workflows/_shared/_static-assets-troubleshooting.mdx
@@ -0,0 +1,3 @@
+- **The break does not play.** Check that the channel's [Break Manifest](../../concepts/break-manifest.mdx) contains the break, and that the creative URL is publicly reachable.
+- **Web: the creative fails to load.** The SDK downloads HLS creatives with HTTP requests from your page, so your CDN must allow cross-origin requests (CORS) from your app's origin.
+- **A creative cannot be played.** The SDK emits an `aderror` event for the asset, continues with the next asset in the break if there is one, and otherwise returns to your content.
diff --git a/ads/workflows/_shared/_vast-app.mdx b/ads/workflows/_shared/_vast-app.mdx
new file mode 100644
index 000000000000..3b1c56f7d71b
--- /dev/null
+++ b/ads/workflows/_shared/_vast-app.mdx
@@ -0,0 +1,16 @@
+The SDK requests and plays VAST tags on the device, through Google's client-side IMA SDK. IMA processes the VAST response — it follows wrappers, picks a media file, and fires the tracking URLs. IMA works with any VAST-compliant ad server: you do not need a Google Ad Manager account, although a Google Ad Manager VAST tag works too.
+
+| Platform | Requirement |
+| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Web | Load Google's client-side IMA SDK on the page, before your application script (see below). |
+| Android | Nothing extra: the SDK runtime includes the Google IMA SDK. |
+| iOS and tvOS | Nothing extra: the runtime pod includes the Google IMA SDK. With Swift Package Manager, add the Google IMA packages as described in [iOS](../../player-integration/optiview-ads-sdk/ios.mdx#swift-package-manager). |
+| React Native | Nothing extra: the native SDKs include the Google IMA SDK. |
+
+On Web, add the IMA loader to your page:
+
+```html
+
+```
+
+Without it, the SDK cannot play VAST breaks: it reports `DA-VAST-IMA-SDK-MISSING` and returns to your content. If your channel also has Google Ad Manager breaks, load `ima3_dai.js` as well; the two scripts work side by side.
diff --git a/ads/workflows/_shared/_vast-layouts.mdx b/ads/workflows/_shared/_vast-layouts.mdx
new file mode 100644
index 000000000000..6a7721bc813f
--- /dev/null
+++ b/ads/workflows/_shared/_vast-layouts.mdx
@@ -0,0 +1,13 @@
+VAST describes a linear (video) ad, so VAST assets work in the [layouts](../../concepts/breaks.mdx#layouts) where an ad video plays:
+
+| Layout | VAST asset |
+| --------------- | -------------------------------------------------------------------------------------------------------------------- |
+| Single | Supported. |
+| Double box | Supported. The VAST ad plays in the ad box, and the asset's `companion` fills the background. |
+| L-shape ad | Supported. The VAST ad plays in the ad window, and the asset's `companion` fills the L-shaped backdrop. |
+| L-shape content | Not supported. The SDK does not play the asset and reports `DA-VAST-UNSUPPORTED-FORMAT`. Use a static asset instead. |
+| Overlay | Not supported. The SDK does not play the asset and reports `DA-VAST-UNSUPPORTED-FORMAT`. Use a static asset instead. |
+
+VAST assets can also be used for [pause ads](../../concepts/breaks.mdx#pause): the SDK reads the image from the VAST companion ads, or a progressive MP4 from the linear ad, and fires its impression tracking when shown.
+
+VAST breaks play in server-guided (SGAI) sessions only.
diff --git a/ads/workflows/_shared/_vast-targeting.mdx b/ads/workflows/_shared/_vast-targeting.mdx
new file mode 100644
index 000000000000..7a35c0bc7c27
--- /dev/null
+++ b/ads/workflows/_shared/_vast-targeting.mdx
@@ -0,0 +1,12 @@
+To personalize the ad request, add `assetParameters`. The SDK appends them to the VAST tag URL as URL-encoded query parameters when it requests the tag. For the break above, with a session-level parameter `device_id` set in the SDK, the request becomes:
+
+```text
+https://adserver.example.com/vast?slot=midroll&content_genre=sports&device_id=abc123
+```
+
+Set the parameters in one of two places:
+
+- **On the break**, for values that are the same for every viewer, such as the content genre.
+- **In the SDK**, with `assetParameters` in the session configuration or `updateAssetParameters()`, for values that depend on the viewer or the device.
+
+When the same key is set in both places, the value on the break wins. Parameter values can also contain macros, such as `$OPTIVIEW_PLAYER_WIDTH$`, that the SDK fills in when it builds the request. See [Ad targeting parameters](../../how-to-guides/ad-tag-parameters.mdx) for the merge rules, the macros, and platform examples.
diff --git a/ads/workflows/_shared/_vast-tracking.mdx b/ads/workflows/_shared/_vast-tracking.mdx
new file mode 100644
index 000000000000..731db36eb40d
--- /dev/null
+++ b/ads/workflows/_shared/_vast-tracking.mdx
@@ -0,0 +1,2 @@
+- IMA fires the tracking URLs in the VAST response, such as impressions, quartiles, and clicks, so the ad server counts the ad.
+- The SDK reports an OptiView Ads [impression](../../how-to-guides/ad-impressions.md) for the break, and emits the same ad events as for any other break.
diff --git a/ads/workflows/_shared/_vast-troubleshooting.mdx b/ads/workflows/_shared/_vast-troubleshooting.mdx
new file mode 100644
index 000000000000..8e9ef8295aec
--- /dev/null
+++ b/ads/workflows/_shared/_vast-troubleshooting.mdx
@@ -0,0 +1,8 @@
+VAST failures never block your content: the SDK emits an `aderror` event, reports a diagnostic, and returns to the content.
+
+| Diagnostic | What it means | What to do |
+| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `DA-VAST-IMA-SDK-MISSING` | Google's client-side IMA SDK is not available. | On Web, load `ima3.js` on the page. |
+| `DA-VAST-IMA-ERROR` | IMA could not load, parse, or play the VAST response — for example an unreachable tag, an empty response, or no playable media file. | Test the tag in Google's [VAST Inspector](https://googleads.github.io/googleads-ima-html5/vsi/). On Web, make sure the response includes a progressive MP4 media file. |
+| `DA-VAST-UNSUPPORTED-FORMAT` | The VAST asset is in an `overlay` or `lshape_content` break. | Use the `single`, `double`, or `lshape_ad` layout, or a static asset. |
+| `DA-VAST-SSAI-UNSUPPORTED` | The VAST asset is in a server-side (SSAI) session. | Play VAST breaks in a server-guided (`sgai`) session. |
diff --git a/ads/workflows/index.mdx b/ads/workflows/index.mdx
new file mode 100644
index 000000000000..54b3923c6e50
--- /dev/null
+++ b/ads/workflows/index.mdx
@@ -0,0 +1,62 @@
+---
+sidebar_position: 0
+sidebar_label: Ad workflows
+description: How OptiView Ads monetizes live and VOD content with static assets, VAST, and Google Ad Manager.
+---
+
+# Ad workflows
+
+OptiView Ads is a **hybrid ad insertion platform**: the same channel and the same breaks can be delivered server-guided (SGAI) or server-side (SSAI), for live streams and for video-on-demand (VOD) assets. Your ad workflow comes down to three choices:
+
+1. **Use case** — are you monetizing a [live stream](./live/index.mdx) or a [VOD asset](./vod/index.mdx)?
+2. **Ad source** — where do the ads come from: static assets from your CDN, your ad server through VAST tags, or Google Ad Manager?
+3. **Delivery mode** — does the player insert the ads (SGAI), or are they stitched into the stream (SSAI)?
+
+Start from your use case, [Live](./live/index.mdx) or [VOD](./vod/index.mdx): each has a page per ad source with everything you need to set it up.
+
+## Hybrid ad insertion: SGAI and SSAI
+
+| Delivery mode | How it works | Use it for |
+| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **Server-Guided Ad Insertion (SGAI)** | The OptiView Ads backend publishes the breaks in a side-loaded [Break Manifest](../concepts/break-manifest.mdx). The [OptiView Ads SDK](../player-integration/optiview-ads-sdk/index.mdx) reads it, and plays the ads on top of your content. | Web, mobile, and most connected TV apps. Required for the advanced [layouts](../concepts/breaks.mdx#layouts) such as double box, L-shape, and overlays. |
+| **Server-Side Ad Insertion (SSAI)** | The ads are stitched into the media stream before it reaches the player. The player plays one continuous stream. | Platforms where a client-side SDK is not an option, such as Roku and older connected TV devices. |
+
+Both modes share the same model: you create a [channel](../concepts/channels.mdx), schedule or detect [breaks](../concepts/breaks.mdx) on it, and OptiView Ads delivers them. You do not maintain two ad setups.
+
+A channel can **steer the delivery mode per device**: for example SGAI on web and mobile, and SSAI on a specific connected TV platform. The Break Manifest then carries the delivery rules, and the SDK picks the mode for the device it runs on when the session starts. In SSAI mode, the SDK still plays the [event-triggered breaks](../concepts/breaks.mdx#event-based-triggers) — pre-roll, pause, and post-roll — on the device, because they depend on what the individual viewer does.
+
+SSAI is available with Google Ad Manager, through Google DAI. Static assets and VAST tags are always played server-guided.
+
+## Live and VOD
+
+Live and VOD are different use cases, and a channel is created for one of them. The difference is mainly in how breaks are placed on the timeline and how players receive them:
+
+| | [Live](./live/index.mdx) | [VOD](./vod/index.mdx) |
+| ------------------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
+| Channel | `streamType: "live"` (the default) | `streamType: "vod"` |
+| Break start | A moment on the live timeline: a UTC timestamp (`wallclock`) or a presentation timestamp (`pts`) | A position in the asset: seconds from the start (`mediatime`), or a UTC timestamp (`wallclock`) |
+| How breaks are made | Scheduled ahead or on the spot, [punched](../concepts/breaks.mdx#break-punching) live, or detected from ad markers in the stream | Scheduled upfront, for example as pre-roll, mid-rolls, and post-roll, or detected from ad markers in the asset |
+| Break Manifest | Polled continuously; breaks appear as they become relevant | Fetched once at the start of playback; it lists all breaks |
+| Events | [Events](../concepts/events.mdx) group the breaks of a live event | Not used |
+
+## Supported workflows
+
+Every ad source works for both live and VOD. One channel, and even one break, can mix ad sources.
+
+| Ad source | What the break carries | Delivery mode | What your app needs |
+| --------------------- | ----------------------------------------------------- | ------------- | -------------------------------------------------------------------------- |
+| Static assets | Direct URLs to video or image creatives on your CDN. | SGAI | Nothing beyond the SDK integration. |
+| Your ad server (VAST) | A VAST tag URL on your ad server, or an advertiser's. | SGAI | On Web, Google's client-side IMA SDK (`ima3.js`). |
+| Google Ad Manager | An ad pod decisioned by Google Ad Manager 360. | SGAI + SSAI | On Web, Google's IMA DAI SDK (`ima3_dai.js`). Google Ad Manager 360 setup. |
+
+### Which ad source should I use?
+
+- **Static assets** when you know which creative to show: direct-sold campaigns, sponsorships, promos, branded overlays. No ad server is involved.
+- **Your ad server (VAST)** when your own ad server, or a partner's, decides which ad plays. Any VAST-compliant ad server works, Google Ad Manager tags included.
+- **Google Ad Manager** when you sell your inventory through Google Ad Manager 360 and want Google to fill complete ad pods, or need server-side insertion through Google DAI.
+
+## Next steps
+
+import DocCardList from '@theme/DocCardList';
+
+
diff --git a/ads/workflows/live/google-ad-manager.mdx b/ads/workflows/live/google-ad-manager.mdx
new file mode 100644
index 000000000000..940759563746
--- /dev/null
+++ b/ads/workflows/live/google-ad-manager.mdx
@@ -0,0 +1,101 @@
+---
+sidebar_position: 3
+sidebar_label: Google Ad Manager
+description: Let Google Ad Manager 360 fill the breaks of a live stream, server-guided (Pod Serving) or server-side (DAI).
+---
+
+# Google Ad Manager on live
+
+import AppNeeds from '../_shared/_gam-app.mdx';
+import SsaiSession from '../_shared/_gam-ssai-session.mdx';
+import Targeting from '../_shared/_gam-targeting.mdx';
+import Troubleshooting from '../_shared/_gam-troubleshooting.mdx';
+
+With Google Ad Manager 360, Google decides which ads fill each break of your live stream. OptiView Ads announces the breaks to Google ahead of time, Google assembles the ads into a pod, and the pod plays when the break starts. Use this workflow when you sell your inventory through Google Ad Manager 360, and to reach platforms that need server-side ad insertion.
+
+| | Google Ad Manager on live |
+| -------------- | ----------------------------------------------------------------------------------------- |
+| Delivery mode | Server-guided with Google Pod Serving (SGAI), server-side with Google DAI (SSAI), or both |
+| Ad decisioning | Google Ad Manager, ahead of the break |
+| Break creation | Scheduled, punched, or detected from the ad markers in your stream |
+| Your app needs | The OptiView Ads SDK; on Web also Google's IMA DAI SDK |
+
+## Choose a delivery mode
+
+| Delivery mode | How it works | Use it for |
+| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
+| [Google Pod Serving](../../integrations/google/pod-serving.mdx) (SGAI) | Google decisions the pod; the SDK inserts it into your untouched stream, guided by the Break Manifest. | Web, mobile, and most connected TV apps. Required for the double box, L-shape, and overlay layouts. |
+| [Google DAI](../../integrations/google/dai.mdx) (SSAI) | The Dolby Ads Proxy inserts the break cues into the stream Google reads; Google stitches the ads into the stream for viewers. | Platforms where a client-side SDK is not an option, such as Roku and older connected TV devices. |
+
+One channel can serve both: the channel steers the [delivery mode per device](../index.mdx#hybrid-ad-insertion-sgai-and-ssai), and the SDK picks it up from the Break Manifest.
+
+## Before you start
+
+- The [Google Ad Manager prerequisites](../../integrations/google/index.mdx) — network code and service account — are in place for your organization.
+- A [live channel](./index.mdx#set-up-a-live-channel), connected to Google: with the custom asset key of your Google live stream for [Google Pod Serving](../../integrations/google/pod-serving.mdx#configure-your-channel), and with the DAI asset key for [Google DAI](../../integrations/google/dai.mdx#configure-your-channel).
+- The OptiView Ads SDK in your app: [Web](../../player-integration/optiview-ads-sdk/web.mdx), [Android](../../player-integration/optiview-ads-sdk/android.mdx), [iOS](../../player-integration/optiview-ads-sdk/ios.mdx), or [React Native](../../player-integration/optiview-ads-sdk/react-native.mdx).
+
+## Schedule a break
+
+A Google break carries a [vendor asset](../../concepts/breaks.mdx#vendor-assets) with `vendor` set to `gam` and `vendorParameters.type` set to `pod`. Google performs the decisioning, so the asset needs no URI. The same break serves both delivery modes.
+
+```bash
+curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels//breaks' \
+ -u "$ADS_API_KEY:$ADS_API_SECRET" \
+ -H 'Content-Type: application/json' \
+ -H 'X-Org-ID: ' \
+ -d '{
+ "start": "2026-07-16T12:15:00.000Z",
+ "duration": 60,
+ "variant": {
+ "format": "single",
+ "assets": [
+ {
+ "type": "vendor",
+ "vendor": "gam",
+ "vendorParameters": { "type": "pod" },
+ "assetParameters": {
+ "description_url": "https://www.example.com/sports/live.html",
+ "plcmt": "1"
+ }
+ }
+ ]
+ }
+ }'
+```
+
+- OptiView Ads announces the break to Google within the [look-forward time](../../integrations/google/index.mdx#look-forward-time) before its start, so Google can decision the pod in time.
+- Create the break at least the [decisioning margin](../../integrations/google/index.mdx#decisioning-margin) before its start, or it is rejected.
+- The break starts in the `PREPARING` state, and becomes `READY` once Google has decisioned it. See [How pod decisioning works](../../integrations/google/pod-serving.mdx#how-pod-decisioning-works).
+
+### Other ways to create the break
+
+- **Punch it live.** Create the break without a `start`. Google decisions it ahead of time, and the break waits in the `CUED` state until you [punch](../../concepts/breaks.mdx#break-punching) it.
+- **Detect it.** Add your stream as an [origin](../../concepts/origins.mdx) and configure a [marker rule](../../concepts/marker-detection.mdx) whose [template](../../concepts/templates.mdx) holds the Google vendor asset.
+
+## What your app needs
+
+
+
+### Server-side sessions
+
+
+
+## Ad targeting parameters
+
+
+
+## Troubleshooting
+
+
+
+## Related resources
+
+| Resource | Description |
+| --------------------------------------------------------------------------- | --------------------------------------------------------------------- |
+| [Live](./index.mdx) | Live channels: timebase, break creation, and delivery modes. |
+| [Google Ad Manager](../../integrations/google/index.mdx) | Organization-level prerequisites: network code and service account. |
+| [Google Pod Serving integration](../../integrations/google/pod-serving.mdx) | Google Ad Manager setup, channel configuration, and pod breaks. |
+| [Google DAI integration](../../integrations/google/dai.mdx) | Server-side insertion: proxy, live stream, and channel configuration. |
+| [Ad targeting parameters](../../how-to-guides/ad-tag-parameters.mdx) | Passing ad tag parameters to Google. |
+| [Google Ad Manager on VOD](../vod/google-ad-manager.mdx) | The same workflow for on-demand assets. |
diff --git a/ads/workflows/live/index.mdx b/ads/workflows/live/index.mdx
new file mode 100644
index 000000000000..838a285dbb85
--- /dev/null
+++ b/ads/workflows/live/index.mdx
@@ -0,0 +1,66 @@
+---
+sidebar_position: 1
+sidebar_label: Live
+description: Monetize a live stream with OptiView Ads.
+---
+
+# Live
+
+A live channel monetizes a linear stream or a live event. Breaks are placed on the live timeline, and players watching the stream — at the live edge or behind it in the DVR window — receive them through the channel's [Break Manifest](../../concepts/break-manifest.mdx).
+
+## Set up a live channel
+
+A channel is live by default. Choose the [timebase](../../concepts/channels.mdx#timebase) that matches the timing information in your stream:
+
+- **`wallclock`** — breaks start at a UTC timestamp, matched against the stream's own wallclock timeline (`EXT-X-PROGRAM-DATE-TIME` in HLS, the segment timeline in DASH). This is the recommended timebase.
+- **`pts`** — breaks start at a presentation timestamp on the encoder's timeline.
+
+```bash
+curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels' \
+ -u "$ADS_API_KEY:$ADS_API_SECRET" \
+ -H 'Content-Type: application/json' \
+ -H 'X-Org-ID: ' \
+ -d '{
+ "name": "Sports main",
+ "streamType": "live",
+ "timebase": "wallclock"
+ }'
+```
+
+The [DVR window](../../concepts/channels.mdx#dvr-window), [live offset](../../concepts/channels.mdx#live-offset), and [ad prefetch window](../../concepts/channels.mdx#ad-prefetch-window) settings tune which breaks the Break Manifest contains at any moment.
+
+## How breaks are created
+
+On a live stream, breaks can be created in three ways, and you can combine them on one channel:
+
+- **Scheduled** — create a break with a start time, ahead of time or on the spot. See [Scheduling](../../concepts/breaks.mdx#scheduling).
+- **Punched** — prepare a break without a start time, and fire it when the moment comes, for example from your production control room. See [Break punching](../../concepts/breaks.mdx#break-punching).
+- **Detected** — add your stream as an [origin](../../concepts/origins.mdx), and OptiView Ads turns the ad markers in the stream, such as SCTE-35 signals, into breaks. See [Break detection](../../concepts/marker-detection.mdx). This replaces the ad breaks already in your stream (Dynamic Ad Replacement).
+
+[Events](../../concepts/events.mdx) let you prepare the breaks of a live event, such as a match or a show, upfront. [Templates](../../concepts/templates.mdx) store a break configuration once, so scheduling becomes a one-liner.
+
+Besides breaks on the timeline, a live channel can carry [event-triggered breaks](../../concepts/breaks.mdx#event-based-triggers): a pre-roll when the viewer starts watching, a pause ad, and a post-roll.
+
+## How players receive the breaks
+
+The SDK polls the Break Manifest during the whole session. A break appears in the manifest shortly before it starts — within the ad prefetch window — so the SDK can prepare the ads, and it stays there while viewers in the DVR window can still reach it. Changes to a break, such as an earlier end, reach the players on the next poll.
+
+## Choose an ad source
+
+Each ad source has its own page with everything you need for live: the break setup, what your app needs, the supported layouts, and troubleshooting. Live channels support both delivery modes of the [hybrid platform](../index.mdx#hybrid-ad-insertion-sgai-and-ssai):
+
+| Ad source | SGAI | SSAI |
+| -------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------- |
+| [Static assets](./static-assets.mdx) | Yes | No |
+| [Your ad server (VAST)](./vast.mdx) | Yes | No |
+| [Google Ad Manager](./google-ad-manager.mdx) | Yes, with [Google Pod Serving](../../integrations/google/pod-serving.mdx) | Yes, with [Google DAI](../../integrations/google/dai.mdx) |
+
+## Related resources
+
+| Resource | Description |
+| ------------------------------------------------------ | ----------------------------------------------------------- |
+| [Getting started](../../getting-started/index.mdx) | Create a live channel and play your first break end to end. |
+| [Channels](../../concepts/channels.mdx) | Timebase, DVR window, live offset, and prefetch settings. |
+| [Breaks](../../concepts/breaks.mdx) | Scheduling, punching, layouts, and the asset model. |
+| [Break detection](../../concepts/marker-detection.mdx) | Turn the ad markers in your stream into breaks. |
+| [VOD](../vod/index.mdx) | Monetize on-demand assets instead. |
diff --git a/ads/workflows/live/static-assets.mdx b/ads/workflows/live/static-assets.mdx
new file mode 100644
index 000000000000..f24d7c71ab0a
--- /dev/null
+++ b/ads/workflows/live/static-assets.mdx
@@ -0,0 +1,96 @@
+---
+sidebar_position: 1
+sidebar_label: Static assets
+description: Play ad creatives from your own CDN in a live stream, without an ad server.
+---
+
+# Static assets on live
+
+import Layouts from '../_shared/_static-assets-layouts.mdx';
+import Formats from '../_shared/_static-assets-formats.mdx';
+import Troubleshooting from '../_shared/_static-assets-troubleshooting.mdx';
+
+With static assets, each break points directly at its ad creatives: video or image files that you host on your own CDN. No ad server is involved — the SDK fetches the creative from the URL on the break and plays it when the break starts. Use this workflow on live streams for direct-sold campaigns, sponsorships, promos, and branded overlays where you already know which creative to show.
+
+| | Static assets on live |
+| -------------- | ------------------------------------------------------------------ |
+| Delivery mode | Server-guided (SGAI) |
+| Ad decisioning | None: the break names the creative |
+| Break creation | Scheduled, punched, or detected from the ad markers in your stream |
+| Your app needs | The OptiView Ads SDK for your platform; nothing else |
+
+## Before you start
+
+- A [live channel](./index.mdx#set-up-a-live-channel). The examples use the `wallclock` timebase.
+- The OptiView Ads SDK in your app: [Web](../../player-integration/optiview-ads-sdk/web.mdx), [Android](../../player-integration/optiview-ads-sdk/android.mdx), [iOS](../../player-integration/optiview-ads-sdk/ios.mdx), or [React Native](../../player-integration/optiview-ads-sdk/react-native.mdx). Static assets need no ad server, no Google Ad Manager account, and no extra configuration in the app.
+- Your creatives on a CDN that the viewers' devices can reach.
+
+## Schedule a break
+
+A static asset sets `type` to `static` and `uri` to the URL of the creative. On a `wallclock` channel, `start` is the UTC moment in your stream when the break begins:
+
+```bash
+curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels//breaks' \
+ -u "$ADS_API_KEY:$ADS_API_SECRET" \
+ -H 'Content-Type: application/json' \
+ -H 'X-Org-ID: ' \
+ -d '{
+ "start": "2026-07-16T12:15:00.000Z",
+ "duration": 30,
+ "variant": {
+ "format": "single",
+ "assets": [
+ {
+ "type": "static",
+ "mediaType": "video",
+ "mimeType": "application/x-mpegurl",
+ "uri": "https://cdn.example.com/ads/spot.m3u8",
+ "interaction": { "clickThrough": "https://www.example.com/offer" }
+ }
+ ]
+ }
+ }'
+```
+
+The `uri` can also be a list of URLs targeted per device type. See [Static assets](../../concepts/breaks.mdx#static-assets) and [Asset URI targeting](../../concepts/breaks.mdx#asset-uri-targeting) for the full asset model.
+
+### Other ways to create the break
+
+- **Punch it live.** Create the break without a `start`, and [punch](../../concepts/breaks.mdx#break-punching) it when the moment comes, for example from your production control room.
+- **Detect it.** Add your stream as an [origin](../../concepts/origins.mdx) and configure a [marker rule](../../concepts/marker-detection.mdx) whose [template](../../concepts/templates.mdx) holds the static assets. Every matching ad marker in the stream, such as an SCTE-35 signal, becomes a break.
+- **Trigger it on a player event.** Set `start` to `{ "type": "event", "event": "start" }` for a pre-roll when the viewer tunes in, `"pause"` for a pause ad, or `"end"` for a post-roll. See [Event based triggers](../../concepts/breaks.mdx#event-based-triggers).
+
+## How the break plays
+
+- The SDK receives the break through the channel's [Break Manifest](../../concepts/break-manifest.mdx) shortly before it starts, within the channel's [ad prefetch window](../../concepts/channels.mdx#ad-prefetch-window), and loads the creative ahead of time so the ad starts without delay.
+- Viewers behind the live edge, in the [DVR window](../../concepts/channels.mdx#dvr-window), play the break when their playhead reaches it.
+- Static assets always play server-guided. On a channel that steers some devices to [SSAI](../index.mdx#hybrid-ad-insertion-sgai-and-ssai), those devices still play the static event-triggered breaks — pre-roll, pause, and post-roll — but not the breaks on the timeline.
+
+## Supported layouts
+
+
+
+## Creative formats
+
+
+
+## Tracking and reporting
+
+The SDK reports an OptiView Ads [impression](../../how-to-guides/ad-impressions.md) for every break it presents, and emits ad events — such as the start and end of each break and ad — that your app can listen to. See the API reference of your platform for the full list of events.
+
+Static assets carry no third-party tracking URLs. If an advertiser needs to count impressions in their own ad server, deliver the creative through a [VAST tag](./vast.mdx) instead: the VAST response carries the advertiser's tracking URLs, which are fired while the ad plays.
+
+## Troubleshooting
+
+
+
+## Related resources
+
+| Resource | Description |
+| --------------------------------------------------------------- | ------------------------------------------------------------- |
+| [Live](./index.mdx) | Live channels: timebase, break creation, and delivery modes. |
+| [Breaks](../../concepts/breaks.mdx) | Layouts, variants, controls, and the asset model. |
+| [Templates](../../concepts/templates.mdx) | Store a break with its static assets once, and reuse it. |
+| [Your ad server (VAST) on live](./vast.mdx) | Let your ad server decide which creative to play. |
+| [Static assets on VOD](../vod/static-assets.mdx) | The same workflow for on-demand assets. |
+| [Ad impression tracking](../../how-to-guides/ad-impressions.md) | How OptiView Ads counts the breaks presented to your viewers. |
diff --git a/ads/workflows/live/vast.mdx b/ads/workflows/live/vast.mdx
new file mode 100644
index 000000000000..bdadf119d0c8
--- /dev/null
+++ b/ads/workflows/live/vast.mdx
@@ -0,0 +1,102 @@
+---
+sidebar_position: 2
+sidebar_label: Your ad server (VAST)
+description: Let your own ad server, or a partner's, decide the ads in a live stream through VAST tags.
+---
+
+# Your ad server (VAST) on live
+
+import AppNeeds from '../_shared/_vast-app.mdx';
+import Layouts from '../_shared/_vast-layouts.mdx';
+import Targeting from '../_shared/_vast-targeting.mdx';
+import Tracking from '../_shared/_vast-tracking.mdx';
+import Troubleshooting from '../_shared/_vast-troubleshooting.mdx';
+
+With VAST, each break points at a VAST tag: a URL on your ad server, or an advertiser's, that returns a [VAST](https://iabtechlab.com/standards/vast/) XML document. The ad server decides which creative to play, and its response carries the media files and the tracking URLs. Use this workflow on live streams when you run your own ad server, or buy from partners that deliver their ads through VAST tags.
+
+| | Your ad server (VAST) on live |
+| -------------- | ------------------------------------------------------------------ |
+| Delivery mode | Server-guided (SGAI) |
+| Ad decisioning | Your ad server, per viewer, when the SDK requests the tag |
+| Break creation | Scheduled, punched, or detected from the ad markers in your stream |
+| Your app needs | The OptiView Ads SDK; on Web also Google's client-side IMA SDK |
+
+## Before you start
+
+- A [live channel](./index.mdx#set-up-a-live-channel). The examples use the `wallclock` timebase.
+- The OptiView Ads SDK in your app: [Web](../../player-integration/optiview-ads-sdk/web.mdx), [Android](../../player-integration/optiview-ads-sdk/android.mdx), [iOS](../../player-integration/optiview-ads-sdk/ios.mdx), or [React Native](../../player-integration/optiview-ads-sdk/react-native.mdx).
+- A VAST tag URL from your ad server, or an advertiser's.
+
+## What your app needs
+
+
+
+## Schedule a break
+
+A VAST asset sets `type` to `vast` and `uri` to the VAST tag URL. On a `wallclock` channel, `start` is the UTC moment in your stream when the break begins:
+
+```bash
+curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels//breaks' \
+ -u "$ADS_API_KEY:$ADS_API_SECRET" \
+ -H 'Content-Type: application/json' \
+ -H 'X-Org-ID: ' \
+ -d '{
+ "start": "2026-07-16T12:15:00.000Z",
+ "duration": 30,
+ "variant": {
+ "format": "single",
+ "assets": [
+ {
+ "type": "vast",
+ "mediaType": "video",
+ "mimeType": "application/xml",
+ "uri": "https://adserver.example.com/vast?slot=midroll",
+ "assetParameters": {
+ "content_genre": "sports"
+ }
+ }
+ ]
+ }
+ }'
+```
+
+The `uri` can also be a list of tag URLs targeted per device type. See [VAST assets](../../concepts/breaks.mdx#vast-assets) and [Asset URI targeting](../../concepts/breaks.mdx#asset-uri-targeting) for the full asset model.
+
+### Other ways to create the break
+
+- **Punch it live.** Create the break without a `start`, and [punch](../../concepts/breaks.mdx#break-punching) it when the moment comes.
+- **Detect it.** Add your stream as an [origin](../../concepts/origins.mdx) and configure a [marker rule](../../concepts/marker-detection.mdx) whose [template](../../concepts/templates.mdx) holds the VAST asset. Every matching ad marker in the stream becomes a break.
+- **Trigger it on a player event.** Set `start` to `{ "type": "event", "event": "start" }` for a pre-roll when the viewer tunes in, `"pause"` for a pause ad, or `"end"` for a post-roll. See [Event based triggers](../../concepts/breaks.mdx#event-based-triggers).
+
+## How the break plays
+
+- The SDK receives the break through the channel's [Break Manifest](../../concepts/break-manifest.mdx) shortly before it starts, and requests the VAST tag on the device. Every viewer gets an individual ad decision.
+- Many viewers reach a live break at the same moment, so your ad server receives a peak of requests at every break start. Make sure it is sized for your concurrent audience.
+- On a channel that steers some devices to [SSAI](../index.mdx#hybrid-ad-insertion-sgai-and-ssai), VAST breaks on the timeline are not played on those devices.
+
+## Supported layouts
+
+
+
+## Ad targeting parameters
+
+
+
+## Tracking and reporting
+
+
+
+## Troubleshooting
+
+
+
+## Related resources
+
+| Resource | Description |
+| -------------------------------------------------------------------- | ------------------------------------------------------------ |
+| [Live](./index.mdx) | Live channels: timebase, break creation, and delivery modes. |
+| [Breaks](../../concepts/breaks.mdx) | Layouts, variants, controls, and the asset model. |
+| [Ad targeting parameters](../../how-to-guides/ad-tag-parameters.mdx) | Passing targeting parameters to your ad server. |
+| [Static assets on live](./static-assets.mdx) | Play creatives from your CDN without an ad server. |
+| [Google Ad Manager on live](./google-ad-manager.mdx) | Let Google Ad Manager fill your breaks. |
+| [Your ad server (VAST) on VOD](../vod/vast.mdx) | The same workflow for on-demand assets. |
diff --git a/ads/workflows/vod/google-ad-manager.mdx b/ads/workflows/vod/google-ad-manager.mdx
new file mode 100644
index 000000000000..c40abd5fb98c
--- /dev/null
+++ b/ads/workflows/vod/google-ad-manager.mdx
@@ -0,0 +1,105 @@
+---
+sidebar_position: 3
+sidebar_label: Google Ad Manager
+description: Let Google Ad Manager 360 fill the breaks of on-demand assets, server-guided or server-side.
+---
+
+# Google Ad Manager on VOD
+
+import AppNeeds from '../_shared/_gam-app.mdx';
+import SsaiSession from '../_shared/_gam-ssai-session.mdx';
+import Targeting from '../_shared/_gam-targeting.mdx';
+import Troubleshooting from '../_shared/_gam-troubleshooting.mdx';
+
+With Google Ad Manager 360, Google decides which ads fill the breaks of your on-demand assets. Google assembles the ads for each break into a pod, and the pod plays when the viewer reaches the break. Use this workflow when you sell your VOD inventory through Google Ad Manager 360, and to reach platforms that need server-side ad insertion.
+
+| | Google Ad Manager on VOD |
+| -------------- | -------------------------------------------------------------------------- |
+| Delivery mode | Server-guided (SGAI), server-side (SSAI), or both |
+| Ad decisioning | Google Ad Manager, per viewer session |
+| Break creation | Scheduled at positions in the asset, or detected from the ad markers in it |
+| Your app needs | The OptiView Ads SDK; on Web also Google's IMA DAI SDK |
+
+Contact your Dolby OptiView account team to enable Google Ad Manager on your VOD channels.
+
+## Choose a delivery mode
+
+| Delivery mode | How it works | Use it for |
+| ------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
+| Server-guided | Google decisions the pods; the SDK inserts them at the break positions of your untouched asset. | Web, mobile, and most connected TV apps. Required for the double box, L-shape, and overlay layouts. |
+| Server-side | Google stitches the ads for the asset's breaks into the on-demand stream, and the player plays the stitched asset. | Platforms where a client-side SDK is not an option, such as Roku and older connected TV devices. |
+
+One channel can serve both: the channel steers the [delivery mode per device](../index.mdx#hybrid-ad-insertion-sgai-and-ssai), and the SDK picks it up from the Break Manifest.
+
+## Before you start
+
+- The [Google Ad Manager prerequisites](../../integrations/google/index.mdx) — network code and service account — are in place for your organization.
+- A [VOD channel](./index.mdx#set-up-a-vod-channel), connected to Google. The examples use the `mediatime` timebase.
+- The OptiView Ads SDK in your app: [Web](../../player-integration/optiview-ads-sdk/web.mdx), [Android](../../player-integration/optiview-ads-sdk/android.mdx), [iOS](../../player-integration/optiview-ads-sdk/ios.mdx), or [React Native](../../player-integration/optiview-ads-sdk/react-native.mdx).
+
+## Schedule a break
+
+A Google break carries a [vendor asset](../../concepts/breaks.mdx#vendor-assets) with `vendor` set to `gam` and `vendorParameters.type` set to `pod`. Google performs the decisioning, so the asset needs no URI. On a `mediatime` channel, `start` is the position in the asset, in seconds:
+
+```bash
+curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels//breaks' \
+ -u "$ADS_API_KEY:$ADS_API_SECRET" \
+ -H 'Content-Type: application/json' \
+ -H 'X-Org-ID: ' \
+ -d '{
+ "start": 600,
+ "duration": 60,
+ "controls": { "snapback": true },
+ "variant": {
+ "format": "single",
+ "assets": [
+ {
+ "type": "vendor",
+ "vendor": "gam",
+ "vendorParameters": { "type": "pod" },
+ "assetParameters": {
+ "description_url": "https://www.example.com/movies/the-big-match.html",
+ "plcmt": "1"
+ }
+ }
+ ]
+ }
+ }'
+```
+
+### Detect the breaks
+
+Instead of scheduling every break, add the asset as an [origin](../../concepts/origins.mdx) and configure a [marker rule](../../concepts/marker-detection.mdx) whose [template](../../concepts/templates.mdx) holds the Google vendor asset. Every matching ad marker in the asset becomes a break at its position.
+
+## How the break plays
+
+- **Server-guided:** the SDK fetches the channel's [Break Manifest](../../concepts/break-manifest.mdx) once when the session starts, and inserts Google's pods at the break positions as the viewer reaches them.
+- **Server-side:** Google stitches the ads into the asset, and the player plays the stitched stream.
+- Ads are decisioned per viewer session rather than per break moment, because every viewer watches from their own position: there is no audience-wide peak at a break start.
+- With `snapback`, a viewer who seeks past a break is taken back to the break first. See [Seeking and snapback](./index.mdx#seeking-and-snapback).
+
+## What your app needs
+
+
+
+### Server-side sessions
+
+
+
+## Ad targeting parameters
+
+
+
+## Troubleshooting
+
+
+
+## Related resources
+
+| Resource | Description |
+| -------------------------------------------------------------------- | ------------------------------------------------------------------- |
+| [VOD](./index.mdx) | VOD channels: timebase, seeking, and delivery modes. |
+| [Google Ad Manager](../../integrations/google/index.mdx) | Organization-level prerequisites: network code and service account. |
+| [Ad targeting parameters](../../how-to-guides/ad-tag-parameters.mdx) | Passing ad tag parameters to Google. |
+| [Your ad server (VAST) on VOD](./vast.mdx) | Play VAST tags from your own ad server, without Google Ad Manager. |
+| [Google Ad Manager on live](../live/google-ad-manager.mdx) | The same workflow for live streams. |
diff --git a/ads/workflows/vod/index.mdx b/ads/workflows/vod/index.mdx
new file mode 100644
index 000000000000..95aac608da8a
--- /dev/null
+++ b/ads/workflows/vod/index.mdx
@@ -0,0 +1,111 @@
+---
+sidebar_position: 2
+sidebar_label: VOD
+description: Monetize video-on-demand assets with OptiView Ads.
+---
+
+# VOD
+
+A VOD channel monetizes an on-demand asset: a movie, an episode, a replay, or a clip. Every viewer watches the asset from its own position, so breaks are placed at positions in the asset rather than at moments in time, and the player receives all of them at once when playback starts.
+
+## Set up a VOD channel
+
+Create the channel with `streamType` set to `vod`, and choose a timebase:
+
+- **`mediatime`** — breaks start at a position in the asset, in seconds from its start. For example, `600` is 10 minutes into the asset. This is the recommended timebase for VOD, and it is only available on VOD channels.
+- **`wallclock`** — breaks start at a UTC timestamp, matched against the asset's `EXT-X-PROGRAM-DATE-TIME` timeline. Use it for replays of a live stream that keep their original program date-time. Unlike on a live channel, the timestamp may lie in the past.
+
+```bash
+curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels' \
+ -u "$ADS_API_KEY:$ADS_API_SECRET" \
+ -H 'Content-Type: application/json' \
+ -H 'X-Org-ID: ' \
+ -d '{
+ "name": "Movie: The Big Match",
+ "streamType": "vod",
+ "timebase": "mediatime"
+ }'
+```
+
+The `streamType` and the `timebase` are set when you create the channel and cannot be changed afterwards. Create one VOD channel per asset, or per group of assets that share the same break positions.
+
+## Schedule breaks
+
+On a VOD channel, breaks are known before playback starts. Create them in one of two ways, or combine them:
+
+- **Scheduled** — create each break with its position in the asset, as below.
+- **Detected** — add the asset as an [origin](../../concepts/origins.mdx), and OptiView Ads turns the ad markers in the asset, such as SCTE-35 signals, into breaks at their positions. See [Break detection](../../concepts/marker-detection.mdx).
+
+With the `mediatime` timebase, `start` is a number of seconds:
+
+```bash
+curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels//breaks' \
+ -u "$ADS_API_KEY:$ADS_API_SECRET" \
+ -H 'Content-Type: application/json' \
+ -H 'X-Org-ID: ' \
+ -d '{
+ "start": 600,
+ "duration": 30,
+ "controls": { "snapback": true },
+ "variant": {
+ "format": "single",
+ "assets": [
+ {
+ "type": "static",
+ "mediaType": "video",
+ "uri": "https://cdn.example.com/ads/spot.m3u8"
+ }
+ ]
+ }
+ }'
+```
+
+A typical VOD ad plan combines:
+
+- **Pre-roll** — a break triggered when playback starts, with an [event trigger](../../concepts/breaks.mdx#start) instead of a start position.
+- **Mid-rolls** — breaks at positions in the asset, such as `600` and `1800`.
+- **Post-roll** — a break triggered when playback ends, with an [end trigger](../../concepts/breaks.mdx#end).
+- **Pause ads** — shown while the viewer is paused, with a [pause trigger](../../concepts/breaks.mdx#pause).
+
+Breaks on a VOD channel cannot overlap.
+
+### Seeking and snapback
+
+VOD viewers seek freely, so decide per break what happens when they skip over it, with the break's [controls](../../concepts/breaks.mdx#controls):
+
+- With `snapback` enabled, a viewer who seeks past a break is taken back to the break first. After the break, playback continues at the position the viewer was seeking to.
+- Without `snapback`, the viewer can skip the break.
+
+A break plays at most once per session: seeking back over a break that has already played does not play it again.
+
+## How players receive the breaks
+
+A VOD Break Manifest lists all breaks of the channel, and carries no polling interval. The SDK fetches it once when the session starts, and schedules every break against the playback position of the asset. Changes you make to the breaks apply to the next playback session.
+
+Your app integrates the SDK in the same way as for live: [Web](../../player-integration/optiview-ads-sdk/web.mdx), [Android](../../player-integration/optiview-ads-sdk/android.mdx), [iOS](../../player-integration/optiview-ads-sdk/ios.mdx), or [React Native](../../player-integration/optiview-ads-sdk/react-native.mdx). Start a session with the Break Manifest URL of the VOD channel when you load the asset.
+
+## What is different from live
+
+The live-only features do not apply to VOD channels:
+
+- **No events.** [Events](../../concepts/events.mdx) group the breaks of a live event; a VOD asset has no live moment to prepare for.
+- **No `pts` timebase.** Use `mediatime` or `wallclock`.
+
+## Choose an ad source
+
+Each ad source has its own page with everything you need for VOD: the break setup, what your app needs, the supported layouts, and troubleshooting. VOD channels support both delivery modes of the [hybrid platform](../index.mdx#hybrid-ad-insertion-sgai-and-ssai):
+
+| Ad source | SGAI | SSAI |
+| -------------------------------------------- | ---- | ---- |
+| [Static assets](./static-assets.mdx) | Yes | No |
+| [Your ad server (VAST)](./vast.mdx) | Yes | No |
+| [Google Ad Manager](./google-ad-manager.mdx) | Yes | Yes |
+
+## Related resources
+
+| Resource | Description |
+| --------------------------------------------------- | ------------------------------------------------------- |
+| [Breaks](../../concepts/breaks.mdx) | Event triggers, controls, layouts, and the asset model. |
+| [Channels](../../concepts/channels.mdx) | Stream type and timebase. |
+| [Break Manifest](../../concepts/break-manifest.mdx) | How breaks are delivered to players. |
+| [Live](../live/index.mdx) | Monetize live streams instead. |
diff --git a/ads/workflows/vod/static-assets.mdx b/ads/workflows/vod/static-assets.mdx
new file mode 100644
index 000000000000..d602b7bb721c
--- /dev/null
+++ b/ads/workflows/vod/static-assets.mdx
@@ -0,0 +1,107 @@
+---
+sidebar_position: 1
+sidebar_label: Static assets
+description: Play ad creatives from your own CDN in on-demand assets, without an ad server.
+---
+
+# Static assets on VOD
+
+import Layouts from '../_shared/_static-assets-layouts.mdx';
+import Formats from '../_shared/_static-assets-formats.mdx';
+import Troubleshooting from '../_shared/_static-assets-troubleshooting.mdx';
+
+With static assets, each break points directly at its ad creatives: video or image files that you host on your own CDN. No ad server is involved — the SDK fetches the creative from the URL on the break and plays it when the viewer reaches the break. Use this workflow on movies, episodes, replays, and clips for direct-sold campaigns, sponsorships, promos, and branded overlays where you already know which creative to show.
+
+| | Static assets on VOD |
+| -------------- | -------------------------------------------------------------------------- |
+| Delivery mode | Server-guided (SGAI) |
+| Ad decisioning | None: the break names the creative |
+| Break creation | Scheduled at positions in the asset, or detected from the ad markers in it |
+| Your app needs | The OptiView Ads SDK for your platform; nothing else |
+
+## Before you start
+
+- A [VOD channel](./index.mdx#set-up-a-vod-channel). The examples use the `mediatime` timebase.
+- The OptiView Ads SDK in your app: [Web](../../player-integration/optiview-ads-sdk/web.mdx), [Android](../../player-integration/optiview-ads-sdk/android.mdx), [iOS](../../player-integration/optiview-ads-sdk/ios.mdx), or [React Native](../../player-integration/optiview-ads-sdk/react-native.mdx). Static assets need no ad server, no Google Ad Manager account, and no extra configuration in the app.
+- Your creatives on a CDN that the viewers' devices can reach.
+
+## Schedule a break
+
+A static asset sets `type` to `static` and `uri` to the URL of the creative. On a `mediatime` channel, `start` is the position in the asset, in seconds. This mid-roll plays 10 minutes in, and viewers cannot skip it by seeking:
+
+```bash
+curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels//breaks' \
+ -u "$ADS_API_KEY:$ADS_API_SECRET" \
+ -H 'Content-Type: application/json' \
+ -H 'X-Org-ID: ' \
+ -d '{
+ "start": 600,
+ "duration": 30,
+ "controls": { "snapback": true },
+ "variant": {
+ "format": "single",
+ "assets": [
+ {
+ "type": "static",
+ "mediaType": "video",
+ "mimeType": "application/x-mpegurl",
+ "uri": "https://cdn.example.com/ads/spot.m3u8",
+ "interaction": { "clickThrough": "https://www.example.com/offer" }
+ }
+ ]
+ }
+ }'
+```
+
+The `uri` can also be a list of URLs targeted per device type. See [Static assets](../../concepts/breaks.mdx#static-assets) and [Asset URI targeting](../../concepts/breaks.mdx#asset-uri-targeting) for the full asset model.
+
+### Pre-roll, post-roll, and pause ads
+
+Set `start` to a player event instead of a position:
+
+| Break | `start` |
+| --------- | --------------------------------------- |
+| Pre-roll | `{ "type": "event", "event": "start" }` |
+| Post-roll | `{ "type": "event", "event": "end" }` |
+| Pause ad | `{ "type": "event", "event": "pause" }` |
+
+A pause ad is typically an image in the `overlay` layout. See [Event based triggers](../../concepts/breaks.mdx#event-based-triggers).
+
+### Detect the breaks
+
+Instead of scheduling every break, add the asset as an [origin](../../concepts/origins.mdx) and configure a [marker rule](../../concepts/marker-detection.mdx) whose [template](../../concepts/templates.mdx) holds the static assets. Every matching ad marker in the asset, such as an SCTE-35 signal, becomes a break at its position.
+
+## How the break plays
+
+- The SDK fetches the channel's [Break Manifest](../../concepts/break-manifest.mdx) once when the session starts. It lists every break, and the SDK loads each creative shortly before its break.
+- With `snapback`, a viewer who seeks past a break is taken back to the break first; afterwards, playback continues where the viewer was seeking to. Without it, viewers can skip the break by seeking. See [Seeking and snapback](./index.mdx#seeking-and-snapback).
+- A break plays at most once per session.
+
+## Supported layouts
+
+
+
+## Creative formats
+
+
+
+## Tracking and reporting
+
+The SDK reports an OptiView Ads [impression](../../how-to-guides/ad-impressions.md) for every break it presents, and emits ad events — such as the start and end of each break and ad — that your app can listen to. See the API reference of your platform for the full list of events.
+
+Static assets carry no third-party tracking URLs. If an advertiser needs to count impressions in their own ad server, deliver the creative through a [VAST tag](./vast.mdx) instead: the VAST response carries the advertiser's tracking URLs, which are fired while the ad plays.
+
+## Troubleshooting
+
+
+
+## Related resources
+
+| Resource | Description |
+| --------------------------------------------------------------- | ------------------------------------------------------------- |
+| [VOD](./index.mdx) | VOD channels: timebase, seeking, and delivery modes. |
+| [Breaks](../../concepts/breaks.mdx) | Layouts, variants, controls, and the asset model. |
+| [Templates](../../concepts/templates.mdx) | Store a break with its static assets once, and reuse it. |
+| [Your ad server (VAST) on VOD](./vast.mdx) | Let your ad server decide which creative to play. |
+| [Static assets on live](../live/static-assets.mdx) | The same workflow for live streams. |
+| [Ad impression tracking](../../how-to-guides/ad-impressions.md) | How OptiView Ads counts the breaks presented to your viewers. |
diff --git a/ads/workflows/vod/vast.mdx b/ads/workflows/vod/vast.mdx
new file mode 100644
index 000000000000..12c07277598f
--- /dev/null
+++ b/ads/workflows/vod/vast.mdx
@@ -0,0 +1,114 @@
+---
+sidebar_position: 2
+sidebar_label: Your ad server (VAST)
+description: Let your own ad server, or a partner's, decide the ads in on-demand assets through VAST tags.
+---
+
+# Your ad server (VAST) on VOD
+
+import AppNeeds from '../_shared/_vast-app.mdx';
+import Layouts from '../_shared/_vast-layouts.mdx';
+import Targeting from '../_shared/_vast-targeting.mdx';
+import Tracking from '../_shared/_vast-tracking.mdx';
+import Troubleshooting from '../_shared/_vast-troubleshooting.mdx';
+
+With VAST, each break points at a VAST tag: a URL on your ad server, or an advertiser's, that returns a [VAST](https://iabtechlab.com/standards/vast/) XML document. The ad server decides which creative to play, and its response carries the media files and the tracking URLs. Use this workflow on movies, episodes, replays, and clips when you run your own ad server, or buy from partners that deliver their ads through VAST tags.
+
+| | Your ad server (VAST) on VOD |
+| -------------- | -------------------------------------------------------------------------- |
+| Delivery mode | Server-guided (SGAI) |
+| Ad decisioning | Your ad server, per viewer, when the SDK requests the tag |
+| Break creation | Scheduled at positions in the asset, or detected from the ad markers in it |
+| Your app needs | The OptiView Ads SDK; on Web also Google's client-side IMA SDK |
+
+## Before you start
+
+- A [VOD channel](./index.mdx#set-up-a-vod-channel). The examples use the `mediatime` timebase.
+- The OptiView Ads SDK in your app: [Web](../../player-integration/optiview-ads-sdk/web.mdx), [Android](../../player-integration/optiview-ads-sdk/android.mdx), [iOS](../../player-integration/optiview-ads-sdk/ios.mdx), or [React Native](../../player-integration/optiview-ads-sdk/react-native.mdx).
+- A VAST tag URL from your ad server, or an advertiser's.
+
+## What your app needs
+
+
+
+## Schedule a break
+
+A VAST asset sets `type` to `vast` and `uri` to the VAST tag URL. On a `mediatime` channel, `start` is the position in the asset, in seconds. This mid-roll plays 10 minutes in, and viewers cannot skip it by seeking:
+
+```bash
+curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels//breaks' \
+ -u "$ADS_API_KEY:$ADS_API_SECRET" \
+ -H 'Content-Type: application/json' \
+ -H 'X-Org-ID: ' \
+ -d '{
+ "start": 600,
+ "duration": 30,
+ "controls": { "snapback": true },
+ "variant": {
+ "format": "single",
+ "assets": [
+ {
+ "type": "vast",
+ "mediaType": "video",
+ "mimeType": "application/xml",
+ "uri": "https://adserver.example.com/vast?slot=midroll",
+ "assetParameters": {
+ "content_genre": "sports"
+ }
+ }
+ ]
+ }
+ }'
+```
+
+The `uri` can also be a list of tag URLs targeted per device type. See [VAST assets](../../concepts/breaks.mdx#vast-assets) and [Asset URI targeting](../../concepts/breaks.mdx#asset-uri-targeting) for the full asset model.
+
+### Pre-roll, post-roll, and pause ads
+
+Set `start` to a player event instead of a position:
+
+| Break | `start` |
+| --------- | --------------------------------------- |
+| Pre-roll | `{ "type": "event", "event": "start" }` |
+| Post-roll | `{ "type": "event", "event": "end" }` |
+| Pause ad | `{ "type": "event", "event": "pause" }` |
+
+See [Event based triggers](../../concepts/breaks.mdx#event-based-triggers).
+
+### Detect the breaks
+
+Instead of scheduling every break, add the asset as an [origin](../../concepts/origins.mdx) and configure a [marker rule](../../concepts/marker-detection.mdx) whose [template](../../concepts/templates.mdx) holds the VAST asset. Every matching ad marker in the asset becomes a break at its position.
+
+## How the break plays
+
+- The SDK fetches the channel's [Break Manifest](../../concepts/break-manifest.mdx) once when the session starts, and requests each VAST tag on the device shortly before the viewer reaches its break. Every viewer gets an individual ad decision.
+- Viewers reach the breaks at different moments, so the requests to your ad server are spread over time.
+- The break's `duration` is the maximum: use a VAST tag that returns the right ad length, because the SDK returns to your content when the duration is reached.
+- With `snapback`, a viewer who seeks past a break is taken back to the break first. See [Seeking and snapback](./index.mdx#seeking-and-snapback).
+
+## Supported layouts
+
+
+
+## Ad targeting parameters
+
+
+
+## Tracking and reporting
+
+
+
+## Troubleshooting
+
+
+
+## Related resources
+
+| Resource | Description |
+| -------------------------------------------------------------------- | ---------------------------------------------------- |
+| [VOD](./index.mdx) | VOD channels: timebase, seeking, and delivery modes. |
+| [Breaks](../../concepts/breaks.mdx) | Layouts, variants, controls, and the asset model. |
+| [Ad targeting parameters](../../how-to-guides/ad-tag-parameters.mdx) | Passing targeting parameters to your ad server. |
+| [Static assets on VOD](./static-assets.mdx) | Play creatives from your CDN without an ad server. |
+| [Google Ad Manager on VOD](./google-ad-manager.mdx) | Let Google Ad Manager fill your breaks. |
+| [Your ad server (VAST) on live](../live/vast.mdx) | The same workflow for live streams. |
diff --git a/sidebarsAds.ts b/sidebarsAds.ts
index 90892d2e98f7..0ad12873f04e 100644
--- a/sidebarsAds.ts
+++ b/sidebarsAds.ts
@@ -15,6 +15,32 @@ const sidebars: SidebarsConfig = {
link: { type: 'doc', id: 'getting-started/index' },
items: [{ type: 'autogenerated', dirName: 'getting-started' }],
},
+ {
+ type: 'category',
+ label: 'Ad workflows',
+ description: 'Live and VOD, SGAI and SSAI, with static assets, VAST, or Google Ad Manager.',
+ collapsed: false,
+ customProps: {
+ icon: '🔀',
+ },
+ link: { type: 'doc', id: 'workflows/index' },
+ items: [
+ {
+ type: 'category',
+ label: 'Live',
+ description: 'Monetize live streams and live events.',
+ link: { type: 'doc', id: 'workflows/live/index' },
+ items: ['workflows/live/static-assets', 'workflows/live/vast', 'workflows/live/google-ad-manager'],
+ },
+ {
+ type: 'category',
+ label: 'VOD',
+ description: 'Monetize on-demand assets.',
+ link: { type: 'doc', id: 'workflows/vod/index' },
+ items: ['workflows/vod/static-assets', 'workflows/vod/vast', 'workflows/vod/google-ad-manager'],
+ },
+ ],
+ },
{
type: 'category',
label: 'Player integration',