diff --git a/ads/assets/img/break-manifest/side-loading.svg b/ads/assets/img/break-manifest/side-loading.svg
new file mode 100644
index 000000000000..55c387f2b779
--- /dev/null
+++ b/ads/assets/img/break-manifest/side-loading.svg
@@ -0,0 +1,25 @@
+
diff --git a/ads/assets/img/breaks/break-lifecycle.svg b/ads/assets/img/breaks/break-lifecycle.svg
new file mode 100644
index 000000000000..3938b695240a
--- /dev/null
+++ b/ads/assets/img/breaks/break-lifecycle.svg
@@ -0,0 +1,27 @@
+
diff --git a/ads/assets/img/breaks/format-double.svg b/ads/assets/img/breaks/format-double.svg
new file mode 100644
index 000000000000..69df7a7e0231
--- /dev/null
+++ b/ads/assets/img/breaks/format-double.svg
@@ -0,0 +1,8 @@
+
diff --git a/ads/assets/img/breaks/format-lshape-ad.svg b/ads/assets/img/breaks/format-lshape-ad.svg
new file mode 100644
index 000000000000..4aa600cc9554
--- /dev/null
+++ b/ads/assets/img/breaks/format-lshape-ad.svg
@@ -0,0 +1,8 @@
+
diff --git a/ads/assets/img/breaks/format-lshape-content.svg b/ads/assets/img/breaks/format-lshape-content.svg
new file mode 100644
index 000000000000..f17df9c31c49
--- /dev/null
+++ b/ads/assets/img/breaks/format-lshape-content.svg
@@ -0,0 +1,8 @@
+
diff --git a/ads/assets/img/breaks/format-overlay.svg b/ads/assets/img/breaks/format-overlay.svg
new file mode 100644
index 000000000000..f275eb2b7779
--- /dev/null
+++ b/ads/assets/img/breaks/format-overlay.svg
@@ -0,0 +1,7 @@
+
diff --git a/ads/assets/img/breaks/format-single.svg b/ads/assets/img/breaks/format-single.svg
new file mode 100644
index 000000000000..9851f4b8426b
--- /dev/null
+++ b/ads/assets/img/breaks/format-single.svg
@@ -0,0 +1,5 @@
+
diff --git a/ads/concepts/break-manifest.mdx b/ads/concepts/break-manifest.mdx
new file mode 100644
index 000000000000..0437a4548680
--- /dev/null
+++ b/ads/concepts/break-manifest.mdx
@@ -0,0 +1,81 @@
+---
+sidebar_position: 7
+sidebar_label: Break Manifest
+---
+
+# Break Manifest
+
+The Break Manifest is the contract between the OptiView Ads backend and the player. It is a small JSON document that describes the ad breaks that are currently relevant for a [channel](/ads/concepts/channels), and the OptiView Player polls it to learn which breaks to prepare and play.
+
+## Side-loading
+
+The Break Manifest is **side-loaded**: it is served from its own endpoint, separately from the media manifest. The player fetches the media stream from your CDN as usual and, in parallel, polls the Break Manifest to drive ad break scheduling.
+
+
+
+Side-loading has some important advantages:
+
+- **Streaming protocol independent.** Because the Break Manifest travels next to the stream instead of inside it, features do not have to be ported into an existing streaming protocol to support your use cases. It also allows us to bring features that are not possible today due to the limitations of those protocols.
+- **Not in your critical path.** OptiView Ads never modifies your media manifest, so ad insertion cannot corrupt the stream and cause an outage the way an insertion platform writing wrong data into the media manifest can.
+- **Minimal requirements on the stream.** The only thing the stream needs is time metadata to schedule the breaks against.
+
+## Endpoint
+
+The Break Manifest is served per channel:
+
+```text
+GET /manifest/v1/:orgId/channels/:channelId
+```
+
+The endpoint is a public read endpoint: it takes no authentication and is served with permissive CORS so that players and CDNs can fetch it directly.
+
+```bash
+curl 'https://us.markers.optiview.dolby.com/manifest/v1/org_123/channels/1f7f3a5a-9c2e-4a56-b1d4-3f8a2c9d6e01'
+```
+
+:::note Regional domains
+The example uses the US region (`https://us.markers.optiview.dolby.com`). For the EU region, replace `us.` with `eu.` (`https://eu.markers.optiview.dolby.com`).
+:::
+
+Responses carry a `Cache-Control` header aligned with the channel's active polling interval, so a cached copy is never held longer than the fastest polling cadence the channel advertises.
+
+## Manifest envelope
+
+The Break Manifest document contains the following top-level properties:
+
+| Property | Description |
+| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `version` | The Break Manifest format version, following Semantic Versioning. Use it to guard against future format changes. |
+| `channelId` | The identifier of the [channel](/ads/concepts/channels) this manifest serves. Players use it for reporting, analytics, and diagnostics. |
+| `timebase` | How each break's `start` is expressed: `wallclock` (UTC ISO 8601 timestamp), `pts` (presentation timestamp), or `mediatime` (seconds from the start of a VOD asset). See [Channels](/ads/concepts/channels). |
+| `polling` | How often the player should refresh the manifest. See [Polling](#polling). |
+| `vendorConfiguration` | Session-level configuration per vendor integration — for example, the Google Ad Manager network code and custom asset key the player needs to create the stream session. |
+| `breaks` | The breaks currently relevant for the channel. Each entry carries the break's schedule, controls, and variants — see [Breaks](./breaks.mdx) for what a break contains. |
+
+Everything inside a break entry — `start`, `duration`, `resumeOffset`, `controls`, and `variant` — is described on the [Breaks](./breaks.mdx) page.
+
+### Polling
+
+The `polling` object advertises how often the player should refresh the manifest, with two cadences:
+
+- **`polling.idle`** — the interval to poll at when no break is active. A slower cadence keeps request load low while nothing is happening. Default: `10` seconds.
+- **`polling.active`** — the interval to poll at while a break is active. A faster cadence lets the player react quickly to duration changes, an early return, or late additions. Default: `1` second.
+
+Both cadences are configured on the channel through its `pollingIdleSeconds` and `pollingActiveSeconds` settings.
+
+### Which breaks are included
+
+The Break Manifest reflects the breaks that are currently relevant for delivery, not the channel's entire break history:
+
+- An upcoming break appears in the manifest ahead of its start, controlled by the channel's ad prefetch window (`adPrefetchMs`, default 10 seconds). This gives the player time to prepare the break before it starts.
+- A past break remains included while its window still overlaps the channel's DVR window (`dvrWindowMs`, default 5 minutes), so viewers seeking back still get the break. Breaks that ended before the DVR look-back are dropped.
+- Only fully prepared breaks are announced. Breaks that are still being prepared, [cued breaks](./breaks.mdx#cued-breaks) waiting to be punched, and failed breaks never appear — see the [break lifecycle](./breaks.mdx#break-lifecycle).
+
+## Related resources
+
+| Resource | Relationship |
+| ------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
+| [Getting started](/ads/getting-started/) | Integrating the OptiView Player that polls the Break Manifest. |
+| [Channels](./channels.mdx) | The parent of the Break Manifest. The timebase and delivery window shape which breaks are included. |
+| [Breaks](./breaks.mdx) | Scheduled or detected ad opportunities announced through the manifest. |
+| [Integrations](../integrations/google/index.mdx) | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. |
diff --git a/ads/concepts/breaks.mdx b/ads/concepts/breaks.mdx
new file mode 100644
index 000000000000..3b39cacd5db4
--- /dev/null
+++ b/ads/concepts/breaks.mdx
@@ -0,0 +1,466 @@
+---
+sidebar_position: 2
+sidebar_label: Breaks
+---
+
+# Breaks
+
+A break is the core monetization entity in OptiView Ads. It represents an ad opportunity scheduled on a channel and describes when the break starts, how long it lasts, what the viewer is allowed to do during the break, and which ad experience is rendered.
+
+Breaks are scoped to an organization and created for a [channel](./channels.mdx). API calls authenticate with an API key and secret using HTTP Basic authentication and identify the organization with the `X-Org-ID` header.
+
+:::note Regional domains
+The examples below use the US region (`https://us.ads.optiview.dolby.com`). For the EU region, replace `us.` with `eu.` (`https://eu.ads.optiview.dolby.com`).
+:::
+
+## Break identity
+
+Every break has an `id` that is unique within its channel. The `id` is optional when creating a break: if you omit it, OptiView Ads generates one for you. When you supply your own, we recommend using a UUID.
+
+## Scheduling
+
+### Timebase-based scheduling
+
+The [timebase of the channel](./channels.mdx#timebase) defines how a break is scheduled. Every break follows the timebase of its channel, and the break's `start` is expressed on that timeline:
+
+- **`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.
+
+`duration` is always required and is expressed in seconds. It is the **maximum** duration of the break.
+
+### Cued breaks
+
+On a wallclock channel you can create a break without a `start`. Such a break is **cued**: it is fully prepared ahead of time, but it is not announced to players yet — it waits for you to assign its start at exactly the right moment. This is ideal for live productions where you know a break is coming but not exactly when.
+
+See [Break punching](#break-punching) for how to fire a cued break.
+
+### Create a break directly
+
+You can create a break manually by supplying the necessary fields: the `start` (when required by the timebase), the `duration`, and the `variant` describing the ad experience. See [Break configuration](#break-configuration) for all the options.
+
+```bash
+curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/1f7f3a5a-9c2e-4a56-b1d4-3f8a2c9d6e01/breaks' \
+ -u "$ADS_API_KEY:$ADS_API_SECRET" \
+ -H 'Content-Type: application/json' \
+ -H 'X-Org-ID: org_123' \
+ -d '{
+ "id": "8c3f6a2e-5b1d-4e7a-9c48-2d6f0b1a3e57",
+ "start": "2026-07-16T12:15:00.000Z",
+ "duration": 120,
+ "resumeOffset": 0,
+ "controls": {
+ "skipOffset": 30,
+ "snapback": true
+ },
+ "variant": {
+ "format": "single",
+ "assets": [
+ {
+ "id": "asset-001",
+ "type": "static",
+ "mediaType": "video",
+ "mimeType": "video/mp4",
+ "uri": "https://cdn.example.com/ads/asset-001.m3u8"
+ }
+ ]
+ }
+ }'
+```
+
+### Create from a template
+
+A [template](./templates.mdx) preconfigures a break: it stores the break payload once so you can schedule consistent breaks quickly. Reference the template with `templateId` when creating the break.
+
+The created break stores a **snapshot** of the template: the template's content is copied onto the break at creation time. Editing the template later does not change breaks that were already created from it.
+
+You can override parts of the template per break. Supported overrides are `id`, `eventId`, `start`, `duration`, and `variant`:
+
+```bash
+curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/1f7f3a5a-9c2e-4a56-b1d4-3f8a2c9d6e01/breaks' \
+ -u "$ADS_API_KEY:$ADS_API_SECRET" \
+ -H 'Content-Type: application/json' \
+ -H 'X-Org-ID: org_123' \
+ -d '{
+ "templateId": "template-sports-spot",
+ "id": "9e4b7c1d-2a8f-4d35-b6e9-7c0a5f2d8b14",
+ "start": "2026-07-16T12:20:00.000Z",
+ "duration": 60
+ }'
+```
+
+For templates that contain [vendor assets](#vendor-assets), you can additionally pass `assetParameters` (a string map) with the request. These are merged into the vendor assets of the template snapshot — for example to set per-break ad targeting parameters; on duplicate keys, the values you provide win.
+
+### Prepare your breaks under an event
+
+An [event](./events.mdx) is an event on your channel for which you want to prepare breaks, such as a live game or a show. Attach a break to an event by setting `eventId` when creating it.
+
+Preparing breaks under an event is only supported on **wallclock** channels. The event's date window defines the limits within which its breaks can be scheduled:
+
+- The break's start must fall within the event window (`startDate` through `endDate`).
+- The entire break must fit inside the window: the break's end (`start` + `duration`) must not pass the event's `endDate`.
+
+A [cued break](#cued-breaks) is not checked against the window at creation, because its start is not known yet; punch it while the event is in progress so its start falls inside the window.
+
+### Scheduling constraints
+
+OptiView Ads enforces a few constraints when scheduling breaks. Each one protects the viewer experience:
+
+- **Breaks cannot overlap.** A scheduled break cannot overlap another break on the same channel. Players render one break at a time; overlapping breaks would make the ad timeline ambiguous for your viewers.
+- **The start cannot lie in the past.** A break must start ahead of the current live position of the channel. Players need to receive the break through the [Break Manifest](./break-manifest.mdx) before its start time; a break scheduled behind the live position would never be seen.
+- **Ad decisioning needs lead time.** Breaks delivered through an ad server integration such as [Google Ad Manager](../integrations/google/index.mdx) need a small extra margin before their start, so the ad decisioning can complete before the break begins. Scheduling closer than that margin is rejected rather than risking an empty break.
+- **Event boundaries are respected.** A break attached to an event must fit entirely inside the event's date window, so all of an event's breaks stay within the occurrence they belong to.
+- **One cued break at a time.** A channel holds at most one [cued break](#cued-breaks). The cued break is "the next break to fire" — allowing several at once would make it ambiguous which break a punch applies to.
+
+## Break Lifecycle
+
+A break moves through a small set of states:
+
+- **`PREPARING`** — the break is being prepared with the ad vendor (for example, waiting for Google Ad Manager to decision the ad pod). Breaks that use a vendor asset always begin in this state.
+- **`CUED`** — the break is prepared but has no start time yet. It waits for you to [punch](#break-punching) it. A break created without a `start` enters this state (after preparation completes, or immediately when no vendor preparation is needed).
+- **`READY`** — the break has a start time and is ready to be announced to players. A break created with a `start` and no vendor preparation begins in this state.
+- **`SIGNALED`** — the break has been announced to players through the [Break Manifest](./break-manifest.mdx) or through [SSAI cue injection](../integrations/google/dai.mdx).
+- **`ERROR`** — the break could not be delivered, for example because it passed its scheduling window before preparation completed.
+
+
+
+At a high level, the transitions are:
+
+| Transition | What happens |
+| ------------------- | ----------------------------------------------------------------------------------------- |
+| `PREPARING → READY` | Preparation completed and the break has a start time; it can now be announced to players. |
+| `PREPARING → CUED` | Preparation completed for a break without a start time; it now waits to be punched. |
+| `PREPARING → ERROR` | The break missed its scheduling window before preparation could complete. |
+| `CUED → READY` | You punched the break: its start time is set and it becomes eligible for delivery. |
+| `READY → SIGNALED` | The break was announced to players. |
+
+## Break punching
+
+Break punching lets you prepare a break ahead of time without yet signaling it to any player. You create the break without a `start`, OptiView Ads prepares everything (including any ad vendor decisioning), and the break waits in the `CUED` state. When the moment arrives — for example, the referee blows the half-time whistle — you **punch** the break: its start time is assigned and it is announced to players right away.
+
+While a break is being prepared or is waiting in the cued state, no other break can be cued on that channel: the cued break is waiting for you to punch it first. Punch (or delete) it before cueing the next one.
+
+Punching is only available on wallclock channels.
+
+```bash
+curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/1f7f3a5a-9c2e-4a56-b1d4-3f8a2c9d6e01/breaks/8c3f6a2e-5b1d-4e7a-9c48-2d6f0b1a3e57/punch' \
+ -u "$ADS_API_KEY:$ADS_API_SECRET" \
+ -H 'Content-Type: application/json' \
+ -H 'X-Org-ID: org_123' \
+ -d '{
+ "start": "2026-07-16T12:25:00.000Z"
+ }'
+```
+
+The body is optional: if you omit it, the break starts now. A requested start in the past is clamped to now.
+
+## Break configuration
+
+This section describes the different possibilities of a break: its general properties, event based triggers, playback controls, layouts, variants, and the asset model.
+
+### General
+
+Every break carries these general properties:
+
+| Property | Description |
+| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `start` | When the break starts: a position on the channel's timebase (see [Timebase-based scheduling](#timebase-based-scheduling)), or a player event (see [Event based triggers](#event-based-triggers)). |
+| `duration` | The **maximum** duration of the break, in seconds. When the ad content is longer, the player returns to the content when the duration is reached; when it is shorter, the player returns early. |
+| `resumeOffset` | Where the player resumes the content after the break, in seconds relative to the break's start. `0` resumes at the point where the break started; when omitted, playback resumes after the break's duration. |
+| `controls` | What the viewer is allowed to do during the break — see [Controls](#controls). |
+| `variant` | The ad experience(s) to render: the layout and its assets, optionally targeted per device — see [Layouts](#layouts) and [Variants](#variants). |
+
+### Event based triggers
+
+Instead of a position on the timeline, a break's `start` can be a player event. Event-triggered breaks are described in the Break Manifest with `start: { "type": "event", "event": "", "delay": }`.
+
+The optional `delay` property postpones the break: it is the number of seconds (≥ 0, default `0`) that must elapse after the event before the break starts. How the delay counts depends on the event, as described below.
+
+#### Start
+
+A `start` break is a **pre-roll**: it fires when content playback begins, once per session. Use it to show an ad before (or shortly after) the viewer starts watching. With a `delay`, the break fires after the viewer has actually watched that many seconds of content — the delay counts played media time only, so pausing or seeking does not advance it.
+
+#### Pause
+
+A `pause` break is a **pause ad**: it fires every time the viewer pauses playback and is dismissed when the viewer resumes. Use it to monetize the pause screen, typically with an image overlay. With a `delay`, the break appears after the viewer has been paused for that many seconds; resuming before the delay elapses cancels the pending break. When multiple `pause` breaks are defined, each pause shows the next one in order.
+
+#### End
+
+An `end` break is a **post-roll**: it fires when playback has ended, once per session. Use it to show an ad after the content finishes. With a `delay`, the break fires that many seconds after playback ended; replaying before the delay elapses cancels the pending break.
+
+### Controls
+
+The `controls` object determines what a viewer is allowed to do during a break:
+
+- **`skipOffset`** — makes the break skippable. The value is the number of seconds into the break after which the viewer can skip it (for example, `skipOffset: 5` makes the break skippable after 5 seconds). When omitted, the break is not skippable.
+- **`snapback`** — controls what happens when a viewer seeks over a break. When enabled, a viewer who tries to seek past the break is brought back to the start of the break; after the break finishes, playback continues at the position the viewer wanted to seek to. When omitted, viewers can seek over the break freely.
+
+### Layouts
+
+The layout (`format`) of a variant determines how the ad and your content share the screen. Click a layout to jump to its section:
+
+| [](#single) | [](#double-box) | [](#l-shape-ad) |
+| :----------------------------------------------------------: | :------------------------------------------------------------------: | :---------------------------------------------------------------------: |
+| [Single](#single) | [Double Box](#double-box) | [L-shape ad](#l-shape-ad) |
+
+| [](#l-shape-content) | [](#overlay) |
+| :------------------------------------------------------------------------------------: | :-------------------------------------------------------------: |
+| [L-shape content](#l-shape-content) | [Overlay](#overlay) |
+
+#### Single
+
+
+
+`format: "single"` is the most basic layout: the ad covers the whole video area, replacing the content for the duration of the break. It requires only an `assets` array; companion assets are not allowed.
+
+```json
+{
+ "format": "single",
+ "assets": [
+ {
+ "type": "static",
+ "mediaType": "video",
+ "uri": "https://cdn.example.com/ads/fullscreen.m3u8"
+ }
+ ]
+}
+```
+
+#### Double Box
+
+
+
+`format: "double"` squeezes the content back into its own box alongside a second box that plays the ad. The background behind both boxes is filled by a **companion** asset, which can be an image or a video — every entry in `assets` therefore requires a `companion`.
+
+```json
+{
+ "format": "double",
+ "assets": [
+ {
+ "type": "static",
+ "mediaType": "video",
+ "uri": "https://cdn.example.com/ads/spot.m3u8",
+ "companion": {
+ "type": "static",
+ "mediaType": "image",
+ "uri": "https://cdn.example.com/ads/backdrop.png"
+ }
+ }
+ ]
+}
+```
+
+#### L-shape ad
+
+
+
+`format: "lshape_ad"` squeezes the content into a corner and **replaces it with an ad**: the ad plays in the main window while a **companion** asset (image or video) fills the L-shaped backdrop. Like Double Box, every entry in `assets` requires a `companion`.
+
+```json
+{
+ "format": "lshape_ad",
+ "assets": [
+ {
+ "type": "static",
+ "mediaType": "video",
+ "uri": "https://cdn.example.com/ads/spot.m3u8",
+ "companion": {
+ "type": "static",
+ "mediaType": "image",
+ "uri": "https://cdn.example.com/ads/lshape-backdrop.png"
+ }
+ }
+ ]
+}
+```
+
+#### L-shape content
+
+
+
+`format: "lshape_content"` is almost identical to [L-shape ad](#l-shape-ad), except that **your content keeps playing** in the main window instead of being replaced by an ad. The L-shaped backdrop itself is the advertisement, so it is a plain asset — no `companion` is needed.
+
+This is the key difference between the two L-shapes: with `lshape_ad` the viewer watches an ad while a companion fills the backdrop; with `lshape_content` the viewer keeps watching your content while the backdrop is the ad.
+
+```json
+{
+ "format": "lshape_content",
+ "assets": [
+ {
+ "type": "static",
+ "mediaType": "image",
+ "uri": "https://cdn.example.com/ads/lshape-ad.png"
+ }
+ ]
+}
+```
+
+#### Overlay
+
+
+
+`format: "overlay"` renders a non-linear ad on top of the content, which keeps playing. In addition to `assets`, an overlay requires a `position` and a `size`, and accepts an optional `opacity`. All values are fractions of the player surface from `0` through `1`:
+
+- `position` — at least one of `top`/`bottom` and one of `left`/`right`.
+- `size` — the `width` and `height` of the overlay.
+- `opacity` — the transparency of the overlay.
+
+```json
+{
+ "format": "overlay",
+ "assets": [
+ {
+ "type": "static",
+ "mediaType": "image",
+ "uri": "https://cdn.example.com/ads/overlay.png"
+ }
+ ],
+ "position": { "top": 0.05, "right": 0.05 },
+ "size": { "width": 0.3, "height": 0.2 },
+ "opacity": 0.9
+}
+```
+
+### Variants
+
+Variants let one break target different devices with different experiences. Set `variant` to a list, and give each variant an optional `targeting.deviceType` (`desktop`, `tablet`, `mobile`, or `tv`):
+
+```json
+{
+ "duration": 30,
+ "variant": [
+ {
+ "format": "double",
+ "targeting": { "deviceType": "desktop" },
+ "assets": [
+ {
+ "type": "static",
+ "mediaType": "video",
+ "uri": "https://cdn.example.com/ads/desktop.m3u8",
+ "companion": {
+ "type": "static",
+ "mediaType": "image",
+ "uri": "https://cdn.example.com/ads/desktop-companion.jpg"
+ }
+ }
+ ]
+ },
+ {
+ "format": "single",
+ "targeting": { "deviceType": "mobile" },
+ "assets": [
+ {
+ "type": "static",
+ "mediaType": "video",
+ "uri": "https://cdn.example.com/ads/mobile.m3u8"
+ }
+ ]
+ },
+ {
+ "format": "single",
+ "assets": [
+ {
+ "type": "static",
+ "mediaType": "video",
+ "uri": "https://cdn.example.com/ads/default.m3u8"
+ }
+ ]
+ }
+ ]
+}
+```
+
+The player picks the variant to render:
+
+- A variant **without** `targeting` is the **default**: it matches any device.
+- The **order of the variants defines which one is chosen**. The player walks the list in order and picks the first variant it matches and supports. This also applies when multiple variants target the same device, or when multiple defaults exist — the earlier one wins if the player can render it, otherwise the player falls through to the next.
+
+In the example above, a desktop viewer gets the Double Box, a mobile viewer gets the mobile single, and every other device falls back to the default single.
+
+### Asset model
+
+An asset describes one piece of ad media inside a variant. Every asset shares these base properties:
+
+| Property | Description |
+| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
+| `id` | Identifier of the asset, unique within the break. Generated when omitted. |
+| `type` | How the asset is retrieved: [`static`](#static-assets), [`vast`](#vast-assets), or [`vendor`](#vendor-assets). |
+| `mediaType` | Whether the asset is a `video` or an `image`. |
+| `mimeType` | Optional MIME type of the media, so players can fail fast when they cannot display it. |
+| `duration` | Optional maximum duration of the asset, in seconds. Required when a break plays multiple assets. The break `duration` takes priority. |
+| `interaction.clickThrough` | Optional URL to open when the viewer clicks or taps the asset. |
+
+#### Static assets
+
+A `static` asset is a media resource the player retrieves directly — a video or image URL served from your CDN, with no additional ad-serving logic:
+
+```json
+{
+ "type": "static",
+ "mediaType": "video",
+ "mimeType": "application/x-mpegurl",
+ "uri": "https://cdn.example.com/ads/spot.m3u8"
+}
+```
+
+#### 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`:
+
+```json
+{
+ "type": "vast",
+ "mediaType": "video",
+ "mimeType": "application/xml",
+ "uri": "https://adserver.example.com/vast/creative-001.xml"
+}
+```
+
+#### 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:
+
+```json
+{
+ "type": "vendor",
+ "vendor": "gam",
+ "mediaType": "video",
+ "vendorParameters": {
+ "type": "pod"
+ },
+ "assetParameters": {
+ "airingId": "airing-001"
+ }
+}
+```
+
+`vendorParameters` carries the parameters required to retrieve the asset from the vendor, and the optional `assetParameters` carry ad targeting parameters forwarded to the vendor. See [Google Pod Serving](../integrations/google/pod-serving.mdx) for the supported values.
+
+#### Asset URI targeting
+
+For `static` and `vast` assets, the `uri` can also be a list of `{ value, targeting }` entries, so one asset can point to different resources per device:
+
+```json
+{
+ "type": "static",
+ "mediaType": "image",
+ "uri": [
+ { "value": "https://cdn.example.com/ads/overlay-tv.png", "targeting": { "deviceType": "tv" } },
+ { "value": "https://cdn.example.com/ads/overlay-mobile.png", "targeting": { "deviceType": "mobile" } },
+ { "value": "https://cdn.example.com/ads/overlay-default.png" }
+ ]
+}
+```
+
+The selection rules match [Variants](#variants): an entry without `targeting` is the default, and when several entries match, the order in the list decides which one is used.
+
+## Related resources
+
+| Resource | Relationship |
+| ------------------------------------------------ | -------------------------------------------------------------------------------------- |
+| [Channels](./channels.mdx) | The parent of a break. The channel's timebase defines how breaks are scheduled. |
+| [Templates](./templates.mdx) | Reusable break presets that can be scheduled on the channel. |
+| [Events](./events.mdx) | An event on your channel for which you want to prepare breaks. |
+| [Origins](./origins.mdx) | Manifest URLs monitored for ad markers. A channel can have multiple origins. |
+| [Break detection](./marker-detection.mdx) | Configure marker rules to turn ad markers in your stream into breaks. |
+| [Break Manifest](./break-manifest.mdx) | The manifest that announces the channel's breaks to players. |
+| [Integrations](../integrations/google/index.mdx) | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. |
diff --git a/ads/concepts/channels.mdx b/ads/concepts/channels.mdx
new file mode 100644
index 000000000000..bef0e3796a34
--- /dev/null
+++ b/ads/concepts/channels.mdx
@@ -0,0 +1,80 @@
+---
+sidebar_position: 1
+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.
+
+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.
+
+Channels are scoped to an organization. In the OptiView Unified Dashboard, open **Ads → Channels** to create and manage them.
+
+In the Dashboard, opening a channel gives you access to each of these tabs:
+
+| Tab | Use it for |
+| ------------------------------------------------ | --------------------------------------------------------------------- |
+| Overview | View channel settings and Break Manifest configuration. |
+| [Breaks](./breaks.mdx) | Schedule, inspect, and delete breaks for the channel. |
+| [Events](./events.mdx) | Prepare breaks for an event on your live stream. |
+| [Origins](./origins.mdx) | Add, enable, disable, and prioritize manifest origins. |
+| [Break Detection](./marker-detection.mdx) | Configure marker rules to turn ad markers in your stream into breaks. |
+| [Integrations](../integrations/google/index.mdx) | Manage channel-level delivery integrations. |
+
+## Channel identity
+
+Every channel has a customer-facing `id`. We recommend using a UUID as the channel `id`, and the `name` property as a human-readable name. If you omit `id` on creation, one is generated for you.
+
+## Timing and delivery settings
+
+These channel settings determine how break start times are interpreted and how breaks are delivered to players through the [Break Manifest](./break-manifest.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.
+
+Choose the timebase when creating the channel; all breaks on the channel use the same timebase.
+
+### DVR window
+
+_Default: `300000` ms (5 minutes)_
+
+`dvrWindowMs` describes how far behind live a viewer can be while watching the channel — the time-shifted (DVR) window of the stream.
+
+It directly impacts the Break Manifest: a break is included in the manifest as long as it is still relevant for a viewer anywhere inside the DVR window. With a larger DVR window, breaks remain in the manifest for longer so that time-shifted viewers still receive them; with a small window, only breaks near the live edge are returned to the player.
+
+### Live offset
+
+_Default: `0` ms_
+
+`liveOffsetMs` describes how far the player's playhead is behind live. Players never play exactly at the live edge — they buffer a few seconds behind it.
+
+OptiView Ads uses this offset mainly to allow you to schedule a break in the past: because viewers watch behind the live edge, a break aimed at the viewer's current playhead position lies slightly behind raw server time. With the offset configured, break start times are evaluated against the position viewers are actually watching instead of raw server time, so such a break is accepted and activates when the playhead reaches it.
+
+### Ad prefetch window
+
+_Default: `10000` ms (10 seconds)_
+
+`adPrefetchMs` defines how far ahead of a break's start time the break is announced to the player through the Break Manifest.
+
+With the default of `10000` (10 seconds), a break whose start time is within the next 10 seconds is included in the manifest. This lead time gives players room to prepare and prefetch the ad content before the break actually starts.
+
+For creating, updating, listing, and deleting channels programmatically, see the Ads API reference.
+
+## Related resources
+
+A channel is the parent or lookup point for the rest of the OptiView Ads model:
+
+| Resource | Relationship |
+| ------------------------------------------------ | -------------------------------------------------------------------------------------- |
+| [Breaks](./breaks.mdx) | Scheduled or detected ad opportunities for the channel. |
+| [Templates](./templates.mdx) | Reusable break presets that can be scheduled on the channel. |
+| [Events](./events.mdx) | An event on your channel for which you want to prepare breaks. |
+| [Origins](./origins.mdx) | Manifest URLs monitored for ad markers. A channel can have multiple origins. |
+| [Break detection](./marker-detection.mdx) | Configure marker rules to turn ad markers in your stream into breaks. |
+| [Break Manifest](./break-manifest.mdx) | The manifest that announces the channel's breaks to players. |
+| [Integrations](../integrations/google/index.mdx) | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. |
diff --git a/ads/concepts/events.mdx b/ads/concepts/events.mdx
new file mode 100644
index 000000000000..e64b998cedfd
--- /dev/null
+++ b/ads/concepts/events.mdx
@@ -0,0 +1,60 @@
+---
+sidebar_position: 4
+sidebar_label: Events
+---
+
+# Events
+
+An event is a channel-scoped time window that groups the ad breaks belonging to one scheduled occurrence, such as a live game, a show, or a tournament. It gives you a single handle for the breaks around that occurrence: the breaks share the event's window, and deleting the event removes them together.
+
+Events also anchor the [break punching](./breaks.mdx#break-punching) workflow: ahead of a live occurrence you prepare breaks under the event without a start time, and during the broadcast you fire them at the exact moment.
+
+## Event identity
+
+Every event has an `id` that is unique within its channel. The `id` is optional when creating an event: if you omit it, OptiView Ads generates one for you. When you supply your own, we recommend using a UUID. For a human-readable label, use the `name` property instead — it is shown in the dashboard.
+
+## Time window
+
+An event is defined by a `startDate` and an `endDate`, both UTC ISO 8601 timestamps. `startDate` must be before `endDate`.
+
+Preparing breaks under an event is supported on **wallclock** channels. The window is enforced on the breaks scheduled under the event: the **entire** break must fit inside the window.
+
+- The break start must be at or after the event `startDate`.
+- The break end (`start` + `duration`) must be at or before the event `endDate`.
+
+A break created **without** a start time (a [cued break](./breaks.mdx#cued-breaks)) is not range-checked at creation, because its start is not known yet. Its start is set when you punch it.
+
+## Breaks under an event
+
+A break is attached to an event by setting `eventId` to the event's `id` on the break. `eventId` is optional: a break can exist on the channel without belonging to any event.
+
+:::warning Deleting an event deletes its breaks
+Deleting an event also removes every break attached to it. Delete an event only after confirming that none of its breaks are still needed.
+:::
+
+## Templates for an event
+
+[Templates](./templates.mdx) can be linked to one or more events through their `eventIds`, so a reusable break preset can be surfaced for quick scheduling under those events. Instead of creating your breaks before the event, prepare templates ahead of time and schedule breaks from them during the event.
+
+## Break punching during an event
+
+For a live occurrence you usually do not know the exact break times in advance, but you want the break fully prepared so it can fire instantly. This is what [break punching](./breaks.mdx#break-punching) is for:
+
+1. Create the event with a window that covers the occurrence, for example kickoff through the final whistle.
+2. Ahead of the occurrence, create the [templates](./templates.mdx) describing the breaks you want to run.
+3. During the event, create a cued break from a template under the event, without a `start`. OptiView Ads prepares it (including any ad decisioning) and it waits in the `CUED` state.
+4. Punch the break whenever it needs to go — for example, at half-time. Its start is set and it is announced to players right away.
+5. Repeat for the next break: cue it from a template, then punch it at the right moment.
+
+A channel holds only one cued break at a time, so punch the current cued break before cueing the next one. Do not delete a cued break unless you have cued the wrong one.
+
+Because a punch uses the current time as the break's start, punch a cued break only while the event is in progress: this keeps the break inside the event's window.
+
+## Related resources
+
+| Resource | Relationship |
+| ------------------------------------------------ | -------------------------------------------------------------------------------------- |
+| [Channels](./channels.mdx) | The parent of an event. An event always belongs to one channel. |
+| [Breaks](./breaks.mdx) | Prepared under an event via `eventId` and scheduled within the event's window. |
+| [Templates](./templates.mdx) | Reusable break presets that can be linked to events for quick scheduling. |
+| [Integrations](../integrations/google/index.mdx) | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. |
diff --git a/ads/concepts/marker-detection.mdx b/ads/concepts/marker-detection.mdx
new file mode 100644
index 000000000000..69e1e2cb8b62
--- /dev/null
+++ b/ads/concepts/marker-detection.mdx
@@ -0,0 +1,82 @@
+---
+sidebar_position: 6
+sidebar_label: Break Detection
+---
+
+# Break Detection
+
+A channel can automatically detect ad markers (such as SCTE-35 cues or `EXT-X-DATERANGE` tags) in its origin manifests and turn them into breaks by applying marker rules. Detection is enabled or disabled per channel. Automatic detection currently supports HLS manifests only. See [Origins](./origins.mdx) for origin selection, priority ordering, and first-online behavior.
+
+## How detection works
+
+When detection is enabled, OptiView Ads polls the channel's enabled [origins](./origins.mdx) in priority order and selects the first online origin. It parses the manifest's markers, evaluates the enabled marker rules, creates breaks for matching markers, and records the result in [detection history](#detection-history).
+
+## Supported markers
+
+Detection recognizes two marker kinds in HLS manifests:
+
+| Marker rule type | HLS marker | Detection behavior |
+| ---------------- | ------------------ | ------------------------------------------------------------------------------------------------- |
+| `CUE` | `#EXT-X-CUE-OUT` | Parses a marker start and optional duration. |
+| `DATERANGE` | `#EXT-X-DATERANGE` | Requires a valid `START-DATE`. Duration comes from `DURATION`, `PLANNED-DURATION`, or `END-DATE`. |
+
+`DATERANGE` is not limited to Apple interstitials. Any `#EXT-X-DATERANGE` tag with a valid start is considered and can be matched by its attributes.
+
+## Marker rules
+
+A marker rule turns a detected marker into a break created from a [template](./templates.mdx). The rule's `type` must match the marker kind, and every configured condition must match the marker attributes. Attribute keys are compared case-insensitively.
+
+Every marker rule has an `id` that is unique within its channel. The `id` is optional when creating a rule: if you omit it, OptiView Ads generates one for you. When you supply your own, we recommend using a UUID.
+
+| Property | Description |
+| ------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
+| `type` | The marker kind this rule matches: `CUE` or `DATERANGE`. |
+| `conditions` | Attribute key/value pairs that must all match on the marker for the rule to fire. An empty object matches any marker of that type. |
+| `templateId` | The [template](./templates.mdx) to schedule the break from when the rule matches. |
+| `enabled` | Whether the rule participates in detection. |
+
+For example, this rule matches `DATERANGE` markers whose `CLASS` attribute is `com.example.ad`:
+
+```json
+{
+ "streamType": "HLS",
+ "type": "DATERANGE",
+ "conditions": { "CLASS": "com.example.ad" },
+ "templateId": "3b8e5f0a-7c2d-4e91-a6b3-9d4f1c8e2a70",
+ "enabled": true
+}
+```
+
+## Detection history
+
+Detection history is the audit trail of what automatic detection decided for each marker. Once an origin is enabled, every marker found in the stream is recorded — even when no marker rule exists yet. This lets you review the markers appearing in your stream and identify which ones you want to turn into breaks before configuring your rules.
+
+Every processed marker is recorded with one of three outcomes:
+
+| Action | Meaning |
+| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
+| `CREATED` | A rule matched and a break was scheduled. The record links to the matched rule and the created break. |
+| `SKIPPED` | No fault: the marker was ineligible (unparseable or without a resolvable start), no rule matched, or a scheduling condition prevented creation. |
+| `FAILED` | An eligible, rule-matched marker could not be scheduled for an unexpected reason. |
+
+Each record includes the raw manifest tag line, the origin that supplied the marker, and a `reason` explaining skips and failures. History is deduplicated per channel: repeated polling of the same marker, including seeing it on another origin, does not create duplicate records.
+
+## Troubleshooting
+
+| Symptom | Checks |
+| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| No breaks are created. | Is detection enabled on the channel? Is there at least one enabled HLS origin? Is the origin reachable and returning a parseable manifest? Is there an enabled marker rule whose `type` and `conditions` match the marker? Does the rule's template exist? |
+| History contains `SKIPPED` with `NO_RULES_CONFIGURED`. | An enabled origin is already being polled and markers are being detected, but the channel has no marker rules yet. Use these records to identify the markers you want to act on, then create and enable a matching marker rule. |
+| History contains `SKIPPED` with `NO_RULE_MATCHED`. | Check the rule `type` and all `conditions` against the marker attributes. Attribute keys are matched case-insensitively, but values must match. |
+| DASH or HESP origin is not producing breaks. | DASH and HESP origins are accepted but skipped by automatic detection. Use an enabled HLS origin. |
+| History contains `SKIPPED` with a scheduling reason. | The marker was recognized, but the break was not scheduled — for example because its start would lie in the past or it would overlap another break. See [Scheduling constraints](./breaks.mdx#scheduling-constraints). |
+| History contains `FAILED`. | The rule matched, but an unexpected scheduling or configuration error prevented break creation. Inspect the `reason` and verify the template and break configuration. |
+
+## Related resources
+
+| Resource | Relationship |
+| ---------------------------- | ---------------------------------------------------------------------------- |
+| [Channels](./channels.mdx) | Detection is enabled or disabled per channel. |
+| [Origins](./origins.mdx) | Manifest URLs monitored for ad markers. A channel can have multiple origins. |
+| [Templates](./templates.mdx) | Reusable break presets that marker rules schedule when a marker matches. |
+| [Breaks](./breaks.mdx) | The breaks created when detection matches a marker against a marker rule. |
diff --git a/ads/concepts/origins.mdx b/ads/concepts/origins.mdx
new file mode 100644
index 000000000000..d140a0f60374
--- /dev/null
+++ b/ads/concepts/origins.mdx
@@ -0,0 +1,38 @@
+---
+sidebar_position: 5
+sidebar_label: Origins
+---
+
+# Origins
+
+An origin is a manifest URL that a channel monitors for ad markers. When [break detection](./marker-detection.mdx) is enabled, OptiView Ads fetches the channel's enabled origins and parses their manifests for markers. A channel can have multiple origins so that detection keeps working when one source goes offline.
+
+## Origin identity
+
+Every origin has an `id` that is unique within its channel. The `id` is optional when creating an origin: if you omit it, OptiView Ads generates one for you. When you supply your own, we recommend using a UUID. For a human-readable label, use the `name` property instead — it is shown in the dashboard.
+
+## Enabling and disabling origins
+
+An origin can be enabled or disabled at any time. Disabling an origin removes it from detection immediately; the origin itself is kept, so you can re-enable it later without recreating it.
+
+## How multiple origins are used
+
+Only enabled origins are considered for detection. Enabled origins are ordered by `priority`, and detection uses the **first online origin**: the first one whose manifest is fetched and parsed successfully.
+
+- **Lowest `priority` value first.**
+- **First online wins.** An origin counts as online when its manifest can be fetched and parsed. A manifest that is reachable but currently advertises no markers still counts as online, so lower-priority origins are not consulted. If an origin cannot be fetched or parsed, detection falls back to the next enabled origin in priority order.
+
+This lets you configure a primary origin and one or more lower-priority backups: when the primary source is unreachable, detection automatically falls back to a backup.
+
+:::note Supported origin types
+Origins can be `HLS`, `DASH`, or `HESP`, but automatic break detection currently parses **HLS** manifests only. Use `HLS` for origins you expect to drive automatic breaks.
+:::
+
+## Related resources
+
+| Resource | Relationship |
+| ----------------------------------------- | ------------------------------------------------------------------------- |
+| [Channels](./channels.mdx) | The parent of an origin. An origin always belongs to one channel. |
+| [Break detection](./marker-detection.mdx) | Configure marker rules to turn ad markers in your stream into breaks. |
+| [Templates](./templates.mdx) | Reusable break presets that marker rules schedule when a marker matches. |
+| [Breaks](./breaks.mdx) | The breaks created when detection matches a marker against a marker rule. |
diff --git a/ads/concepts/templates.mdx b/ads/concepts/templates.mdx
new file mode 100644
index 000000000000..493fb866b7be
--- /dev/null
+++ b/ads/concepts/templates.mdx
@@ -0,0 +1,68 @@
+---
+sidebar_position: 3
+sidebar_label: Templates
+---
+
+# Templates
+
+A template is a reusable break preset for OptiView Ads. It stores a break configuration once so you can schedule consistent breaks quickly, either manually from the dashboard and API or automatically through marker rules.
+
+## Template identity
+
+Every template has an `id` that is unique within your organization. The `id` is optional when creating a template: if you omit it, OptiView Ads generates one for you. When you supply your own, we recommend using a UUID. For a human-readable label, use the `name` property instead — it is shown in the dashboard.
+
+## What a template contains
+
+A template holds the same configuration as a break: anything you can express on a break you can preset on a template. See [Break configuration](./breaks.mdx#break-configuration) for the full description of `variant`, `resumeOffset`, and `controls`.
+
+A few properties differ from a break:
+
+| Property | Difference |
+| ------------ | --------------------------------------------------------------------------------------------------------------------------------- |
+| `name` | A human-readable display name for the template, shown in the dashboard. |
+| `duration` | Optional on a template (it is required on a break). When set, it is the default duration for breaks scheduled from the template. |
+| `channelIds` | Channels the template is linked to, so it is surfaced when scheduling breaks on those channels. |
+| `eventIds` | [Events](./events.mdx) the template is linked to. |
+| `start` | A template has no `start`: a template is not scheduled itself. The start is supplied when a break is scheduled from the template. |
+
+## Snapshot semantics
+
+A template is a preset, not a live link. When a break is scheduled from a template:
+
+1. The template's configuration is **copied onto the new break** at creation.
+2. The break records the source `templateId` as provenance.
+3. There is **no synchronization afterwards**. Editing or deleting the template later does not change breaks that were already created from it — they keep their copied configuration.
+
+## Scheduling a break from a template
+
+Templates are used throughout the system to schedule breaks:
+
+- **API** — create a break on a channel and reference the template with `templateId`. The template configuration is snapshotted onto the break at creation.
+- **Dashboard** — use the **Schedule now** action on a template to create a break on the channel immediately.
+- **Marker rules** — when an in-stream ad marker is matched against a [marker rule](./marker-detection.mdx), a break is scheduled from the template that the rule references.
+
+An API example:
+
+```bash
+curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/1f7f3a5a-9c2e-4a56-b1d4-3f8a2c9d6e01/breaks' \
+ -u "$ADS_API_KEY:$ADS_API_SECRET" \
+ -H 'Content-Type: application/json' \
+ -H 'X-Org-ID: org_123' \
+ -d '{
+ "templateId": "3b8e5f0a-7c2d-4e91-a6b3-9d4f1c8e2a70",
+ "start": "2026-07-16T13:00:00.000Z"
+ }'
+```
+
+`templateId` is the only required field. You can override the snapshotted configuration per break with optional fields — `start`, `duration`, `variant`, `eventId`, and `id`. For templates that contain [vendor assets](./breaks.mdx#vendor-assets), you can additionally pass `assetParameters` (a string map) with the request; these are merged into the vendor assets of the snapshot, and on duplicate keys the values you provide win.
+
+Start semantics depend on the channel timebase; see [Timebase-based scheduling](./breaks.mdx#timebase-based-scheduling).
+
+## Related resources
+
+| Resource | Relationship |
+| ----------------------------------------- | ------------------------------------------------------------------------------------- |
+| [Channels](./channels.mdx) | Breaks scheduled from a template are created on a channel. |
+| [Breaks](./breaks.mdx) | The result of scheduling a template: a break with the template's snapshotted content. |
+| [Events](./events.mdx) | An event on your channel for which you want to prepare breaks. |
+| [Break detection](./marker-detection.mdx) | Configure marker rules to turn ad markers in your stream into breaks. |
diff --git a/ads/integrations/google/dai.mdx b/ads/integrations/google/dai.mdx
new file mode 100644
index 000000000000..ee4463f7a99a
--- /dev/null
+++ b/ads/integrations/google/dai.mdx
@@ -0,0 +1,92 @@
+---
+sidebar_position: 2
+sidebar_label: Google DAI
+---
+
+# Google DAI
+
+Google DAI (Dynamic Ad Insertion) is the server-side (SSAI) delivery mode for Google Ad Manager: Google stitches the ads directly into the media stream that players receive.
+
+## What is Google DAI?
+
+With server-side ad insertion, the ad content is inserted into the video stream itself before it reaches the player. The player plays one continuous stream and needs no ad-insertion logic of its own.
+
+Use Google DAI for platforms that cannot run the server-guided [Google Pod Serving](./pod-serving.mdx) flow — such as Roku and older connected TV devices. On platforms that support pod serving, prefer pod serving: it keeps the media stream untouched and enables the advanced break [layouts](../../concepts/breaks.mdx#layouts).
+
+Before you start, make sure the [Google Ad Manager prerequisites](./index.mdx) are configured for your organization.
+
+## Configure Google Ad Manager
+
+### Live streams
+
+A live stream in Google Ad Manager connects your linear stream to Google's dynamic ad insertion. Create one under **Video > Live streams > New live stream** and select **Linear** as the type. The live stream provides the **DAI asset key** that identifies it, and it carries the **content stream URL** and the **master ad tag**.
+
+### Content stream URL and the Dolby Ads Proxy
+
+Google DAI needs to see the ad break cues inside the stream to know where to insert ads. Because OptiView Ads schedules breaks without modifying your origin's stream, the **Dolby Ads Proxy** sits between your CDN and Google DAI: it passes your HLS stream through unchanged and inserts the ad break cue metadata (`EXT-X-DATERANGE` tags) for upcoming breaks.
+
+```text
+Customer origin ──▶ Customer CDN ──▶ Dolby Ads Proxy ──▶ Google DAI
+```
+
+Use the Dolby Ads Proxy URL for your channel's origin as the live stream's content stream URL:
+
+```text
+https://us.ads.optiview.dolby.com/proxy/v1/{organization-id}/origins/{origin-id}/google/index.m3u8
+```
+
+For the EU region, use `https://eu.ads.optiview.dolby.com/proxy/v1/`. Your account team can help you determine the exact URL for your setup.
+
+### Master ad tag
+
+The master ad tag is the ad tag URL configured on the live stream. It references your network code and the ad unit to target, so when Google inserts an ad break it knows which line items and creatives are eligible. See [Configure Google Ad Manager](./pod-serving.mdx#configure-google-ad-manager) on the Google Pod Serving page for how ad units, orders, line items, and creatives fit together — the same campaign setup applies to Google DAI.
+
+## Configure your channel
+
+Enable Google DAI on your [channel](../../concepts/channels.mdx) by creating a channel integration with the DAI asset key(s) from your Google live stream(s):
+
+```json
+{
+ "type": "SSAI",
+ "vendor": "GOOGLE",
+ "daiAssetKeys": ["si2XCsdWTB-Cz9zWftSCoQ"]
+}
+```
+
+The DAI asset key identifies the Google live stream that ad breaks are announced to. A DAI asset key can only be used on one channel within your organization.
+
+## Schedule a break
+
+Scheduling works exactly like the pod serving flow: create a [break](../../concepts/breaks.mdx) with a vendor asset and optional `assetParameters`. See [Schedule a pod break](./pod-serving.mdx#schedule-a-pod-break) — including the [required Google parameters](./pod-serving.mdx#required-google-parameters).
+
+## How the Google DAI flow works
+
+1. A break is created and, ahead of its start (within the [look-forward time](./index.mdx#look-forward-time)), OptiView Ads announces it to Google using the integration's DAI asset key.
+2. When the break's start approaches, the Dolby Ads Proxy inserts the break's cue metadata (`EXT-X-DATERANGE` tags carrying the SCTE-35 markers) into the stream served to Google DAI. The break becomes `SIGNALED` once its cue is inserted into the stream.
+3. Google DAI detects the cue, matches it with the announced break, and stitches the decisioned ads into the stream delivered to players.
+
+### Validate your stream
+
+You can validate the end-to-end setup with Google's [DAI SDK for HTML5 Video Suite Inspector](https://googleads.github.io/googleads-ima-html5-dai/vsi/). Enter your network code and DAI asset key to play your live stream through Google DAI and inspect the inserted ad breaks and the ad requests Google makes.
+
+## Troubleshooting
+
+A break that fails ends up in the `ERROR` state with an `errorReason` — see [Troubleshooting](./pod-serving.mdx#troubleshooting) on the Google Pod Serving page for the possible reasons.
+
+Common Google DAI-specific issues relate to the ad tag parameters on the ad requests:
+
+- **Missing required parameters.** Google requires `description_url` for ad serving, and `plcmt`, `vpa`, and `vpmute` for programmatic monetization. For app inventory, `rdid`, `is_lat`, `idtype`, `an`, and `msid` are also required, and `wta` is highly recommended. See [Required Google parameters](./pod-serving.mdx#required-google-parameters).
+- **Invalid `description_url` format.** The value must be URL-encoded exactly once — a double-encoded value (for example `https%253A%252F%252F...`) is invalid. Supply the plain URL and verify the encoding in the final ad request.
+
+Use the [Video Suite Inspector](#validate-your-stream) to inspect the ad requests and confirm the parameters Google receives.
+
+## Related resources
+
+| Resource | Relationship |
+| --------------------------------------- | -------------------------------------------------------------------------- |
+| [Google Ad Manager](./index.mdx) | The prerequisites shared by both Google delivery modes. |
+| [Google Pod Serving](./pod-serving.mdx) | Server-guided (SGAI) delivery, preferred where supported. |
+| [Channels](../../concepts/channels.mdx) | Hold the Google DAI channel integration. |
+| [Origins](../../concepts/origins.mdx) | The stream origins proxied by the Dolby Ads Proxy. |
+| [Breaks](../../concepts/breaks.mdx) | Carry the vendor asset that Google uses to make ad decisions. |
+| [API reference](/ads/api) | Creating and managing channels, breaks, and integrations programmatically. |
diff --git a/ads/integrations/google/index.mdx b/ads/integrations/google/index.mdx
new file mode 100644
index 000000000000..5069bc34fc3e
--- /dev/null
+++ b/ads/integrations/google/index.mdx
@@ -0,0 +1,70 @@
+---
+sidebar_position: 2
+sidebar_label: Google Ad Manager
+---
+
+# Google Ad Manager 360
+
+Google Ad Manager 360 (GAM 360) is the first supported OptiView Ads vendor. It requires a GAM 360 account with Dynamic Ad Insertion (DAI) enabled.
+
+OptiView Ads supports two Google delivery modes:
+
+- **[Google Pod Serving](./pod-serving.mdx)** — server-guided (SGAI): Google decisions the ad pods, and the player inserts them guided by the [Break Manifest](../../concepts/break-manifest.mdx). This is the preferred mode and enables the advanced break [layouts](../../concepts/breaks.mdx#layouts).
+- **[Google DAI](./dai.mdx)** — server-side (SSAI): Google stitches the ads directly into the media stream. Use this for platforms that cannot run the server-guided flow, such as Roku and older connected TV devices.
+
+Before you can use either mode, the prerequisites below must be in place.
+
+## What we need from you
+
+Google configuration is organization-level and set up by your Dolby OptiView account team. To get started, provide your account team with your **network code** and a **service account** with access to the Google Ad Manager API.
+
+### Network code
+
+The network code is the unique numeric identifier of your Google Ad Manager network. OptiView Ads uses it in every interaction with Google — when announcing upcoming breaks, and in the pod URLs that players request.
+
+You can find it in the Google Ad Manager dashboard under **Admin > Global settings > Network settings**, in the **Network code** field. It is also visible in the URL of your Google Ad Manager dashboard: `https://admanager.google.com/`.
+
+### Service account
+
+A service account is a non-human Google account that lets OptiView Ads call the Google Ad Manager API on behalf of your network — for example, to announce upcoming ad breaks so Google can decision them in time.
+
+To configure it:
+
+1. Create a service account in the [Google Cloud console](https://console.cloud.google.com/iam-admin/serviceaccounts) and generate a JSON key for it.
+2. In Google Ad Manager, make sure API access is enabled under **Admin > Global settings > Network settings**.
+3. Add the service account's email address as a user in Google Ad Manager under **Admin > Access & authorization > Users**, with a role that grants API access.
+4. Share the JSON key securely with your Dolby OptiView account team.
+
+See Google's [service account documentation](https://support.google.com/admanager/answer/6078734) for details.
+
+### Look-forward time
+
+The look-forward time is how far ahead of a break's scheduled start OptiView Ads announces the break to Google. Google needs this lead time to decision the ads for the break — selecting the ads takes time, so announcing the break early ensures the ads are ready when the break starts.
+
+By default, breaks are announced **5 minutes** before their scheduled start. If you want a different look-forward time, contact your Dolby OptiView account team.
+
+### Decisioning margin
+
+The decisioning margin is the minimum time between the moment a break is scheduled and its start. It protects against breaks that Google cannot decision in time: if a break is scheduled closer to its start than the margin allows, the request is rejected instead of risking an empty break.
+
+The default margin is **5 seconds**. If you want a different margin, contact your Dolby OptiView account team.
+
+## Next steps
+
+Once the prerequisites are configured, continue with the delivery mode that fits your platform:
+
+| Delivery mode | When to use it |
+| --------------------------------------- | ------------------------------------------------------------------------------------------------------ |
+| [Google Pod Serving](./pod-serving.mdx) | Server-guided insertion on platforms running an OptiView player: web, iOS, Android, and most CTV apps. |
+| [Google DAI](./dai.mdx) | Server-side insertion for platforms without server-guided support, such as Roku and older CTV devices. |
+
+## Related resources
+
+| Resource | Relationship |
+| --------------------------------------------------- | -------------------------------------------------------------------------- |
+| [Google Pod Serving](./pod-serving.mdx) | Server-guided (SGAI) delivery through Google pod serving. |
+| [Google DAI](./dai.mdx) | Server-side (SSAI) delivery through Google Dynamic Ad Insertion. |
+| [Channels](../../concepts/channels.mdx) | Hold the Google delivery configuration for a stream. |
+| [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. |
+| [API reference](/ads/api) | Creating and managing channels, breaks, and integrations programmatically. |
diff --git a/ads/integrations/google/pod-serving.mdx b/ads/integrations/google/pod-serving.mdx
new file mode 100644
index 000000000000..6016f149de15
--- /dev/null
+++ b/ads/integrations/google/pod-serving.mdx
@@ -0,0 +1,157 @@
+---
+sidebar_position: 1
+sidebar_label: Google Pod Serving
+---
+
+# Google Pod Serving
+
+Google Pod Serving is the server-guided (SGAI) delivery mode for Google Ad Manager. Google decisions the ad pods server-side, and the player inserts them client-side, guided by the [Break Manifest](../../concepts/break-manifest.mdx).
+
+## What is pod serving?
+
+With pod serving, Google assembles a complete ad break — a "pod" of one or more ads — server-side and exposes it as a single stream that the player stitches in at the right moment. This combines the best of both worlds:
+
+- **Server-side ad decisioning and stitching.** Google selects, transcodes, and stitches the ads into a pod that matches your stream's renditions, so playback transitions are seamless and ads are resistant to ad blockers.
+- **Client-side insertion.** The media stream itself is never modified. The player inserts the pod guided by the Break Manifest, which keeps advanced [layouts](../../concepts/breaks.mdx#layouts) such as double box and L-shape possible and keeps OptiView Ads out of your stream's critical path.
+
+Before you start, make sure the [Google Ad Manager prerequisites](./index.mdx) are configured for your organization.
+
+## Configure Google Ad Manager
+
+Pod serving is configured in the Google Ad Manager dashboard with the building blocks below.
+
+### Ad units
+
+An ad unit represents a place where ads can be served — for pod serving, it represents your live stream's ad breaks. You create ad units under **Inventory > Ad units**. The ad unit is linked to your OptiView channel through the live stream's master ad tag (see below): the ad tag references the ad unit, and the live stream references your channel through its custom asset key.
+
+### Orders, line items, and creatives
+
+Google Ad Manager organizes campaigns in three levels:
+
+- **Order** — the agreement with an advertiser or buyer. An order groups one or more line items.
+- **Line item** — a concrete delivery commitment within an order: which ad unit(s) to target, the flight dates, how many impressions to deliver, and at which priority.
+- **Creative** — the actual ad media (the video file) served when a line item is selected. Creatives are attached to line items.
+
+### Live streams
+
+A live stream in Google Ad Manager connects your linear stream to Google's dynamic ad insertion. Create one under **Video > Live streams > New live stream** and select **pod serving** as the ad serving type, with an HLS pod serving manifest. The live stream carries two values that matter to OptiView Ads:
+
+- the **custom asset key**, and
+- the **master ad tag**.
+
+### Custom asset key
+
+The custom asset key is an identifier you choose when creating the live stream. It uniquely identifies the live stream event in Google Ad Manager, and it is the key OptiView Ads uses to tell Google which stream a break belongs to when announcing upcoming breaks.
+
+### Master ad tag
+
+The master ad tag is the ad tag URL configured on the live stream. It ties the setup together: it references your network code and the ad unit, so when Google decisions a pod for the live stream, it knows which line items and creatives are eligible. Ad tag parameters added to pod requests extend this master ad tag.
+
+## Configure your channel
+
+Enable Google Pod Serving on your [channel](../../concepts/channels.mdx) by configuring the custom asset key from your Google live stream on it. A custom asset key can only be used on one channel within your organization.
+
+Once configured, the Break Manifest for the channel carries the Google session configuration (network code and custom asset key), so players can set up their pod serving session automatically.
+
+## Schedule a pod break
+
+A Google pod break is a regular [break](../../concepts/breaks.mdx) whose variant carries a [vendor asset](../../concepts/breaks.mdx#vendor-assets) with `vendorParameters.type` set to `"pod"`:
+
+```json
+{
+ "type": "vendor",
+ "vendor": "gam",
+ "vendorParameters": {
+ "type": "pod"
+ },
+ "assetParameters": {
+ "description_url": "https://www.example.com/sports/live.html",
+ "plcmt": "1"
+ }
+}
+```
+
+Google performs the decisioning, so no asset URI is needed: once Google has decisioned the break, the resulting pod identifier is stored as the asset's URI.
+
+### Ad tag parameters
+
+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.md) for the player-side API on each platform.
+
+As a rule of thumb:
+
+- **Provide through the backend (`assetParameters`)** the values that describe your content and are the same for every viewer, such as `description_url` and custom targeting parameters.
+- **Provide through the player (`adTagParameters`)** the values that depend on the playback environment or the device, such as `vpa`, `vpmute`, and — for apps — the device and app identity parameters.
+
+### Required Google parameters
+
+Google requires a number of ad tag parameters for ad serving and programmatic monetization. Missing or malformed values reduce fill or block programmatic demand entirely.
+
+| Parameter | What it is | Required |
+| ----------------- | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
+| `description_url` | A URL to a crawlable page that describes the video being played — not your top-level domain. | Required for ad serving on web and in apps. |
+| `plcmt` | Declares the inventory placement: `1` for in-stream, `2` for accompanying content. | Required for programmatic monetization on all platforms. |
+| `vpa` | Whether playback starts automatically (`auto`) or on user action (`click`). Leave unset if unknown. | Required for programmatic monetization on all platforms. |
+| `vpmute` | Whether playback starts muted: `1` for muted, `0` for unmuted. | Required for programmatic monetization on all platforms. |
+
+:::caution Encode `description_url` exactly once
+In the final ad request, the `description_url` value must be URL-encoded exactly once on web and CTV (and not encoded at all in mobile apps). Double encoding is a common mistake and makes the value invalid.
+
+Correct: `description_url=https%3A%2F%2Fwww.example.com%2Fsports%2Flive.html`
+Incorrect (double-encoded): `description_url=https%253A%252F%252Fwww.example.com%252Fsports%252Flive.html`
+
+Supply the plain, unencoded URL in `assetParameters`, and verify in the final ad request that the value appears encoded exactly once.
+:::
+
+### Additional parameters for app inventory
+
+For requests from mobile apps and connected TV devices, programmatic buyers additionally require device and app identity parameters. These are playback-environment values, so supply them from the player side through `adTagParameters`.
+
+| Parameter | What it is |
+| --------- | ------------------------------------------------------------------------------------ |
+| `rdid` | The resettable device identifier (identifier for advertising) of the device. |
+| `idtype` | The type of the device identifier (for example `adid` for Android, `idfa` for iOS). |
+| `is_lat` | Whether the user has limited ad tracking: `0` for tracking allowed, `1` for limited. |
+| `an` | The human-readable name of your app. |
+| `msid` | The app ID (bundle or package name, for example `com.publisher.app`). |
+
+### Recommended: `wta`
+
+The "Why this ad?" parameter (`wta`) declares whether the player renders Google's ad badging (the AdChoices icon): `wta=1` when it is rendered, `wta=0` when it is not. It is not required, but it is highly recommended: `wta=0` traffic is not eligible for certain types of personalization from Google demand, and ads served in the European Economic Area have regulatory requirements around ad badging.
+
+## How pod decisioning works
+
+1. A Google pod break is created and starts in the `PREPARING` state.
+2. Ahead of the break's start (within the [look-forward time](./index.mdx#look-forward-time)), OptiView Ads announces the break to Google using the channel's custom asset key.
+3. Google decisions the ad pod and returns a pod identifier, which is stored as the vendor asset's URI. The break becomes `READY` when it has a start time, or `CUED` when it is waiting to be [punched](../../concepts/breaks.mdx#break-punching).
+4. When the break is announced to players through the [Break Manifest](../../concepts/break-manifest.mdx), it becomes `SIGNALED`. The player requests the pod from Google using the pod identifier.
+
+A Google pod break can only be punched after Google has decisioned it — that is, once it has left the `PREPARING` state.
+
+## Troubleshooting
+
+A break that fails ends up in the `ERROR` state with an `errorReason` explaining what went wrong:
+
+| `errorReason` | What it means |
+| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `CUSTOM_ASSET_KEY_NOT_FOUND` | The channel's custom asset key is not registered in Google Ad Manager. Register the key as a live stream in GAM, or correct the channel's custom asset key. |
+| `UNKNOWN_DAI_API_ERROR` | Google returned an error that could not be mapped to a more specific reason. Verify your Google Ad Manager configuration, or contact your account team. |
+| `SCHEDULING_WINDOW_MISSED` | The break passed its scheduling window before it could be announced to Google. Schedule pod breaks further ahead of their start time. |
+| `UNKNOWN` | An unexpected error occurred. Contact your account team. |
+
+Additionally, creating a pod break that starts too close to the live point is rejected up front with the reason `DECISIONING_MARGIN`: pod breaks must start at least the [decisioning margin](./index.mdx#decisioning-margin) after the live point, so Google has time to decision the ads.
+
+## 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.md) | Passing ad tag parameters from the player. |
+| [Getting started on Web](../../getting-started/web.mdx) | Set up an OptiView player with pod serving on web. |
+| [Getting started on iOS](../../getting-started/ios.mdx) | Set up an OptiView player with pod serving on iOS. |
+| [Getting started on Android](../../getting-started/android.mdx) | Set up an OptiView player with pod serving on Android. |
diff --git a/ads/integrations/index.mdx b/ads/integrations/index.mdx
new file mode 100644
index 000000000000..13f2eb75c1a6
--- /dev/null
+++ b/ads/integrations/index.mdx
@@ -0,0 +1,25 @@
+---
+sidebar_position: 1
+sidebar_label: Integrations
+---
+
+# 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.
+
+## Supported vendors
+
+| Vendor | Enum value | Delivery |
+| ------------------------------------------- | ---------- | ----------------------------------------------------------------------------------------------- |
+| [Google Ad Manager 360](./google/index.mdx) | `gam` | [Google Pod Serving](./google/pod-serving.mdx) (SGAI) and [Google DAI](./google/dai.mdx) (SSAI) |
+
+The vendor model is extensible. When another vendor is supported, its documentation will be added as a separate page in this section and listed in the Integrations sidebar.
+
+## Related resources
+
+| Resource | Relationship |
+| --------------------------------------- | ---------------------------------------------------------------------------------- |
+| [Channels](../concepts/channels.mdx) | Hold the channel-level `customAssetKey` used for Google server-guided pod serving. |
+| [Breaks](../concepts/breaks.mdx) | Carry the vendor asset in a break variant. |
+| [Templates](../concepts/templates.mdx) | Reusable break presets that can include vendor assets. |
+| [Google Ad Manager](./google/index.mdx) | The first supported vendor: Google Pod Serving (SGAI) and Google DAI (SSAI). |
diff --git a/sidebarsAds.ts b/sidebarsAds.ts
index c0ca37d24082..7975a313b0b4 100644
--- a/sidebarsAds.ts
+++ b/sidebarsAds.ts
@@ -15,6 +15,26 @@ const sidebars: SidebarsConfig = {
link: { type: 'doc', id: 'getting-started/index' },
items: [{ type: 'autogenerated', dirName: 'getting-started' }],
},
+ {
+ type: 'category',
+ label: 'Core concepts',
+ description: 'Understand the resources that power OptiView Ads.',
+ collapsed: false,
+ customProps: {
+ icon: '📚',
+ },
+ items: [{ type: 'autogenerated', dirName: 'concepts' }],
+ },
+ {
+ type: 'category',
+ label: 'Integrations',
+ description: 'Configure integrations for server-guided and server-side ad insertion.',
+ customProps: {
+ icon: '🏷️',
+ },
+ link: { type: 'doc', id: 'integrations/index' },
+ items: [{ type: 'autogenerated', dirName: 'integrations' }],
+ },
{
type: 'category',
label: 'How-to guides',