From bd48eb880e35b3e665002eb0ba9f76b264abcfeb Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 13:42:59 +0000 Subject: [PATCH 01/22] ADS-112 document Ads channels Co-Authored-By: maarten.rimaux --- ads/concepts/channels.mdx | 216 ++++++++++++++++++++++++++++++++++++++ sidebarsAds.ts | 10 ++ 2 files changed, 226 insertions(+) create mode 100644 ads/concepts/channels.mdx diff --git a/ads/concepts/channels.mdx b/ads/concepts/channels.mdx new file mode 100644 index 000000000000..e033ad80a64e --- /dev/null +++ b/ads/concepts/channels.mdx @@ -0,0 +1,216 @@ +--- +sidebar_position: 1 +sidebar_label: Channels +--- + +# Channels + +import RebrandingNotice from '../callouts/_rebranding_notice.md'; + + + +A channel is the top-level OptiView Ads resource for one live stream. It stores the stream timing model, the Break Manifest polling policy, optional Google DAI asset metadata, and the enablement state for automatic marker detection. + +Channels are scoped to an organization. API calls identify the organization with the `X-Org-ID` header and authenticate with an API key and secret using HTTP Basic authentication. + +## Dashboard path + +In the OptiView Unified Dashboard, open **Ads → Channels**. The channel list exposes **New**, **Edit**, **Delete**, and **Details** actions. + +After opening a channel, the current V2 navigation includes these areas: + +| Area | Use it for | +| ----------------- | ------------------------------------------------------ | +| Overview | View channel settings and player/origin quick actions. | +| Breaks | Schedule, inspect, and delete breaks for the channel. | +| Events | Manage event windows and event-scoped breaks. | +| Origins | Add, enable, disable, and prioritize manifest origins. | +| Break Detection | Configure marker rules and review detection history. | +| Break Integration | Manage channel-level delivery integrations. | + +## Channel identity + +Every channel has a customer-facing `id`. The API stores it together with the organization ID, so the unique identity is: + +```text +organizationId + channelId +``` + +Use stable channel IDs that match your operational names, such as `sports-main` or `news-east`. If you omit `id` on creation, the API generates one. + +## Related resources + +A channel is the parent or lookup point for the rest of the Ads V2 model: + +| Resource | Relationship | +| ----------------- | ---------------------------------------------------------------------------- | +| Origins | Manifest URLs monitored for ad markers. A channel can have multiple origins. | +| Marker rules | Rules that turn detected markers into breaks. | +| Detection history | Audit records for marker detection decisions on the channel. | +| Breaks | Scheduled or detected ad opportunities for the channel. | +| Events | Time windows that group related breaks. | +| Templates | Reusable break presets that can be scheduled on the channel. | +| Integrations | Channel-level delivery integrations, such as SSAI DAI cue fan-out. | + +## Timebase + +The `timebase` determines how breaks are scheduled for the channel. + +| Timebase | Break start field | Use when | +| ----------- | ----------------- | -------------------------------------------------------------------------------- | +| `wallclock` | `startWallclock` | The stream has UTC wallclock timing, usually from HLS `EXT-X-PROGRAM-DATE-TIME`. | +| `pts` | `startPts` | The workflow schedules against a presentation timestamp timeline. | + +Choose the timebase when creating the channel. Breaks created for that channel use the same timebase. + +## Timing configuration + +| Field | Type | Default | Description | +| ---------------------- | -------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `timebase` | `wallclock` or `pts` | Required | Selects whether breaks use `startWallclock` or `startPts`. | +| `dvrWindowMs` | integer | `300000` | DVR look-back window used when deciding which breaks are still relevant for delivery. | +| `liveOffsetMs` | integer | `0` | Live latency offset. Wallclock break starts are evaluated against the live playhead rather than raw server time. | +| `pollingIdleSeconds` | integer | `10` | Break Manifest polling interval advertised when no break is active. | +| `pollingActiveSeconds` | integer | `1` | Break Manifest polling interval advertised during an active break; also used for active manifest caching. | +| `customAssetKey` | string | none | Google DAI custom asset key used for server-guided pod serving on this channel. It must be unique within the organization when set. | +| `detectionEnabled` | boolean | `false` | Read-only response field showing whether automatic marker detection is enabled. | + +## Marker detection lifecycle + +`detectionEnabled` is read-only on channel create and update requests. Toggle detection with the dedicated channel actions: + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/detection/enable' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/detection/disable' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +When detection is enabled, the worker polls the enabled origins for the channel in priority order. The first online origin is used for marker evaluation. Marker rules decide whether a detected marker creates a break, and detection history records the action, reason, origin, marker rule, and break IDs. + +## Create a channel + +Dashboard: **Ads → Channels → New**. + +API: + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "id": "sports-main", + "name": "Sports main", + "timebase": "wallclock", + "dvrWindowMs": 300000, + "liveOffsetMs": 0, + "pollingIdleSeconds": 10, + "pollingActiveSeconds": 1, + "customAssetKey": "sports-main-custom-asset" + }' +``` + +Example response: + +```json +{ + "id": "sports-main", + "name": "Sports main", + "timebase": "wallclock", + "dvrWindowMs": 300000, + "liveOffsetMs": 0, + "pollingIdleSeconds": 10, + "pollingActiveSeconds": 1, + "customAssetKey": "sports-main-custom-asset", + "detectionEnabled": false, + "createdAt": "2026-07-16T12:00:00.000Z" +} +``` + +## Get a channel + +```bash +curl 'https://ads.example.com/api/v1/channels/sports-main' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +## Update a channel + +```bash +curl -X PATCH 'https://ads.example.com/api/v1/channels/sports-main' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "name": "Sports main HD", + "pollingIdleSeconds": 15 + }' +``` + +## List channels + +```bash +curl 'https://ads.example.com/api/v1/channels?page=1&pageSize=20&sort=-createdAt' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +List endpoints use the same pagination shape. Channels can be filtered by `name` and sorted by `name` or `createdAt`. + +| Query parameter | Default | Description | +| --------------- | ------------ | -------------------------------------------------------------------------- | +| `page` | `1` | Page number. | +| `pageSize` | `20` | Items per page. Maximum `100`. | +| `filter` | none | Optional RSQL filter expression. | +| `sort` | `-createdAt` | Comma-separated sort fields. Prefix a field with `-` for descending order. | + +Examples: + +```bash +curl 'https://ads.example.com/api/v1/channels?filter=name=like=sports&pageSize=50' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +```bash +curl 'https://ads.example.com/api/v1/channels?sort=name,-createdAt' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +## Delete a channel + +```bash +curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +Delete a channel only after confirming that no active workflow still depends on its origins, marker rules, events, breaks, templates, or integrations. + +## Add an origin to a channel + +Dashboard: open the channel, then use **Origins** from the channel navigation. + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/origins' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "name": "Primary HLS origin", + "type": "HLS", + "url": "https://origin.example.com/live/sports-main/master.m3u8", + "enabled": true, + "priority": 0 + }' +``` + +Lower `priority` values are tried first when detection is enabled. diff --git a/sidebarsAds.ts b/sidebarsAds.ts index c0ca37d24082..3894133a7329 100644 --- a/sidebarsAds.ts +++ b/sidebarsAds.ts @@ -4,6 +4,16 @@ import signalingApiSidebar from './ads/api/signaling/sidebar'; const sidebars: SidebarsConfig = { ads: [ 'index', + { + type: 'category', + label: 'Core concepts', + description: 'Understand the resources that power OptiView Ads.', + collapsed: false, + customProps: { + icon: '📚', + }, + items: ['concepts/channels'], + }, { type: 'category', label: 'Getting started', From fe9c61c73faf81ff8ec5d4731e0f036e05074f0a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 13:55:11 +0000 Subject: [PATCH 02/22] ADS-112 clarify channel service fallbacks Co-Authored-By: maarten.rimaux --- ads/concepts/channels.mdx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ads/concepts/channels.mdx b/ads/concepts/channels.mdx index e033ad80a64e..b752463179e7 100644 --- a/ads/concepts/channels.mdx +++ b/ads/concepts/channels.mdx @@ -63,17 +63,17 @@ The `timebase` determines how breaks are scheduled for the channel. Choose the timebase when creating the channel. Breaks created for that channel use the same timebase. -## Timing configuration - -| Field | Type | Default | Description | -| ---------------------- | -------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- | -| `timebase` | `wallclock` or `pts` | Required | Selects whether breaks use `startWallclock` or `startPts`. | -| `dvrWindowMs` | integer | `300000` | DVR look-back window used when deciding which breaks are still relevant for delivery. | -| `liveOffsetMs` | integer | `0` | Live latency offset. Wallclock break starts are evaluated against the live playhead rather than raw server time. | -| `pollingIdleSeconds` | integer | `10` | Break Manifest polling interval advertised when no break is active. | -| `pollingActiveSeconds` | integer | `1` | Break Manifest polling interval advertised during an active break; also used for active manifest caching. | -| `customAssetKey` | string | none | Google DAI custom asset key used for server-guided pod serving on this channel. It must be unique within the organization when set. | -| `detectionEnabled` | boolean | `false` | Read-only response field showing whether automatic marker detection is enabled. | +## Configuration reference + +| Field | Type | Default | Description | +| ---------------------- | -------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `timebase` | `wallclock` or `pts` | Required | Selects whether breaks use `startWallclock` or `startPts`. | +| `dvrWindowMs` | integer | service fallback: `300000` | DVR look-back window used when deciding which breaks are still relevant for delivery. | +| `liveOffsetMs` | integer | `0` | Live latency offset. Wallclock break starts are evaluated against the live playhead rather than raw server time. | +| `pollingIdleSeconds` | integer | service fallback: `10` | Break Manifest polling interval advertised when no break is active. | +| `pollingActiveSeconds` | integer | service fallback: `1` | Break Manifest polling interval advertised during an active break; also used for active manifest caching. | +| `customAssetKey` | string | none | Google DAI custom asset key used for server-guided pod serving on this channel. It must be unique within the organization when set. | +| `detectionEnabled` | boolean | `false` | Read-only response field showing whether automatic marker detection is enabled. | ## Marker detection lifecycle @@ -91,7 +91,7 @@ curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/detection/disa -H 'X-Org-ID: org_123' ``` -When detection is enabled, the worker polls the enabled origins for the channel in priority order. The first online origin is used for marker evaluation. Marker rules decide whether a detected marker creates a break, and detection history records the action, reason, origin, marker rule, and break IDs. +When detection is enabled, the worker polls the enabled origins for the channel in priority order. The first online origin is used for marker evaluation. Marker rules decide whether a detected marker creates a break, and detection history records the action, reason, origin, marker rule, and break ID. ## Create a channel From 08c307d590d7ad394730c660b1bc6013f455131f Mon Sep 17 00:00:00 2001 From: "maarten.rimaux" Date: Thu, 30 Jul 2026 14:18:24 +0000 Subject: [PATCH 03/22] ADS-112 use autogenerated concepts sidebar, keep Getting started first Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- sidebarsAds.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sidebarsAds.ts b/sidebarsAds.ts index 3894133a7329..1d95c1fbaeba 100644 --- a/sidebarsAds.ts +++ b/sidebarsAds.ts @@ -6,24 +6,24 @@ const sidebars: SidebarsConfig = { 'index', { type: 'category', - label: 'Core concepts', - description: 'Understand the resources that power OptiView Ads.', + label: 'Getting started', + description: 'Set up your first stream with OptiView Ads!', collapsed: false, customProps: { - icon: '📚', + icon: '🚀', }, - items: ['concepts/channels'], + link: { type: 'doc', id: 'getting-started/index' }, + items: [{ type: 'autogenerated', dirName: 'getting-started' }], }, { type: 'category', - label: 'Getting started', - description: 'Set up your first stream with OptiView Ads!', + label: 'Core concepts', + description: 'Understand the resources that power OptiView Ads.', collapsed: false, customProps: { - icon: '🚀', + icon: '📚', }, - link: { type: 'doc', id: 'getting-started/index' }, - items: [{ type: 'autogenerated', dirName: 'getting-started' }], + items: [{ type: 'autogenerated', dirName: 'concepts' }], }, { type: 'category', From 676c9a2ebde2420f136d4410bffdf066fe143a94 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 14:18:48 +0000 Subject: [PATCH 04/22] ADS-113 document ad breaks Co-Authored-By: maarten.rimaux --- ads/assets/img/breaks/break-lifecycle.svg | 30 + ads/assets/img/breaks/format-double.svg | 8 + ads/assets/img/breaks/format-lshape-ad.svg | 8 + .../img/breaks/format-lshape-content.svg | 8 + ads/assets/img/breaks/format-overlay.svg | 7 + ads/assets/img/breaks/format-single.svg | 5 + ads/concepts/breaks.mdx | 614 ++++++++++++++++++ 7 files changed, 680 insertions(+) create mode 100644 ads/assets/img/breaks/break-lifecycle.svg create mode 100644 ads/assets/img/breaks/format-double.svg create mode 100644 ads/assets/img/breaks/format-lshape-ad.svg create mode 100644 ads/assets/img/breaks/format-lshape-content.svg create mode 100644 ads/assets/img/breaks/format-overlay.svg create mode 100644 ads/assets/img/breaks/format-single.svg create mode 100644 ads/concepts/breaks.mdx diff --git a/ads/assets/img/breaks/break-lifecycle.svg b/ads/assets/img/breaks/break-lifecycle.svg new file mode 100644 index 000000000000..54c414a029f2 --- /dev/null +++ b/ads/assets/img/breaks/break-lifecycle.svg @@ -0,0 +1,30 @@ + + + + + + + + initial + + PREPARING + + CUED + + READY + + ERROR + + SIGNALED + + + worker/EABN + + worker/EABN + + worker/health + + API punch + + manifest / proxy + diff --git a/ads/assets/img/breaks/format-double.svg b/ads/assets/img/breaks/format-double.svg new file mode 100644 index 000000000000..978e9035f2eb --- /dev/null +++ b/ads/assets/img/breaks/format-double.svg @@ -0,0 +1,8 @@ + + + + + CONTENT + AD + side-by-side primary and companion windows + 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..5f7a72692bfb --- /dev/null +++ b/ads/assets/img/breaks/format-lshape-ad.svg @@ -0,0 +1,8 @@ + + + + AD + companion + backdrop + ad window with companion backdrop + 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..96bdc0bdf71e --- /dev/null +++ b/ads/assets/img/breaks/format-lshape-content.svg @@ -0,0 +1,8 @@ + + + + CONTENT + companion + backdrop + live content window with companion backdrop + diff --git a/ads/assets/img/breaks/format-overlay.svg b/ads/assets/img/breaks/format-overlay.svg new file mode 100644 index 000000000000..f526f3b3bcc6 --- /dev/null +++ b/ads/assets/img/breaks/format-overlay.svg @@ -0,0 +1,7 @@ + + + CONTENT + + OVERLAY + semi-transparent overlay over live content + diff --git a/ads/assets/img/breaks/format-single.svg b/ads/assets/img/breaks/format-single.svg new file mode 100644 index 000000000000..a83e60ae25ca --- /dev/null +++ b/ads/assets/img/breaks/format-single.svg @@ -0,0 +1,5 @@ + + + AD + full-screen replacement + diff --git a/ads/concepts/breaks.mdx b/ads/concepts/breaks.mdx new file mode 100644 index 000000000000..196774b9a628 --- /dev/null +++ b/ads/concepts/breaks.mdx @@ -0,0 +1,614 @@ +--- +sidebar_position: 2 +sidebar_label: Breaks +--- + +# Breaks + +import RebrandingNotice from '../callouts/_rebranding_notice.md'; + + + +A break is the core monetization entity in OptiView Ads. It represents an ad opportunity scheduled on a channel and contains the timing, lifecycle state, playback controls, layout variants, and typed assets that a player or delivery service needs. + +Breaks are scoped to an organization and created for a channel. API calls authenticate with an API key and secret using HTTP Basic authentication and identify the organization with the `X-Org-ID` header. + +## Dashboard path + +In the OptiView Unified Dashboard, open **Ads → Channels**, open a channel, and select **Breaks**. The Breaks area is used to schedule, inspect, and delete breaks for the channel. + +## Break identity + +The compound identity of a Break is: + +```text +orgId + channelId + id +``` + +`id` is optional when creating a Break. If omitted, the API generates an identifier. The identity is scoped by both the organization and channel, so the same `id` can exist on different channels or in different organizations. + +| Field | Type | Description | +| ------------ | ---------------- | --------------------------------------------------------------------------------------------------------------- | +| `id` | string | Break identifier. Auto-generated if omitted on create. | +| `orgId` | string | Organization scope. Supplied by the authenticated `X-Org-ID` context. | +| `channelId` | string | Parent channel identifier. | +| `originId` | string, optional | Provenance for an automatically detected Break. Set internally by the detection worker and not client-settable. | +| `templateId` | string, optional | Identifier of the Template used to create the Break, if any. | +| `eventId` | string, optional | Identifier of the Event under which the Break was scheduled, if any. | + +The Break also stores internal Google DAI fields such as `podId`, `assetKey`, `networkCode`, `customAssetKey`, and `daiAssetKeys`, plus the lifecycle `status`, optional `errorMessage`, timebase-specific start fields, denormalized indexes, and the raw `data` payload. + +### Stored fields + +| Field | Type | Required/default behavior | +| ------------------- | ---------------------- | ------------------------------------------------------------- | +| `id` | string | Required; generated when omitted on create. | +| `orgId` | string | Required organization scope. | +| `channelId` | string | Required channel scope. | +| `eventId` | string, optional | Event association. | +| `templateId` | string, optional | Template association retained after creation. | +| `podId` | string, optional | Google DAI pod identifier after vendor-pod decisioning. | +| `status` | enum | `PREPARING`, `CUED`, `READY`, `SIGNALED`, or `ERROR`. | +| `originId` | string, optional | Internal provenance for an automatically detected Break. | +| `markerRuleId` | string, optional | Marker rule associated with automatic detection. | +| `markerDetectionId` | string, optional | Detection-history record associated with automatic detection. | +| `assetKey` | string, optional | Google DAI asset key. | +| `networkCode` | string, optional | Organization Google DAI network-code snapshot. | +| `customAssetKey` | string, optional | Channel Google DAI custom-asset-key snapshot. | +| `daiAssetKeys` | string array, optional | Deduplicated SSAI DAI asset-key snapshot. | +| `errorMessage` | string, optional | Failure reason when status is `ERROR`. | +| `timebase` | `wallclock` or `pts` | Required; copied from the channel. | +| `startWallclock` | Date, optional | Wallclock start for wallclock channels. | +| `startPts` | number, optional | Numeric PTS start for PTS channels. | +| `duration` | number | Required duration in seconds. | +| `variantFormats` | string array, optional | Denormalized variant-format index. | +| `assetTypes` | string array, optional | Denormalized asset-type index. | +| `vendors` | string array, optional | Denormalized vendor index. | +| `data` | object | Required raw `BreakData` payload. | +| `createdAt` | Date | Automatically managed creation timestamp. | +| `updatedAt` | Date | Automatically managed modification timestamp. | + +## Related resources + +| Resource | Relationship | +| ---------------------------------- | ---------------------------------------------------------------------------------- | +| [Channels](/ads/concepts/channels) | Parent resource. The channel's timebase determines which start field a Break uses. | +| Events | Time windows that group related Breaks. | +| Templates | Reusable Break definitions merged into a new Break at creation time. | +| Origins | Manifest sources whose detected markers can create Breaks. | +| Marker rules and detection history | Rules and audit records associated with automatically detected Breaks. | +| Integrations | Channel-level delivery integrations, including SSAI DAI cue fan-out. | + +## Scheduling + +### Timebase-dependent starts + +Every Break copies the timebase of its channel: + +| Channel timebase | API `start` value | Stored field | Requirement | +| ---------------- | ------------------------ | ---------------- | ------------------------------------------------------- | +| `wallclock` | ISO 8601 datetime string | `startWallclock` | Optional. Omitting it creates a CUED no-start workflow. | +| `pts` | Non-negative number | `startPts` | Required. | + +The API request field is named `start`; the service maps it to `startWallclock` or `startPts` according to the channel timebase. A PTS channel rejects a missing or non-numeric start. A wallclock channel accepts an omitted start, but a supplied start must be a valid ISO datetime. + +`duration` is required and is expressed in seconds. A scheduled Break cannot overlap another Break on the same channel. Wallclock overlap is evaluated using wallclock instants; PTS overlap is evaluated using PTS values. + +The service also requires a scheduled start to be sufficiently ahead of the current effective playhead. GAM vendor pod Breaks must additionally clear the EABN decisioning margin. + +### Create a Break directly + +Create a Break by supplying its payload and, when required, its `start`: + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "id": "break-2026-001", + "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 + +Supply `templateId` to use a Template as the base. At creation time, the service merges the Template's stored `data` and duration with the request overrides, validates the result, and snapshots the resolved payload into the new Break's `data`. Later Template edits do not change an existing Break. + +Supported creation overrides are: + +- `id` +- `eventId` +- `start` +- `duration` +- `variant` +- `assetParameters` + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/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": "break-from-template-001", + "start": "2026-07-16T12:20:00.000Z", + "eventId": "event-2026-final", + "assetParameters": { + "airingId": "airing-001" + } + }' +``` + +### Create under an Event + +An `eventId` must identify an Event belonging to the same organization and channel. For wallclock channels, the Break must satisfy all of these conditions: + +- `start >= event.startDate` +- `start <= event.endDate` +- `start + duration <= event.endDate` + +PTS channels still require the Event to exist on the same organization and channel, but the service does not compare a numeric PTS start to the Event's wallclock window. + +### Scheduling constraints + +The API rejects starts that are too close to, or behind, the effective playhead. It also rejects any overlap with an existing Break on the channel. These checks apply to direct and Template-based creation. + +## Lifecycle + +The exact Break status values are: + +```text +PREPARING +CUED +READY +SIGNALED +ERROR +``` + +`errorMessage` contains the human-readable reason when a Break is moved to `ERROR`. + +### Initial status + +| Break kind | Start supplied? | Initial status | +| ---------------------------------------------------------------- | --------------- | -------------- | +| GAM vendor pod (`vendor: "gam"`, `vendorParameters.type: "pod"`) | Either | `PREPARING` | +| Non-vendor Break | Yes | `READY` | +| Non-vendor Break on a wallclock channel | No | `CUED` | + +![Break lifecycle diagram](../assets/img/breaks/break-lifecycle.svg) + +### Status transitions and owners + +| Transition | Owner | Behavior | +| ------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `PREPARING → CUED` | Worker / EABN | After Google DAI decisioning, a Break without a timebase-appropriate start becomes CUED. | +| `PREPARING → READY` | Worker / EABN | After Google DAI decisioning, a Break with a timebase-appropriate start becomes READY. | +| `PREPARING → ERROR` | Worker / health worker | A missed unsignaled Break is failed with `Break passed its scheduling window before it could be signaled`. | +| `CUED → READY` | API punch | Punching assigns `startWallclock` and makes the Break eligible for delivery. | +| `READY → SIGNALED` | Manifest service | The Break Manifest includes READY and SIGNALED Breaks, then changes returned READY Breaks to SIGNALED. | +| `READY → SIGNALED` | Proxy | After injecting HLS cues, the proxy changes the injected READY Breaks to SIGNALED. The update is scoped to READY and is idempotent. | + +The worker can also reset a superseded active Google Break from `READY` or `SIGNALED` back to `PREPARING` when it is still outside the decision margin. + +## Cue and punch workflow + +Vendor pod Breaks can be prepared before their exact start is known: + +1. Create a GAM vendor pod Break without a start on a wallclock channel. It starts in `PREPARING`. +2. The worker/EABN service pre-decides the Break with Google DAI. +3. After decisioning, the Break receives a `podId` and becomes `CUED`. +4. Punch the Break when it should fire. Punching sets `startWallclock` and changes the status to `READY`. + +Only wallclock channels support punch. A GAM CUED Break must have a `podId` from EABN decisioning before it can be punched. The application allows only one no-start Break in `PREPARING` or `CUED` per channel; creating another one fails. + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks/gam-cued-001/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" + }' +``` + +If the body is omitted, the punch uses the current time. A requested past time is clamped to now. + +## Break payload (`data`) + +The stored `data` object has this shape: + +```ts +type BreakData = { + duration: number; // required, seconds, >= 0 + resumeOffset?: number; // seconds, >= 0 + controls?: { + skipOffset?: number; // seconds, >= 0 + snapback?: boolean; + }; + variant: BreakVariant | BreakVariant[]; // one object or a non-empty array +}; +``` + +| Field | Type | Description | +| --------------------- | ----------------- | -------------------------------------------------------------- | +| `duration` | number | Required Break duration in seconds. | +| `resumeOffset` | number, optional | Resume offset in seconds. | +| `controls.skipOffset` | number, optional | Minimum elapsed time before skipping is allowed. | +| `controls.snapback` | boolean, optional | Enables snapback behavior. | +| `variant` | object or array | One layout variant, or a non-empty array of targeted variants. | + +## Layouts and variants + +This is the canonical V2 layout and variant reference. Templates use the same payload model and should refer to this section rather than duplicate the layout definitions. + +![OptiView Ads format overview](../assets/img/ads_formats.svg) + +### Asset model + +Every asset has these common fields: + +```ts +{ + id: string; + mediaType: "video" | "image"; + mimeType?: string; + duration?: number; + interaction?: { + clickThrough?: string; + }; +} +``` + +`id` is generated as a UUID when omitted. Asset `type` is one of: + +| `type` | Fields | +| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `static` | `uri`: a URL string or an array of `{ value, targeting? }` objects. | +| `vast` | `uri`: a URL string or an array of `{ value, targeting? }` objects. | +| `vendor` | `vendor: "gam"`, `vendorParameters`, optional `assetParameters`, and `uri`. GAM vendor parameters require `type: "pod"`. The default `uri` is `"placeholder"`. | + +For URI arrays, each entry can include optional device targeting: + +```ts +{ + value: string; + targeting?: { + deviceType?: "desktop" | "tablet" | "mobile" | "tv"; + }; +} +``` + +### `single` + +![Single format](../assets/img/breaks/format-single.svg) + +The `single` variant contains a non-empty plain `assets` array: + +```ts +{ + format: "single"; + targeting?: { deviceType?: "desktop" | "tablet" | "mobile" | "tv" }; + assets: Asset[]; +} +``` + +Use a full-screen creative. Optimize the asset size for the player and supply companion imagery separately when the player experience requires it. + +### `double` + +![Double format](../assets/img/breaks/format-double.svg) + +The `double` variant contains a non-empty array in which every entry has a primary asset and a `companion` asset: + +```ts +{ + format: "double"; + targeting?: { deviceType?: "desktop" | "tablet" | "mobile" | "tv" }; + assets: Array; +} +``` + +Use 16:9 companion imagery where possible. The double box is unsupported on many smart TVs; provide a single-format fallback for those devices. + +### `lshape_ad` + +![L-shape ad format](../assets/img/breaks/format-lshape-ad.svg) + +The `lshape_ad` variant uses the same companion-bearing asset shape as `double`: + +```ts +{ + format: "lshape_ad"; + targeting?: { deviceType?: "desktop" | "tablet" | "mobile" | "tv" }; + assets: Array; +} +``` + +The ad occupies the smaller window and the companion asset supplies the remaining backdrop. Use 16:9 companion imagery and optimize image dimensions for the target player. + +### `lshape_content` + +![L-shape content format](../assets/img/breaks/format-lshape-content.svg) + +The `lshape_content` variant uses a non-empty plain asset array: + +```ts +{ + format: "lshape_content"; + targeting?: { deviceType?: "desktop" | "tablet" | "mobile" | "tv" }; + assets: Asset[]; +} +``` + +The live content occupies the smaller window and the remaining area is supplied by the layout's companion/backdrop treatment. + +### `overlay` + +![Overlay format](../assets/img/breaks/format-overlay.svg) + +The `overlay` variant uses a non-empty plain asset array plus required position and size objects: + +```ts +{ + format: "overlay"; + targeting?: { deviceType?: "desktop" | "tablet" | "mobile" | "tv" }; + assets: Asset[]; + position: { + top?: number; + bottom?: number; + left?: number; + right?: number; + }; + size: { + width: number; + height: number; + }; + opacity?: number; +} +``` + +`position` requires at least one of `top` or `bottom` and at least one of `left` or `right`. All position and size values are fractions from `0` through `1`, not percentages. `opacity`, when supplied, is also a fraction from `0` through `1`. + +### Multiple variants and device targeting + +Set `variant` to an array when one Break contains multiple layouts for different devices. Each variant can have an optional `targeting.deviceType` value: + +```json +{ + "duration": 30, + "variant": [ + { + "format": "single", + "targeting": { + "deviceType": "mobile" + }, + "assets": [ + { + "type": "static", + "mediaType": "video", + "uri": "https://cdn.example.com/ads/mobile.m3u8" + } + ] + }, + { + "format": "single", + "targeting": { + "deviceType": "tv" + }, + "assets": [ + { + "type": "static", + "mediaType": "video", + "uri": "https://cdn.example.com/ads/tv.m3u8" + } + ] + }, + { + "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": "double", + "targeting": { + "deviceType": "tablet" + }, + "assets": [ + { + "type": "static", + "mediaType": "video", + "uri": "https://cdn.example.com/ads/tablet.m3u8", + "companion": { + "type": "static", + "mediaType": "image", + "uri": "https://cdn.example.com/ads/tablet-companion.jpg" + } + } + ] + } + ] +} +``` + +## Delivery overview + +### Break Manifest polling + +The Manifest Service returns `READY` and `SIGNALED` Breaks that remain within the channel's DVR window. It changes returned `READY` Breaks to `SIGNALED` and emits the timebase-specific start, duration, controls, resume offset, and variant data. Players poll the Manifest according to the channel's advertised idle and active polling intervals. + +### SSAI cue injection + +For wallclock GAM pod Breaks on channels with an SSAI DAI integration, the Proxy injects HLS `EXT-X-DATERANGE` OUT and IN cues into the media playlist. After the cues are written, it changes the injected Breaks from `READY` to `SIGNALED`. + +`PREPARING`, `CUED`, and `ERROR` Breaks are not delivered through either path. + +## API usage + +All examples use the same organization-scoped Basic authentication as the Channels API. + +### Create directly + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "id": "break-api-001", + "start": "2026-07-16T12:30:00.000Z", + "duration": 60, + "controls": { + "skipOffset": 10, + "snapback": false + }, + "variant": { + "format": "single", + "assets": [ + { + "type": "vast", + "mediaType": "video", + "uri": "https://ads.example.com/vast/creative-001.xml" + } + ] + } + }' +``` + +### Create from a Template + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "templateId": "template-sports-spot", + "start": "2026-07-16T12:31:00.000Z", + "duration": 45 + }' +``` + +### List Breaks + +```bash +curl 'https://ads.example.com/api/v1/channels/sports-main/breaks?page=1&pageSize=20' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +`page` defaults to `1`; `pageSize` defaults to `20` and has a maximum of `100`. Lists also accept the optional RSQL `filter` and `sort` parameters. + +Filterable fields are: + +```text +wallclock, assetType, format, eventId, templateId, duration, status, originId +``` + +Sortable fields are: + +```text +wallclock, duration, status, createdAt +``` + +Filter by one status: + +```bash +curl 'https://ads.example.com/api/v1/channels/sports-main/breaks?filter=status==READY' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +Filter by either READY or SIGNALED: + +```bash +curl 'https://ads.example.com/api/v1/channels/sports-main/breaks?filter=status=in=(READY,SIGNALED)' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +The `/active` and `/current` variants are also available: + +```text +GET /api/v1/channels/{channelId}/breaks/active +GET /api/v1/channels/{channelId}/breaks/current +``` + +### Get one Break + +```bash +curl 'https://ads.example.com/api/v1/channels/sports-main/breaks/break-api-001' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +### Delete one Break + +```bash +curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main/breaks/break-api-001' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +### Bulk delete Breaks + +```bash +curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main/breaks' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "ids": ["break-api-001", "break-api-002"] + }' +``` + +### Punch a CUED Break + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks/gam-cued-001/punch' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "start": "2026-07-16T12:35:00.000Z" + }' +``` + +## See also + +- [Channels](/ads/concepts/channels) — channel timebases, polling policy, origins, marker detection, and delivery integrations. +- Templates — reusable Break definitions and Template-based scheduling. +- Events — event windows and event-scoped Breaks. +- Marker Detection — automatic marker evaluation and Break provenance. +- Vendors and Google DAI — vendor pod decisioning and delivery. From dc9bc4616051440ba2da885c29955af77c2c5f5e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 14:19:57 +0000 Subject: [PATCH 05/22] ADS-116 document ad templates Co-Authored-By: maarten.rimaux --- ads/concepts/templates.mdx | 272 +++++++++++++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 ads/concepts/templates.mdx diff --git a/ads/concepts/templates.mdx b/ads/concepts/templates.mdx new file mode 100644 index 000000000000..b1e2fea2538d --- /dev/null +++ b/ads/concepts/templates.mdx @@ -0,0 +1,272 @@ +--- +sidebar_position: 3 +sidebar_label: Templates +--- + +# Templates + +import RebrandingNotice from '../callouts/_rebranding_notice.md'; + + + +A template is a reusable break preset for OptiView Ads. It stores a break payload once so you can schedule consistent breaks quickly, either manually from the dashboard and API or automatically through marker rules. + +Templates are scoped to an organization. API calls identify the organization with the `X-Org-ID` header and authenticate with an API key and secret using HTTP Basic authentication. + +## Dashboard path + +In the OptiView Unified Dashboard, templates are available in two places: + +| Path | Use it for | +| ------------------------------------------------------ | ----------------------------------------------------- | +| `/{organizationId}/ads/templates` | Manage every template in the organization. | +| `/{organizationId}/ads/channels/{channelId}/templates` | Manage the templates surfaced for a specific channel. | + +Both lists expose **New**, **Edit**, and **Delete** actions, plus a **Schedule now** action that immediately schedules a break on the channel from the selected template. + +## Template identity + +Every template has a customer-facing `id`. The API stores it together with the organization ID, so the unique identity is: + +```text +organizationId + templateId +``` + +Use stable template IDs that match your operational names, such as `midroll-30s` or `sponsor-lshape`. If you omit `id` on creation, the API generates one. + +## What a template contains + +A template holds the same payload as a break's `data`, so anything you can express on a break you can preset on a template: + +- `variant` — one variant, or a list of variants with device `targeting`, using the same variant formats (`single`, `double`, `lshape_ad`, `lshape_content`, `overlay`) and typed assets as a break. +- `resumeOffset` and `controls` (skip offset, snapback) — optional playback behaviour. +- `duration` — optional on a template (it is required on a break). When set, it is copied onto breaks scheduled from the template. + +The **Breaks** section is the canonical reference for variant formats, layouts, typed assets, and device targeting. This section cross-links there instead of repeating those details. + +Templates can also record associations that make them easier to organize and surface: + +| Field | Relationship | +| ------------ | ------------------------------------------------------------------------------------------ | +| `channelIds` | Channels the template is associated with (for example, in the per-channel dashboard list). | +| `eventIds` | **Events** the template is associated with. | + +## Snapshot semantics + +A template is a preset, not a live link. When a break is scheduled from a template: + +1. The template's payload 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 payload. + +Templates are **hard-deleted**. Deleting a template removes it permanently; there is no soft-delete or archival state. Breaks previously created from the template are unaffected and still report their historical `templateId`, but that `templateId` no longer resolves to a template, and listing breaks by a deleted template returns a not-found error. + +## Scheduling a break from a template + +You can schedule a break from a template in three ways: + +- **Dashboard** — use the **Schedule now** action on a template in either template list to create a break on the channel immediately. +- **API** — create a break on a channel and reference the template with `templateId` (see [Schedule a break from a template](#schedule-a-break-from-a-template) below). +- **Marker rules** — each marker rule targets a template through its `templateId`. When automatic detection matches a marker, the worker schedules a break from that template. See the **Marker Detection** section for how rules are configured and evaluated. + +In every case the template payload is snapshotted onto the resulting break, as described in [Snapshot semantics](#snapshot-semantics). + +## Configuration reference + +| Field | Type | Default | Description | +| -------------- | -------------------- | --------- | ----------------------------------------------------------------------------------- | +| `id` | string | generated | Customer-facing template ID, unique within the organization. | +| `name` | string | none | Human-readable label shown in the dashboard. | +| `channelIds` | string[] | none | Channels the template is associated with. | +| `eventIds` | string[] | none | Events the template is associated with. | +| `duration` | integer | none | Optional break duration in seconds, copied onto breaks scheduled from the template. | +| `variant` | variant or variant[] | Required | Break variant(s). See the **Breaks** section for formats, assets, and targeting. | +| `resumeOffset` | integer | none | Optional resume offset applied to breaks scheduled from the template. | +| `controls` | object | none | Optional playback controls: `skipOffset` and `snapback`. | + +## Create a template + +Dashboard: **Ads → Templates → New**. + +API: + +```bash +curl -X POST 'https://ads.example.com/api/v1/templates' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "id": "midroll-30s", + "name": "Mid-roll 30s", + "channelIds": ["sports-main"], + "duration": 30, + "variant": { + "format": "single", + "assets": [ + { + "type": "vast", + "mediaType": "video", + "uri": "https://ads.example.com/vast/midroll.xml" + } + ] + } + }' +``` + +Example response: + +```json +{ + "id": "midroll-30s", + "name": "Mid-roll 30s", + "channelIds": ["sports-main"], + "duration": 30, + "variant": { + "format": "single", + "assets": [ + { + "type": "vast", + "mediaType": "video", + "uri": "https://ads.example.com/vast/midroll.xml" + } + ] + }, + "createdAt": "2026-07-16T12:00:00.000Z" +} +``` + +## Get a template + +```bash +curl 'https://ads.example.com/api/v1/templates/midroll-30s' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +## Update a template + +```bash +curl -X PATCH 'https://ads.example.com/api/v1/templates/midroll-30s' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "name": "Mid-roll 30s (VAST)", + "duration": 30 + }' +``` + +Updating a template does not change breaks already scheduled from it — see [Snapshot semantics](#snapshot-semantics). + +## List templates + +```bash +curl 'https://ads.example.com/api/v1/templates?page=1&pageSize=20&sort=-createdAt' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +List endpoints use the same pagination shape as the rest of the API. Templates can be sorted by `name`, `duration`, or `createdAt`. + +| Query parameter | Default | Description | +| --------------- | ------------ | -------------------------------------------------------------------------- | +| `page` | `1` | Page number. | +| `pageSize` | `20` | Items per page. Maximum `100`. | +| `filter` | none | Optional RSQL filter expression. | +| `sort` | `-createdAt` | Comma-separated sort fields. Prefix a field with `-` for descending order. | + +Templates maintain denormalized fields derived from their payload so you can filter without inspecting the full `variant`. The `filter` expression accepts these selectors: + +| Filter selector | Matches on | Operators | +| --------------- | --------------------------------------- | ------------------------------------------ | +| `name` | Template name | `==`, `!=`, `=like=`, `=in=` | +| `duration` | Template duration | `==`, `!=`, `=gt=`, `=ge=`, `=lt=`, `=le=` | +| `format` | Variant formats present on the template | `==`, `!=`, `=like=`, `=in=` | +| `assetType` | Asset types present on the template | `==`, `!=`, `=like=`, `=in=` | +| `vendor` | Vendors present on the template | `==`, `!=`, `=like=`, `=in=` | + +Examples: + +```bash +# Overlay templates only +curl 'https://ads.example.com/api/v1/templates?filter=format==overlay' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +```bash +# Short VAST templates (30s or less), sorted by duration +curl 'https://ads.example.com/api/v1/templates?filter=duration=le=30;assetType==vast&sort=duration' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +```bash +# Templates that use a vendor asset (for example, Google Ad Manager pods) +curl 'https://ads.example.com/api/v1/templates?filter=vendor==gam' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +Combine multiple conditions with `;`. + +## Delete a template + +```bash +curl -X DELETE 'https://ads.example.com/api/v1/templates/midroll-30s' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +Delete multiple templates in one request: + +```bash +curl -X DELETE 'https://ads.example.com/api/v1/templates' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ "ids": ["midroll-30s", "sponsor-lshape"] }' +``` + +Deletes are permanent (hard delete). Existing breaks scheduled from the template are not affected — see [Snapshot semantics](#snapshot-semantics). + +## Schedule a break from a template + +Create a break on a channel and reference the template with `templateId`. The template payload is snapshotted onto the break at creation. + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "templateId": "midroll-30s", + "start": "2026-07-16T13:00:00.000Z" + }' +``` + +`templateId` is the only required field. You can override the snapshotted payload per break with optional fields — `start`, `duration`, `variant`, `assetParameters`, `eventId`, and `id`. Start semantics depend on the channel timebase; see the **Channels** and **Breaks** sections for scheduling and lifecycle details. + +The created break records the source `templateId` alongside its own copied payload: + +```json +{ + "id": "b_9f2c", + "channelId": "sports-main", + "templateId": "midroll-30s", + "status": "PREPARING", + "start": "2026-07-16T13:00:00.000Z", + "duration": 30, + "variant": { + "format": "single", + "assets": [ + { + "type": "vast", + "mediaType": "video", + "uri": "https://ads.example.com/vast/midroll.xml" + } + ] + }, + "createdAt": "2026-07-16T12:30:00.000Z" +} +``` From 7d868de1e160c901701b3263419c8643f7ddc4c3 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 14:20:54 +0000 Subject: [PATCH 06/22] ADS-114 document ad events Co-Authored-By: maarten.rimaux --- ads/concepts/events.mdx | 272 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 ads/concepts/events.mdx diff --git a/ads/concepts/events.mdx b/ads/concepts/events.mdx new file mode 100644 index 000000000000..f016bfad886d --- /dev/null +++ b/ads/concepts/events.mdx @@ -0,0 +1,272 @@ +--- +sidebar_position: 2 +sidebar_label: Events +--- + +# Events + +import RebrandingNotice from '../callouts/_rebranding_notice.md'; + + + +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 operational cue/punch workflow. Ahead of a live occurrence you prepare vendor pod breaks under the event without a start time, and during the broadcast you fire them at the exact moment with the punch endpoint. + +Events are scoped to an organization and a channel. API calls identify the organization with the `X-Org-ID` header and authenticate with an API key and secret using HTTP Basic authentication. + +## Dashboard path + +In the OptiView Unified Dashboard, open **Ads → Channels**, open a channel, then select **Events** from the channel navigation. From there you can create, edit, and delete events, and inspect the breaks scheduled under each event. + +## Event identity + +Every event has an `id`. The API stores it together with the organization ID and the parent channel ID, so the unique identity is: + +```text +organizationId + channelId + eventId +``` + +Because the identity includes the channel, the same `id` can exist under different channels. Use stable, descriptive event IDs that match your operational names, such as `finals-2026` or `week-1-home`. If you omit `id` on creation, the API generates one. + +## Time window + +An event is defined by a `startDate` and an `endDate`, both UTC ISO 8601 timestamps. `startDate` must be before `endDate`. + +The window is enforced on the breaks scheduled under the event. When you create a break with an explicit start on a `wallclock` channel and attach it to an event, the API validates that the **entire** break interval fits 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`. + +If either check fails, the create request is rejected: + +| Condition | Response | +| --------------------------------- | ----------------------------------------------------------------------------- | +| Break start is outside the window | `Break start must be within the event's date range ( - )` | +| Break end is outside the window | `Break end time (start + duration) must be within the event's date range (…)` | + +The window validation applies to `wallclock` channels. On `pts` channels the event must still exist, but the break is not range-checked against the event dates. See [Channels](./channels.mdx) for the timebase model. + +A break created **without** a start time (a cued break, see below) is not range-checked at creation, because its start is not known yet. Its start is set when you punch it. + +## Relationship to breaks + +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 cascades to every break whose `eventId` matches it. The event and its member breaks are removed together in a single transaction. Bulk-deleting events removes the breaks of all deleted events. There is no confirmation step in the API — delete an event only after confirming that none of its breaks are still needed. +::: + +To list only the breaks that belong to an event, use the event's breaks endpoint (see [API usage](#api-usage)). + +## Cue / punch workflow during an event + +For a live occurrence you usually do not know the exact break times in advance, but you want the ad decision ready so the break can fire instantly. Events are where this "prepare ahead, fire live" workflow lives. The full break state machine and the Google DAI (vendor pod) prerequisites are documented in the Breaks and Vendors / Google sections; the flow below focuses on running an event. + +### Ahead of the event: prepare cued breaks + +Create the vendor pod breaks under the event **without a `start`**. A vendor pod break with no start begins in `PREPARING`: OptiView Ads asks Google DAI to pre-decision the pod. Once the pod is decisioned, the break transitions to `CUED` and is ready to fire. + +A channel can hold **only one cued break at a time**. While a no-start break is `PREPARING` or `CUED` on a channel, creating another no-start break on the same channel is rejected: + +```text +Channel already has a CUED break +``` + +Punch (or delete) the outstanding break before cueing the next one. + +### During the event: punch the cued break + +When the moment arrives, fire the cued break with the punch endpoint: + +```text +POST /api/v1/channels/:channelId/breaks/:breakId/punch +``` + +The request body is optional. It may contain a single `start` (UTC ISO 8601). If `start` is omitted it defaults to now, and a `start` in the past is clamped to now. A successful punch sets the break's start and transitions it from `CUED` to `READY`, after which it is delivered. + +Punching has these constraints: + +- **Wallclock only.** The channel must use the `wallclock` timebase. Punching a `pts` channel is rejected with `Only channels with a 'wallclock' timebase are allowed to punch breaks.` +- **Must be cued.** The break must be in `CUED` status; otherwise the request fails with `Break '' is not in CUED status`. +- **Pod must be decisioned.** For a vendor pod break, the Google DAI pod decision must have completed (the break must have left `PREPARING`); otherwise the request fails with `Ad break '' is not yet decisioned by EABN`. + +Because a punch clamps the start to the current time, punch a cued break only while the event is in progress. This keeps the break's start inside the event's `startDate`/`endDate` window. + +### Worked example: half-time break in a live game + +1. Create the event for the game with a window that covers kickoff through the final whistle. +2. Ahead of kickoff, create a vendor pod break under the event with no `start`. It enters `PREPARING`, then `CUED` once Google DAI has pre-decisioned the pod. +3. At half-time, punch the break with no body. Its start is set to now and it transitions to `READY`, so the pod is delivered immediately. +4. To prepare the next in-game break, first punch or delete the current cued break, then cue the next one — a channel holds only one cued break at a time. + +## Relationship to templates + +Templates can be linked to one or more events through their `eventIds` array, so a reusable break preset can be surfaced for quick scheduling under those events. Listing templates for an event returns every template whose `eventIds` contains the event's `id`. + +## Configuration reference + +| Field | Type | Required | Description | +| ------------- | --------------- | -------- | ----------------------------------------------------------- | +| `id` | string | No | Customer-facing event ID. Generated if omitted on creation. | +| `name` | string | Yes | Human-readable event name. Must be non-empty. | +| `description` | string | No | Optional free-text description. | +| `startDate` | ISO 8601 string | Yes | Start of the event window. Must be before `endDate`. | +| `endDate` | ISO 8601 string | Yes | End of the event window. | + +The event response returns `id`, `name`, `description`, `startDate`, `endDate`, and `createdAt`. The organization and channel IDs are taken from the request context and are not part of the response body. + +## API usage + +Events live under a channel. Replace `sports-main` with your channel ID. + +### Create an event + +Dashboard: open the channel, then **Events → New**. + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/events' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "id": "finals-2026", + "name": "Finals 2026", + "description": "Championship final", + "startDate": "2026-07-20T18:00:00.000Z", + "endDate": "2026-07-20T22:00:00.000Z" + }' +``` + +Example response: + +```json +{ + "id": "finals-2026", + "name": "Finals 2026", + "description": "Championship final", + "startDate": "2026-07-20T18:00:00.000Z", + "endDate": "2026-07-20T22:00:00.000Z", + "createdAt": "2026-07-16T12:00:00.000Z" +} +``` + +### Get an event + +```bash +curl 'https://ads.example.com/api/v1/channels/sports-main/events/finals-2026' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +### Update an event + +`id` cannot be changed. When you send `startDate` or `endDate`, the resulting window must still keep `startDate` before `endDate`. + +```bash +curl -X PATCH 'https://ads.example.com/api/v1/channels/sports-main/events/finals-2026' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "name": "Finals 2026 (delayed)", + "endDate": "2026-07-20T23:00:00.000Z" + }' +``` + +### List events + +```bash +curl 'https://ads.example.com/api/v1/channels/sports-main/events?page=1&pageSize=20&sort=-createdAt' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +List endpoints share the same pagination shape. Events can be filtered by `name`, `description`, `startDate`, and `endDate`, and sorted by `name`, `description`, `startDate`, `endDate`, or `createdAt`. + +| Query parameter | Default | Description | +| --------------- | ------------ | -------------------------------------------------------------------------- | +| `page` | `1` | Page number. | +| `pageSize` | `20` | Items per page. Maximum `100`. | +| `filter` | none | Optional RSQL filter expression. | +| `sort` | `-createdAt` | Comma-separated sort fields. Prefix a field with `-` for descending order. | + +### List breaks for an event + +```bash +curl 'https://ads.example.com/api/v1/channels/sports-main/events/finals-2026/breaks?pageSize=50' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +### Delete an event + +```bash +curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main/events/finals-2026' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +This also deletes every break attached to the event. To delete several events (and their breaks) at once, send their IDs to the collection endpoint: + +```bash +curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main/events' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ "ids": ["finals-2026", "semifinal-2026"] }' +``` + +### Cue and punch a break in an event context + +Cue a vendor pod break under the event by omitting `start`: + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "id": "halftime-1", + "eventId": "finals-2026", + "duration": 90, + "variant": { + "format": "single", + "assets": [ + { + "type": "vendor", + "vendor": "gam", + "mediaType": "video", + "vendorParameters": { "type": "pod" } + } + ] + } + }' +``` + +At the right moment, punch it. With no body the start defaults to now: + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks/halftime-1/punch' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +To punch with an explicit start (a past start is clamped to now): + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks/halftime-1/punch' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ "start": "2026-07-20T20:00:00.000Z" }' +``` + +## Related resources + +| Resource | Relationship | +| ---------------- | ----------------------------------------------------------------------------------------------- | +| Channels | The parent of an event. An event always belongs to one channel. See [Channels](./channels.mdx). | +| Breaks | Attached to an event via `eventId`; the Breaks section documents the full status state machine. | +| Templates | Linked to events via `eventIds` for quick scheduling. | +| Vendors / Google | Provide the pod pre-decisioning that moves a cued vendor pod break from `PREPARING` to `CUED`. | From 6594233a650d285f01a6de8d5504ec81be4d3666 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 14:21:54 +0000 Subject: [PATCH 07/22] ADS-115 document marker detection Co-Authored-By: maarten.rimaux --- ads/concepts/marker-detection.mdx | 303 ++++++++++++++++++++++++++++++ ads/concepts/origins.mdx | 164 ++++++++++++++++ 2 files changed, 467 insertions(+) create mode 100644 ads/concepts/marker-detection.mdx create mode 100644 ads/concepts/origins.mdx diff --git a/ads/concepts/marker-detection.mdx b/ads/concepts/marker-detection.mdx new file mode 100644 index 000000000000..d9baf65bfad1 --- /dev/null +++ b/ads/concepts/marker-detection.mdx @@ -0,0 +1,303 @@ +--- +sidebar_position: 3 +sidebar_label: Break Detection +--- + +# Break Detection + +import RebrandingNotice from '../callouts/_rebranding_notice.md'; + + + +Automatic marker detection turns ad markers found in an origin manifest into breaks by applying marker rules. Detection runs per channel when it is enabled. V2 detection currently supports HLS manifests only. See [Origins](./origins.mdx) for origin selection, priority ordering, and first-online behavior. + +Channels are scoped to an organization. API calls identify the organization with the `X-Org-ID` header and authenticate with an API key and secret using HTTP Basic authentication. + +## Dashboard path + +Open the channel and select **Break Detection**. Use this area to configure marker rules and review **Detection history**. + +Marker rules can be toggled with **Enable marker rule** and **Disable marker rule**. These Dashboard actions use the marker-rule update endpoint with the `enabled` field; there are no dedicated marker-rule enable or disable endpoints. + +## Detection lifecycle + +`detectionEnabled` is read-only on channel create and update requests. Toggle automatic detection with the dedicated channel actions: + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/detection/enable' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/detection/disable' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +When detection is enabled, a scheduler polls the channel's enabled origins in priority order. The first online origin is selected for the cycle. The worker parses its markers, evaluates enabled marker rules, creates breaks for matching markers, and records the result in Detection history. + +## Supported markers + +V2 marker detection supports HLS only. It recognizes two marker kinds: + +| Marker rule type | HLS marker | Detection behavior | +| ---------------- | ------------------ | ------------------------------------------------------------------------------------------------- | +| `CUE` | `#EXT-X-CUE-OUT` | Parses a marker start and optional duration. `CUE-IN` and `CUE-SPAN` are ignored. | +| `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. The rule's `type` must match the marker kind, and every configured condition must match the marker attributes. Attribute keys are compared case-insensitively. + +### Configuration reference + +| Field | Type | Default | Description | +| ----------------- | -------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| `streamType` | enum | Required | Currently only `HLS` is supported. | +| `type` | enum: `CUE` or `DATERANGE` | Required | Marker kind this rule matches. The value must be valid for the selected `streamType`. | +| `conditions` | object (map of string to string) | Required | 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` | string | Required | Non-empty ID of the break template to instantiate. The template must exist and be available to the channel. | +| `assetParameters` | object (map of string to string) | none | Optional parameters merged into the created break body, such as ad-targeting parameters passed downstream. | +| `enabled` | boolean | `true` | 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": "preroll-30s", + "assetParameters": { "adType": "midroll" }, + "enabled": true +} +``` + +There is no dedicated marker-rule enable or disable endpoint. The Dashboard **Enable marker rule** / **Disable marker rule** actions map to a normal update: + +```bash +curl -X PATCH 'https://ads.example.com/api/v1/channels/sports-main/markerRules/rule-123' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ "enabled": false }' +``` + +## Marker rule endpoints + +All marker-rule endpoints are scoped to a channel: + +| Operation | Method | Path | +| ----------- | -------- | ------------------------------------------------------- | +| List | `GET` | `/api/v1/channels/:channelId/markerRules` | +| Get | `GET` | `/api/v1/channels/:channelId/markerRules/:markerRuleId` | +| Create | `POST` | `/api/v1/channels/:channelId/markerRules` | +| Update | `PATCH` | `/api/v1/channels/:channelId/markerRules/:markerRuleId` | +| Delete | `DELETE` | `/api/v1/channels/:channelId/markerRules/:markerRuleId` | +| Bulk delete | `DELETE` | `/api/v1/channels/:channelId/markerRules` | + +### Create a marker rule + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/markerRules' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "streamType": "HLS", + "type": "DATERANGE", + "conditions": { "CLASS": "com.example.ad" }, + "templateId": "preroll-30s", + "assetParameters": { "adType": "midroll" }, + "enabled": true + }' +``` + +Example response: + +```json +{ + "id": "rule-123", + "streamType": "HLS", + "type": "DATERANGE", + "conditions": { "CLASS": "com.example.ad" }, + "templateId": "preroll-30s", + "assetParameters": { "adType": "midroll" }, + "enabled": true, + "createdAt": "2026-07-16T12:00:00.000Z" +} +``` + +### Update a marker rule + +Use the same endpoint to change rule configuration or enable/disable participation: + +```bash +curl -X PATCH 'https://ads.example.com/api/v1/channels/sports-main/markerRules/rule-123' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "conditions": { "CLASS": "com.example.ad", "X-CAMPAIGN": "sports" }, + "enabled": true + }' +``` + +### List marker rules + +```bash +curl 'https://ads.example.com/api/v1/channels/sports-main/markerRules?page=1&pageSize=20&sort=-createdAt' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +List endpoints use the shared pagination shape: + +| Query parameter | Default | Description | +| --------------- | ------------ | -------------------------------------------------------------------------- | +| `page` | `1` | Page number. | +| `pageSize` | `20` | Items per page. Maximum `100`. | +| `filter` | none | Optional RSQL filter expression. | +| `sort` | `-createdAt` | Comma-separated sort fields. Prefix a field with `-` for descending order. | + +### Bulk delete marker rules + +```bash +curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main/markerRules' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ "ids": ["rule-123", "rule-456"] }' +``` + +## Detection history + +Detection history is the audit trail of what automatic detection decided for each marker. + +| Operation | Method | Path | +| --------- | ------ | ------------------------------------------------------------------ | +| List | `GET` | `/api/v1/channels/:channelId/detection/history` | +| Get | `GET` | `/api/v1/channels/:channelId/detection/history/:markerDetectionId` | + +### List detection history + +```bash +curl 'https://ads.example.com/api/v1/channels/sports-main/detection/history?page=1&pageSize=20&sort=-createdAt' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +### Detection history response fields + +| Field | Type | Description | +| -------------- | ------ | -------------------------------------------- | +| `id` | string | Detection-history record ID. | +| `originId` | string | Origin that supplied the marker. | +| `markerRuleId` | string | Present when an enabled marker rule matched. | +| `breakId` | string | Present when a break was created. | +| `action` | enum | `CREATED`, `SKIPPED`, or `FAILED`. | +| `marker` | string | The raw manifest tag line. | +| `reason` | string | Optional machine-readable reason. | +| `createdAt` | string | Creation timestamp. | + +Example response row: + +```json +{ + "id": "detection-789", + "originId": "origin-123", + "markerRuleId": "rule-123", + "breakId": "break-456", + "action": "CREATED", + "marker": "#EXT-X-DATERANGE:ID=\"ad-1\",CLASS=\"com.example.ad\",START-DATE=\"2026-07-16T12:00:00.000Z\",DURATION=30", + "createdAt": "2026-07-16T12:00:01.000Z" +} +``` + +### Action values + +| Action | Meaning | +| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `CREATED` | A rule matched and a break was scheduled. `markerRuleId` and `breakId` are set. | +| `SKIPPED` | No fault: the marker was ineligible because it was unparseable or had no resolvable start; no rule matched; no rules were configured; or an expected scheduling condition prevented creation. | +| `FAILED` | An eligible, rule-matched marker could not be scheduled for an unexpected reason such as misconfiguration, invalid data, or infrastructure failure. | + +Common `reason` values include: + +- `NO_RULES_CONFIGURED` +- `NO_RULE_MATCHED` +- `MARKER_MISSING_START` +- `MARKER_MALFORMED` +- Scheduling-rejection reasons such as `BREAK_START_IN_PAST`, `DECISIONING_MARGIN`, and `BREAK_OVERLAP` + +History is deduplicated per channel. Repeated polling of the same marker, including seeing it on another origin, does not create duplicate rows. + +## 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`. | Create and enable a marker rule for the channel. | +| 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 by the API 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 in this cycle. Check reasons such as `BREAK_START_IN_PAST`, `DECISIONING_MARGIN`, or `BREAK_OVERLAP`. | +| 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. | + +## End-to-end example + +1. Add and enable an HLS origin for `sports-main`. See [Origins](./origins.mdx). + + ```bash + curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/origins' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "name": "Primary HLS origin", + "type": "HLS", + "url": "https://origin.example.com/live/sports-main/master.m3u8", + "enabled": true, + "priority": 0 + }' + ``` + +2. Create a break template and note its ID, such as `preroll-30s`. The marker rule references this value as `templateId`. + +3. Create an enabled marker rule for a matching HLS marker: + + ```bash + curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/markerRules' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "streamType": "HLS", + "type": "DATERANGE", + "conditions": { "CLASS": "com.example.ad" }, + "templateId": "preroll-30s", + "enabled": true + }' + ``` + +4. Enable detection on the channel: + + ```bash + curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/detection/enable' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' + ``` + +5. When the selected origin manifest advertises a matching `#EXT-X-DATERANGE` or `#EXT-X-CUE-OUT` marker, the worker evaluates the rule and creates an automatic break. + +6. Confirm the result in Detection history: + + ```bash + curl 'https://ads.example.com/api/v1/channels/sports-main/detection/history?page=1&pageSize=20&sort=-createdAt' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' + ``` + + A successful detection has `action: "CREATED"` and includes both `markerRuleId` and `breakId`. diff --git a/ads/concepts/origins.mdx b/ads/concepts/origins.mdx new file mode 100644 index 000000000000..fd31c5003488 --- /dev/null +++ b/ads/concepts/origins.mdx @@ -0,0 +1,164 @@ +--- +sidebar_position: 2 +sidebar_label: Origins +--- + +# Origins + +import RebrandingNotice from '../callouts/_rebranding_notice.md'; + + + +An origin is a manifest URL that a channel monitors for ad markers. When automatic marker detection is enabled, the worker 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. + +Origins are scoped to an organization and to a channel. API calls identify the organization with the `X-Org-ID` header and authenticate with an API key and secret using HTTP Basic authentication. + +## Dashboard path + +In the OptiView Unified Dashboard, open the channel and select **Origins** from the channel navigation. From there you can add an origin, edit it, delete it, set its priority, and use **Enable origin** / **Disable origin** to control whether detection considers it. + +## How multiple origins are used + +Only origins with `enabled: true` are considered for detection. Enabled origins are ordered by `priority` ascending, then by creation time. The worker walks that ordered list and uses the **first online origin**: the first one whose manifest is fetched and parsed successfully. + +- **Lowest `priority` value first.** `priority` is an integer; lower values are tried before higher ones. Negative values are allowed, so `-1` is tried before `0`. +- **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 and wins, so lower-priority origins are not consulted in the same cycle. If an origin cannot be fetched or parsed, detection falls back to the next enabled origin in priority order. + +:::note Supported origin types +The API accepts `HLS`, `DASH`, and `HESP` for `type`, but automatic marker detection currently parses **HLS** manifests only. `DASH` and `HESP` origins can be stored and prioritized, but they are skipped by detection today. Use `HLS` for origins you expect to drive automatic breaks. +::: + +## Configuration reference + +| Field | Type | Default | Description | +| ---------- | ------------------------ | -------- | -------------------------------------------------------------------------------------------------------------- | +| `url` | string | Required | Manifest URL to monitor. Must be a valid URL. | +| `type` | `HLS`, `DASH`, or `HESP` | Required | Manifest format. Only `HLS` is parsed by detection today; `DASH` and `HESP` are accepted but not yet detected. | +| `name` | string | none | Optional human-readable label shown in the Dashboard. | +| `enabled` | boolean | `false` | Whether detection considers this origin. Change it with the enable/disable actions, not with an update. | +| `priority` | integer | `0` | Selection order for detection. Lower values are tried first; negative values are allowed. | + +`enabled` cannot be changed through the update endpoint. Use the dedicated enable and disable actions instead. + +## Add an origin + +Dashboard: open the channel, then use **Origins → Add**. + +API: + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/origins' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "name": "Primary HLS origin", + "type": "HLS", + "url": "https://origin.example.com/live/sports-main/master.m3u8", + "enabled": true, + "priority": 0 + }' +``` + +Example response: + +```json +{ + "id": "3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f", + "channelId": "sports-main", + "name": "Primary HLS origin", + "type": "HLS", + "url": "https://origin.example.com/live/sports-main/master.m3u8", + "enabled": true, + "priority": 0, + "createdAt": "2026-07-16T12:00:00.000Z" +} +``` + +Add a lower-priority backup origin so detection can fall back if the primary source is unreachable: + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/origins' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "name": "Backup HLS origin", + "type": "HLS", + "url": "https://backup.example.com/live/sports-main/master.m3u8", + "enabled": true, + "priority": 1 + }' +``` + +## Get an origin + +```bash +curl 'https://ads.example.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +## Update an origin + +The update endpoint accepts `url`, `type`, `name`, and `priority`. It does not accept `enabled`. + +```bash +curl -X PATCH 'https://ads.example.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "name": "Primary HLS origin (HD)", + "priority": 0 + }' +``` + +## Enable or disable an origin + +Dashboard: **Origins → Enable origin** / **Disable origin**. + +API: + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f/enable' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f/disable' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +Disabling an origin removes it from detection immediately. The origin record is kept, so you can re-enable it later without recreating it. + +## List origins + +```bash +curl 'https://ads.example.com/api/v1/channels/sports-main/origins?page=1&pageSize=20&sort=priority' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +List endpoints use the shared pagination shape. + +| Query parameter | Default | Description | +| --------------- | ------------ | -------------------------------------------------------------------------- | +| `page` | `1` | Page number. | +| `pageSize` | `20` | Items per page. Maximum `100`. | +| `filter` | none | Optional RSQL filter expression. | +| `sort` | `-createdAt` | Comma-separated sort fields. Prefix a field with `-` for descending order. | + +## Delete an origin + +```bash +curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'X-Org-ID: org_123' +``` + +## Next steps + +Origins supply the manifests; [marker detection](./marker-detection.mdx) decides which markers in those manifests become breaks. Configure at least one enabled `HLS` origin before enabling detection on the channel. From a542d76c961d0077e1004356ac830d4840bf1430 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 14:22:58 +0000 Subject: [PATCH 08/22] ADS-117 document ad vendors Co-Authored-By: maarten.rimaux --- ads/vendors/google.mdx | 163 +++++++++++++++++++++++++++++++++++++++++ ads/vendors/index.mdx | 67 +++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 ads/vendors/google.mdx create mode 100644 ads/vendors/index.mdx diff --git a/ads/vendors/google.mdx b/ads/vendors/google.mdx new file mode 100644 index 000000000000..d0e6cfff06b5 --- /dev/null +++ b/ads/vendors/google.mdx @@ -0,0 +1,163 @@ +--- +sidebar_position: 2 +sidebar_label: Google Ad Manager +--- + +# Google Ad Manager 360 + +import RebrandingNotice from '../callouts/_rebranding_notice.md'; + + + +Google Ad Manager 360 (GAM 360) is the first supported OptiView Ads vendor. It requires a GAM 360 account with Dynamic Ad Insertion (DAI) and pod serving enabled. + +## Organization configuration + +Google configuration is organization-level and administrator-managed. A Dolby OptiView administrator or account team configures these values; they are not configured through the self-serve Basic API. + +| Field | Type | Required | Effective service default when unset | +| -------------------------------- | ---------------- | --------------------------------------------------------------- | ------------------------------------ | +| `google.networkCode` | string | Optional in the organization schema; required for GAM signaling | None | +| `google.serviceAccountPath` | string | Optional in the organization schema; required for GAM signaling | None | +| `google.eabnLookForwardTimeMs` | positive integer | Optional | `300000` ms | +| `google.eabnDecisioningMarginMs` | positive integer | Optional | `5000` ms | + +The organization-level values override the service defaults. `networkCode` and `serviceAccountPath` must be present before EABN can signal a break. + +## SGAI pod serving + +SGAI is server-guided pod serving keyed by the channel's `customAssetKey`. A `customAssetKey` is unique within an organization when set. See [Channels](../../concepts/channels) for channel configuration. + +A GAM pod break uses a vendor asset with `vendorParameters.type` set to `"pod"`: + +```json +{ + "type": "vendor", + "vendor": "gam", + "uri": "placeholder", + "vendorParameters": { + "type": "pod" + } +} +``` + +### EABN lifecycle + +1. A GAM pod break is created with status `PREPARING`. +2. EABN waits until the look-forward window opens, then signals a Google DAI ad break through the channel's `customAssetKey`. +3. Google returns a `podId`. The vendor asset's `uri` is set to that pod ID. +4. The break becomes `READY` when it has a start time, or `CUED` when it has no start time. +5. For a delivered HLS manifest, the proxy injects the cue and changes `READY` to `SIGNALED`. + +The player then requests the pod manifest using the vendor asset `uri`, which is the decisioned `podId`. + +`google.eabnLookForwardTimeMs` controls when EABN signals a scheduled break: signaling begins when the effective live point reaches `start - eabnLookForwardTimeMs`. Its effective default is `300000` ms. + +`google.eabnDecisioningMarginMs` is the minimum lead time required for decisioning. If `start - effectiveNow` falls below this margin, the break is missed instead of being signaled. Its effective default is `5000` ms. + +### Cue-punch + +A `CUED` break has no start time and waits for a punch before it plays. Punching changes the status from `CUED` to `READY`. A GAM pod break cannot be punched until EABN has decisioned it: + +```text +Ad break '' is not yet decisioned by EABN +``` + +## SSAI_DAI + +`SSAI_DAI` is a channel integration. It carries one or more Google DAI asset keys: + +```json +{ + "type": "SSAI_DAI", + "daiAssetKeys": ["sports-main-1", "sports-main-2"] +} +``` + +Each DAI asset key can be used by at most one channel integration within an organization. Duplicate keys in one request are de-duplicated. A conflict with another channel integration returns HTTP `409`: + +```text +One or more daiAssetKeys are already used by another channel integration +``` + +Create an integration with the self-serve API: + +```bash +curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/integrations' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "type": "SSAI_DAI", + "daiAssetKeys": [ + "sports-main-1", + "sports-main-2" + ] + }' +``` + +### Best-effort fan-out + +At signal time, EABN snapshots the channel integration keys onto the break and fans the break out to each `daiAssetKey` through Google's by-asset-key ad break endpoint. The signals are best-effort: a failure for one key is logged and does not affect the primary signal or the other keys. An SSAI-only break has no `customAssetKey`, is not lifecycle-tracked, and never receives a `podId`. + +### Proxy cue injection + +For an HLS wallclock channel with an `SSAI_DAI` integration, the proxy finds active wallclock GAM pod breaks within the DVR window and injects `EXT-X-DATERANGE` cues into the media playlist. The injected cues contain `SCTE35-OUT` and `SCTE35-IN` data. After injection, the proxy changes the affected breaks from `READY` to `SIGNALED`. + +PTS channels receive passthrough manifests with no cue injection. A channel without an `SSAI_DAI` integration also receives a passthrough manifest with no ad cue injection. + +## Ad targeting parameters + +In V2, a vendor asset's optional `assetParameters` carry ad-tag and targeting parameters. OptiView Ads forwards them to Google as custom parameters during decisioning. + +For the player-side SDK `adTagParameters` usage, see [Ad tag parameters](../../how-to-guides/ad-tag-parameters). The player automatically adds `theoads_slot`. + +## Custom GAM creatives + +Dynamic backdrops and overlays require custom creative templates configured in the GAM console. See [Custom GAM creatives](../../how-to-guides/gam-custom-creatives). + +## Troubleshooting + +### Break status `ERROR` + +A break can be stored with status `ERROR` and: + +```text +Break passed its scheduling window before it could be signaled +``` + +This means the break missed its scheduling window because the remaining time fell below the decisioning margin, or the missed-break health check caught it. Schedule pod breaks at least the decisioning margin ahead of the live point and verify the EABN and Google configuration. + +### GAM configuration error + +Break creation returns HTTP `400` when the organization network code, service-account path, or channel custom asset key is missing: + +```text +Vendor asset of type GAM requires organization.google.networkCode, organization.google.serviceAccountPath and channel.customAssetKey to be configured +``` + +Verify all three values: + +- `organization.google.networkCode` +- `organization.google.serviceAccountPath` +- `channel.customAssetKey` + +### Pod break too close to live + +Break creation returns HTTP `400` when a pod break starts too close to the live point: + +```text +POD ad breaks must start at least ms after the live point to allow time for ad decisioning +``` + +The default `` is `5000`. + +### Break remains `PREPARING` + +If a break never leaves `PREPARING`, EABN may be skipping the signal because the organization is missing `networkCode` or `serviceAccountPath` at signal time. Check the organization Google configuration and confirm that the channel has the required delivery key: `customAssetKey` for SGAI, or an `SSAI_DAI` integration with `daiAssetKeys`. + +## Related resources + +- [Channels](../../concepts/channels) +- [Scheduling breaks](../../how-to-guides/scheduling-breaks) +- [API reference](/ads/api) diff --git a/ads/vendors/index.mdx b/ads/vendors/index.mdx new file mode 100644 index 000000000000..29e8f3c7175d --- /dev/null +++ b/ads/vendors/index.mdx @@ -0,0 +1,67 @@ +--- +sidebar_position: 1 +sidebar_label: Vendors +--- + +# Vendors + +import RebrandingNotice from '../callouts/_rebranding_notice.md'; + + + +A vendor is the ad decisioning or serving integration that OptiView Ads signals breaks to. Google Ad Manager 360 is the first supported vendor. + +Vendors are not standalone REST resources in Ads V2. A break variant carries a **vendor asset**, while vendor configuration is applied at the organization and channel levels. Self-serve channel, break, and integration APIs use HTTP Basic authentication with an API key and secret, plus the `X-Org-ID` header. Organization-level Google configuration is administrator-managed; see [Google Ad Manager](./google). + +## Vendor assets + +An asset with `"type": "vendor"` represents a vendor-delivered ad. The current vendor enum contains only `"gam"`. + +| Field | Type | Required/default | Description | +| ------------------ | ------------------------ | --------------------------- | ------------------------------------------------------------------------------------------------ | +| `type` | string literal | Required: `"vendor"` | Selects the vendor asset type. Other asset types are `"static"` and `"vast"`. | +| `vendor` | enum | Required: `"gam"` | Identifies the ad vendor. | +| `uri` | string | Defaults to `"placeholder"` | Holds the vendor result. For a decisioned GAM pod, it is replaced with the Google `podId`. | +| `vendorParameters` | `Record` | Required | Vendor-specific parameters. GAM assets must include a `type` key whose current value is `"pod"`. | +| `assetParameters` | `Record` | Optional | Ad-tag and targeting parameters forwarded during decisioning. | + +Example GAM pod asset: + +```json +{ + "type": "vendor", + "vendor": "gam", + "uri": "placeholder", + "vendorParameters": { + "type": "pod" + } +} +``` + +## Supported vendors + +| Vendor | Enum value | Delivery | +| --------------------------------- | ---------- | ------------------------------------- | +| [Google Ad Manager 360](./google) | `gam` | SGAI pod serving and SSAI_DAI fan-out | + +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 Vendors sidebar. + +## How vendors relate to the Ads V2 model + +| Resource | Relationship | +| -------------------------------------------- | -------------------------------------------------------------------------------------------- | +| [Channels](../concepts/channels) | Hold the channel-level `customAssetKey` used for Google server-guided pod serving. | +| [Breaks](../how-to-guides/scheduling-breaks) | Carry the vendor asset in a break variant. | +| Templates | Reusable break presets that can be scheduled on channels. See the [API reference](/ads/api). | +| Integrations | Configure channel-level delivery integrations such as `SSAI_DAI` and its `daiAssetKeys`. | + +Vendor assets are validated as part of break and template requests. A GAM vendor asset must use: + +```json +{ + "vendor": "gam", + "vendorParameters": { + "type": "pod" + } +} +``` From a05f03eda4e62c72202617d32c92da4668967ba3 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 14:23:58 +0000 Subject: [PATCH 09/22] ADS-120 document break manifest Co-Authored-By: maarten.rimaux --- ads/concepts/break-manifest.mdx | 231 ++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 ads/concepts/break-manifest.mdx diff --git a/ads/concepts/break-manifest.mdx b/ads/concepts/break-manifest.mdx new file mode 100644 index 000000000000..9818e9155410 --- /dev/null +++ b/ads/concepts/break-manifest.mdx @@ -0,0 +1,231 @@ +--- +sidebar_position: 2 +sidebar_label: Break Manifest +--- + +# Break Manifest + +import RebrandingNotice from '../callouts/_rebranding_notice.md'; + + + +The Break Manifest is the canonical, machine-readable description of the ad breaks that are currently relevant for a [channel](/ads/concepts/channels). It is a small JSON document that the OptiView Player polls on a fixed cadence to learn which breaks to prepare and play. + +The Break Manifest is **side-loaded**: it is served from its own endpoint, separately from the media (HLS/DASH) manifest. The player fetches the media manifest from your CDN as usual and, in parallel, polls the Break Manifest to drive ad break scheduling. This is different from server-side ad insertion (SSAI), where ad cues are injected directly into the media manifest. + +## Side-loading versus SSAI cue injection + +OptiView Ads can deliver break timing to the player in two distinct ways. A channel can use either mechanism depending on how the workflow is integrated. + +| Delivery mechanism | Where the break information lives | Who consumes it | +| ----------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| Side-loaded (this page) | A separate JSON Break Manifest served from a dedicated endpoint. | The OptiView Player, which polls the endpoint and schedules breaks client-side. | +| SSAI cue injection | `#EXT-X-DATERANGE` cues rewritten inline into the proxied HLS media playlist. | Any player that reads the manifest; used for Google DAI server-guided pods. | + +With **side-loading**, the media manifest is untouched: the player merges the break timeline it reads from the Break Manifest with the content timeline it reads from the media manifest. This keeps the media manifest cacheable and lets the player own the ad experience (layout, skip, snapback). + +With **SSAI cue injection**, OptiView Ads proxies the upstream HLS playlist and inserts `#EXT-X-DATERANGE` cues in place. Cue injection applies only to `wallclock` channels that have a Google DAI (`SSAI_DAI`) integration configured, because `#EXT-X-DATERANGE` requires a `START-DATE`, which has no `pts` equivalent. + +## Endpoint + +```text +GET /manifest/v1/:orgId/channels/:channelId +``` + +| Path parameter | Description | +| -------------- | --------------------------------------- | +| `orgId` | The organization that owns the channel. | +| `channelId` | The channel to read breaks for. | + +The Break Manifest 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. It differs from the [Channels](/ads/concepts/channels) management API, which is authenticated. Do not place secrets in the polling URL. + +```bash +curl 'https://ads.example.com/manifest/v1/org_123/channels/sports-main' +``` + +### Responses + +| Status | Meaning | +| ------ | -------------------------------------------------------------------------------------- | +| `200` | The channel exists. Returns the Break Manifest JSON document described below. | +| `404` | No channel with `channelId` exists in the organization. Returns a JSON error envelope. | + +### Caching + +The response carries a `Cache-Control` header so that players and CDNs poll at a rate the channel controls. + +| Case | `Cache-Control` | Source | +| ------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------- | +| `200` (any channel) | `public, max-age=` | The channel's active polling interval (`pollingActiveSeconds`), in seconds. | +| `404` (not found) | `public, max-age=` | A short negative-cache window (default `5` seconds) so a missing channel is not hammered. | + +The `max-age` on a successful response always uses the **active** polling interval, so that a cached copy is never held longer than the shortest polling cadence the channel advertises. Use the `polling` values inside the manifest body (see below) to decide how often to poll; use `Cache-Control` for CDN and HTTP cache behavior. + +## Manifest envelope + +The response body is the Break Manifest envelope. The following descriptions are written from the service `breakManifestSchema`. + +| Field | Type | Description | +| ---------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------- | +| `version` | string | Break Manifest format version. Currently `1.0.0`. Use it to guard against future format changes. | +| `timebase` | `wallclock` or `pts` | The channel timebase. Determines how each break's `start` is expressed (see [Channels → Timebase](/ads/concepts/channels)). | +| `polling` | object | Advertised polling cadence, in seconds. Contains `idle` and `active`. | +| `polling.idle` | integer | Interval to poll at when no break is active (from the channel `pollingIdleSeconds`). | +| `polling.active` | integer | Interval to poll at while a break is active (from the channel `pollingActiveSeconds`). | +| `breaks` | array | The breaks currently relevant for the channel. May be empty. Each entry is described in [Break entries](#break-entries). | + +## Break entries + +Each element of `breaks` describes one ad break. The fields are written from the service break schema. + +| Field | Type | Required | Description | +| -------------- | --------------------------- | :------: | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `id` | string | Yes | Stable identifier of the break, unique within the channel. | +| `start` | ISO 8601 string, or number | Yes | Break start on the channel timebase. A UTC ISO 8601 timestamp when `timebase` is `wallclock`; a numeric presentation timestamp when `timebase` is `pts`. | +| `duration` | number (seconds) | Yes | Length of the break, in seconds. | +| `resumeOffset` | number (seconds) | No | Where content playback resumes relative to the break, in seconds. Omitted when the break does not override the default resume behavior. | +| `controls` | object | No | Playback controls for the break. See [Controls](#controls). | +| `variant` | object, or array of objects | Yes | The ad experience(s) to render for the break. A single variant object, or a non-empty list of variants. See [Variants](#variants). | + +A break only appears once its timebase-specific start is known: `wallclock` breaks require a resolved start timestamp, and `pts` breaks require a numeric start. Breaks missing that value for the channel timebase are not included. + +### Controls + +When present, `controls` refines how the player treats the break. + +| Field | Type | Description | +| ------------ | ---------------- | --------------------------------------------------------------------------------- | +| `skipOffset` | number (seconds) | How long into the break before it becomes skippable. Omit to make it unskippable. | +| `snapback` | boolean | When `true`, the player snaps back to the break-in point after seeking past it. | + +### Variants + +`variant` carries the ad experience. Each variant has a `format` and a set of `assets`; some formats add layout fields. Provide a single variant, or a list when the break offers more than one experience (for example, targeted by device type). + +| `format` | Description | +| ---------------- | --------------------------------------------------------------------------------- | +| `single` | Full-screen ad insertion that replaces the content. | +| `double` | Double Box: content continues alongside the ad and a companion asset. | +| `lshape_ad` | L-shape with the ad in the main area and a companion asset. | +| `lshape_content` | L-shape with content scaled into the main area. | +| `overlay` | Overlay ad positioned and sized over the content (`position`, `size`, `opacity`). | + +## Which breaks are included + +The Break Manifest reflects the breaks that are currently relevant for delivery, not the channel's entire break history. Selection is driven by two channel settings, [`dvrWindowMs` and `liveOffsetMs`](/ads/concepts/channels): + +- A cutoff time is computed as `now − liveOffsetMs − dvrWindowMs`. +- A break is included when its end (its `start` plus `duration`) is at or after that cutoff. This keeps breaks whose window still overlaps the DVR buffer, and keeps upcoming breaks, while dropping breaks that ended before the DVR look-back. +- Only breaks in the `READY` or `SIGNALED` [status](#break-lifecycle) are eligible. Breaks that are still `PREPARING` or `CUED`, or that have `ERROR`, are never exposed. + +`liveOffsetMs` lets a channel account for live latency by shifting the effective "now" backward, so breaks remain visible relative to the live playhead rather than raw server time. `dvrWindowMs` (default `300000`, i.e. 5 minutes) sets how far back the look-back extends. + +## Break lifecycle + +A break moves through a small set of statuses. Two of them are visible in the Break Manifest. + +| Status | In manifest | Meaning | +| ----------- | :---------: | --------------------------------------------------------------------------- | +| `PREPARING` | No | The break is being prepared (for example, awaiting a Google DAI pod asset). | +| `CUED` | No | The break is pre-decisioned and awaiting a confirmed start time. | +| `READY` | Yes | The break is ready to be delivered and is eligible for the manifest. | +| `SIGNALED` | Yes | The break has been served in the Break Manifest at least once. | +| `ERROR` | No | The break failed to prepare and is not delivered. | + +### READY → SIGNALED + +Serving the Break Manifest is what advances a break from `READY` to `SIGNALED`. When a poll includes one or more `READY` breaks, the service returns them in the response **and** transitions them to `SIGNALED` as a side effect of that read. A break that is already `SIGNALED` continues to be returned (while it remains within the DVR window) without any further status change. This makes the first appearance of a break in the manifest the moment it is considered signaled to players. + +## Player polling + +The OptiView Player consumes the Break Manifest by polling the endpoint: + +1. Fetch the Break Manifest for the channel. +2. Read `polling.idle` and `polling.active` (seconds) to set the next poll delay: poll at the `idle` cadence when no break is active, and at the `active` cadence while a break is active. +3. Merge each `break` onto the content timeline using `start` (interpreted with `timebase`) and `duration`, and render the `variant`. +4. Honor `controls` (`skipOffset`, `snapback`) and `resumeOffset` when playing the break and resuming content. + +Because the endpoint sets `Cache-Control` from the channel's active polling interval, a shared cache never serves a manifest older than the fastest advertised cadence. + +## Annotated examples + +### Wallclock channel + +For a channel created with `timebase: wallclock`, each break `start` is a UTC ISO 8601 timestamp. + +```json +{ + "version": "1.0.0", + "timebase": "wallclock", + "polling": { + "idle": 10, + "active": 1 + }, + "breaks": [ + { + "id": "break-1", + "start": "2026-07-16T12:30:00.000Z", // UTC wallclock start of the break + "duration": 30, // seconds + "resumeOffset": 0, // resume content at the break-in point + "controls": { + "skipOffset": 5, // skippable 5s in + "snapback": true // snap back to the break if the viewer seeks past it + }, + "variant": { + "format": "single", + "assets": [ + { + "id": "a1", + "type": "static", + "mediaType": "video", + "uri": "https://cdn.example.com/ads/ad.m3u8" + } + ] + } + } + ] +} +``` + +### PTS channel + +For a channel created with `timebase: pts`, each break `start` is a numeric presentation timestamp on the channel's media clock instead of a wallclock timestamp. The envelope and the rest of each break entry are otherwise identical. + +```json +{ + "version": "1.0.0", + "timebase": "pts", + "polling": { + "idle": 10, + "active": 1 + }, + "breaks": [ + { + "id": "break-9", + "start": 5400000, // numeric PTS start on the channel media clock + "duration": 30, // seconds + "variant": [ + { + "format": "single", // default full-screen experience + "assets": [{ "id": "a1", "type": "vast", "mediaType": "video", "uri": "https://ads.example.com/vast.xml" }] + }, + { + "format": "overlay", // alternative overlay experience + "assets": [{ "id": "a2", "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 + } + ] + } + ] +} +``` + +When `breaks` is empty, the envelope is still returned with the channel `timebase` and `polling` values, and the player keeps polling at the `idle` cadence. + +## Related resources + +- [Channels](/ads/concepts/channels) — the timebase, polling policy (`pollingIdleSeconds`, `pollingActiveSeconds`), and delivery window (`dvrWindowMs`, `liveOffsetMs`) that shape the Break Manifest. +- [Scheduling breaks](/ads/how-to-guides/scheduling-breaks) — how breaks are created and signaled for a channel. +- [Getting started](/ads/getting-started/) — integrating the OptiView Player that polls the Break Manifest. From f8b7cff058ea42d83a798e856b56c8dd508463ee Mon Sep 17 00:00:00 2001 From: "maarten.rimaux" Date: Thu, 30 Jul 2026 14:25:41 +0000 Subject: [PATCH 10/22] ADS-112..120: add Vendors category and order concepts via sidebar_position Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ads/concepts/break-manifest.mdx | 2 +- ads/concepts/events.mdx | 2 +- ads/concepts/marker-detection.mdx | 2 +- ads/concepts/origins.mdx | 2 +- sidebarsAds.ts | 10 ++++++++++ 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ads/concepts/break-manifest.mdx b/ads/concepts/break-manifest.mdx index 9818e9155410..2f8dde6a195b 100644 --- a/ads/concepts/break-manifest.mdx +++ b/ads/concepts/break-manifest.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 2 +sidebar_position: 7 sidebar_label: Break Manifest --- diff --git a/ads/concepts/events.mdx b/ads/concepts/events.mdx index f016bfad886d..9c3f79a1f4d4 100644 --- a/ads/concepts/events.mdx +++ b/ads/concepts/events.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 2 +sidebar_position: 4 sidebar_label: Events --- diff --git a/ads/concepts/marker-detection.mdx b/ads/concepts/marker-detection.mdx index d9baf65bfad1..9aa9107c1e9e 100644 --- a/ads/concepts/marker-detection.mdx +++ b/ads/concepts/marker-detection.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 3 +sidebar_position: 6 sidebar_label: Break Detection --- diff --git a/ads/concepts/origins.mdx b/ads/concepts/origins.mdx index fd31c5003488..b715d1d2ce98 100644 --- a/ads/concepts/origins.mdx +++ b/ads/concepts/origins.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 2 +sidebar_position: 5 sidebar_label: Origins --- diff --git a/sidebarsAds.ts b/sidebarsAds.ts index 1d95c1fbaeba..1c597267f66c 100644 --- a/sidebarsAds.ts +++ b/sidebarsAds.ts @@ -25,6 +25,16 @@ const sidebars: SidebarsConfig = { }, items: [{ type: 'autogenerated', dirName: 'concepts' }], }, + { + type: 'category', + label: 'Vendors', + description: 'Configure vendor integrations for server-guided and server-side ad insertion.', + customProps: { + icon: '🏷️', + }, + link: { type: 'doc', id: 'vendors/index' }, + items: [{ type: 'autogenerated', dirName: 'vendors' }], + }, { type: 'category', label: 'How-to guides', From 35101c5cae9fb1f8082e44579a4d941c2c0ec9d4 Mon Sep 17 00:00:00 2001 From: "maarten.rimaux" Date: Thu, 30 Jul 2026 15:07:45 +0000 Subject: [PATCH 11/22] ADS-112 rework channels concept per feedback Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ads/concepts/break-manifest.mdx | 4 - ads/concepts/breaks.mdx | 4 - ads/concepts/channels.mdx | 212 ++++++++---------------------- ads/concepts/events.mdx | 4 - ads/concepts/marker-detection.mdx | 4 - ads/concepts/origins.mdx | 4 - ads/concepts/templates.mdx | 4 - ads/vendors/google.mdx | 4 - ads/vendors/index.mdx | 4 - 9 files changed, 54 insertions(+), 190 deletions(-) diff --git a/ads/concepts/break-manifest.mdx b/ads/concepts/break-manifest.mdx index 2f8dde6a195b..bfe75792e175 100644 --- a/ads/concepts/break-manifest.mdx +++ b/ads/concepts/break-manifest.mdx @@ -5,10 +5,6 @@ sidebar_label: Break Manifest # Break Manifest -import RebrandingNotice from '../callouts/_rebranding_notice.md'; - - - The Break Manifest is the canonical, machine-readable description of the ad breaks that are currently relevant for a [channel](/ads/concepts/channels). It is a small JSON document that the OptiView Player polls on a fixed cadence to learn which breaks to prepare and play. The Break Manifest is **side-loaded**: it is served from its own endpoint, separately from the media (HLS/DASH) manifest. The player fetches the media manifest from your CDN as usual and, in parallel, polls the Break Manifest to drive ad break scheduling. This is different from server-side ad insertion (SSAI), where ad cues are injected directly into the media manifest. diff --git a/ads/concepts/breaks.mdx b/ads/concepts/breaks.mdx index 196774b9a628..e7c3cc3e9b00 100644 --- a/ads/concepts/breaks.mdx +++ b/ads/concepts/breaks.mdx @@ -5,10 +5,6 @@ sidebar_label: Breaks # Breaks -import RebrandingNotice from '../callouts/_rebranding_notice.md'; - - - A break is the core monetization entity in OptiView Ads. It represents an ad opportunity scheduled on a channel and contains the timing, lifecycle state, playback controls, layout variants, and typed assets that a player or delivery service needs. Breaks are scoped to an organization and created for a channel. API calls authenticate with an API key and secret using HTTP Basic authentication and identify the organization with the `X-Org-ID` header. diff --git a/ads/concepts/channels.mdx b/ads/concepts/channels.mdx index b752463179e7..e08e65744ee2 100644 --- a/ads/concepts/channels.mdx +++ b/ads/concepts/channels.mdx @@ -5,42 +5,25 @@ sidebar_label: Channels # Channels -import RebrandingNotice from '../callouts/_rebranding_notice.md'; +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. -A channel is the top-level OptiView Ads resource for one live stream. It stores the stream timing model, the Break Manifest polling policy, optional Google DAI asset metadata, and the enablement state for automatic marker detection. - -Channels are scoped to an organization. API calls identify the organization with the `X-Org-ID` header and authenticate with an API key and secret using HTTP Basic authentication. - -## Dashboard path - -In the OptiView Unified Dashboard, open **Ads → Channels**. The channel list exposes **New**, **Edit**, **Delete**, and **Details** actions. - -After opening a channel, the current V2 navigation includes these areas: - -| Area | Use it for | -| ----------------- | ------------------------------------------------------ | -| Overview | View channel settings and player/origin quick actions. | -| Breaks | Schedule, inspect, and delete breaks for the channel. | -| Events | Manage event windows and event-scoped breaks. | -| Origins | Add, enable, disable, and prioritize manifest origins. | -| Break Detection | Configure marker rules and review detection history. | -| Break Integration | Manage channel-level delivery integrations. | +Channels are scoped to an organization. In the OptiView Unified Dashboard, open **Ads → Channels** to create and manage them. ## Channel identity -Every channel has a customer-facing `id`. The API stores it together with the organization ID, so the unique identity is: +Every channel has a customer-facing `id`. Together with the organization, it forms the unique identity of the channel: ```text organizationId + channelId ``` -Use stable channel IDs that match your operational names, such as `sports-main` or `news-east`. If you omit `id` on creation, the API generates one. +Use stable channel IDs that match your operational names, such as `sports-main` or `news-east`. If you omit `id` on creation, one is generated for you. ## Related resources -A channel is the parent or lookup point for the rest of the Ads V2 model: +A channel is the parent or lookup point for the rest of the OptiView Ads model: | Resource | Relationship | | ----------------- | ---------------------------------------------------------------------------- | @@ -52,165 +35,78 @@ A channel is the parent or lookup point for the rest of the Ads V2 model: | Templates | Reusable break presets that can be scheduled on the channel. | | Integrations | Channel-level delivery integrations, such as SSAI DAI cue fan-out. | -## Timebase - -The `timebase` determines how breaks are scheduled for the channel. +In the Dashboard, opening a channel gives you access to each of these areas: -| Timebase | Break start field | Use when | -| ----------- | ----------------- | -------------------------------------------------------------------------------- | -| `wallclock` | `startWallclock` | The stream has UTC wallclock timing, usually from HLS `EXT-X-PROGRAM-DATE-TIME`. | -| `pts` | `startPts` | The workflow schedules against a presentation timestamp timeline. | +| Area | Use it for | +| ----------------- | ------------------------------------------------------ | +| Overview | View channel settings and player/origin quick actions. | +| Breaks | Schedule, inspect, and delete breaks for the channel. | +| Events | Manage event windows and event-scoped breaks. | +| Origins | Add, enable, disable, and prioritize manifest origins. | +| Break Detection | Configure marker rules and review detection history. | +| Break Integration | Manage channel-level delivery integrations. | -Choose the timebase when creating the channel. Breaks created for that channel use the same timebase. +## Timing and delivery settings -## Configuration reference +These channel settings determine how break start times are interpreted and how breaks are delivered to players through the [Break Manifest](./break-manifest.mdx). -| Field | Type | Default | Description | -| ---------------------- | -------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -| `timebase` | `wallclock` or `pts` | Required | Selects whether breaks use `startWallclock` or `startPts`. | -| `dvrWindowMs` | integer | service fallback: `300000` | DVR look-back window used when deciding which breaks are still relevant for delivery. | -| `liveOffsetMs` | integer | `0` | Live latency offset. Wallclock break starts are evaluated against the live playhead rather than raw server time. | -| `pollingIdleSeconds` | integer | service fallback: `10` | Break Manifest polling interval advertised when no break is active. | -| `pollingActiveSeconds` | integer | service fallback: `1` | Break Manifest polling interval advertised during an active break; also used for active manifest caching. | -| `customAssetKey` | string | none | Google DAI custom asset key used for server-guided pod serving on this channel. It must be unique within the organization when set. | -| `detectionEnabled` | boolean | `false` | Read-only response field showing whether automatic marker detection is enabled. | +### Timebase -## Marker detection lifecycle +The `timebase` determines which timeline break start times are expressed on: -`detectionEnabled` is read-only on channel create and update requests. Toggle detection with the dedicated channel actions: +- **`wallclock`** — breaks are scheduled with UTC wallclock timestamps (`startWallclock`). Use this when the stream carries wallclock timing, typically from HLS `EXT-X-PROGRAM-DATE-TIME` tags. +- **`pts`** — breaks are scheduled against the encoder's presentation timestamp timeline (`startPts`). The player needs to retrieve 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. -```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/detection/enable' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` +Choose the timebase when creating the channel; all breaks on the channel use the same timebase. -```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/detection/disable' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` +### DVR window -When detection is enabled, the worker polls the enabled origins for the channel in priority order. The first online origin is used for marker evaluation. Marker rules decide whether a detected marker creates a break, and detection history records the action, reason, origin, marker rule, and break ID. - -## Create a channel - -Dashboard: **Ads → Channels → New**. - -API: - -```bash -curl -X POST 'https://ads.example.com/api/v1/channels' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "id": "sports-main", - "name": "Sports main", - "timebase": "wallclock", - "dvrWindowMs": 300000, - "liveOffsetMs": 0, - "pollingIdleSeconds": 10, - "pollingActiveSeconds": 1, - "customAssetKey": "sports-main-custom-asset" - }' -``` +`dvrWindowMs` describes how far behind live a viewer can be while watching the channel — the time-shifted (DVR) window of the stream. -Example response: - -```json -{ - "id": "sports-main", - "name": "Sports main", - "timebase": "wallclock", - "dvrWindowMs": 300000, - "liveOffsetMs": 0, - "pollingIdleSeconds": 10, - "pollingActiveSeconds": 1, - "customAssetKey": "sports-main-custom-asset", - "detectionEnabled": false, - "createdAt": "2026-07-16T12:00:00.000Z" -} -``` +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. -## Get a channel +### Live offset -```bash -curl 'https://ads.example.com/api/v1/channels/sports-main' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` +`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. -## Update a channel - -```bash -curl -X PATCH 'https://ads.example.com/api/v1/channels/sports-main' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "name": "Sports main HD", - "pollingIdleSeconds": 15 - }' -``` +OptiView Ads uses this offset when evaluating wallclock break start times: instead of comparing against raw server time, break timing is evaluated against the position viewers are actually watching, so breaks activate when the playhead reaches them. -## List channels +### Polling intervals -```bash -curl 'https://ads.example.com/api/v1/channels?page=1&pageSize=20&sort=-createdAt' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` +The Break Manifest response tells players how often to poll for updates. Two channel settings control this cadence: -List endpoints use the same pagination shape. Channels can be filtered by `name` and sorted by `name` or `createdAt`. +- **`pollingIdleSeconds`** — the polling interval advertised when no break is active. A slower cadence keeps request load low while nothing is happening. +- **`pollingActiveSeconds`** — the polling interval advertised while a break is active (also used for active manifest caching). A faster cadence lets players catch break transitions quickly. -| Query parameter | Default | Description | -| --------------- | ------------ | -------------------------------------------------------------------------- | -| `page` | `1` | Page number. | -| `pageSize` | `20` | Items per page. Maximum `100`. | -| `filter` | none | Optional RSQL filter expression. | -| `sort` | `-createdAt` | Comma-separated sort fields. Prefix a field with `-` for descending order. | +### Ad prefetch window -Examples: +`adPrefetchMs` defines how far ahead of a break's start time the break is announced to the player through the Break Manifest. -```bash -curl 'https://ads.example.com/api/v1/channels?filter=name=like=sports&pageSize=50' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` +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. -```bash -curl 'https://ads.example.com/api/v1/channels?sort=name,-createdAt' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` +## Google Ad Manager (pod serving) integration -## Delete a channel +`customAssetKey` connects the channel to Google Ad Manager for server-guided pod serving. It is the Google DAI custom asset key that identifies this live stream in Google Ad Manager, and it must be unique within your organization. -```bash -curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` +Set it when the channel uses Google DAI pod serving; leave it unset otherwise. See the [Google vendor guide](../vendors/google.mdx) for the full Google Ad Manager setup. -Delete a channel only after confirming that no active workflow still depends on its origins, marker rules, events, breaks, templates, or integrations. +## Marker detection -## Add an origin to a channel +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. -Dashboard: open the channel, then use **Origins** from the channel navigation. +Detection is controlled per channel with the read-only `detectionEnabled` state and dedicated enable/disable actions (in the Dashboard under **Break Detection**). When detection is enabled, the enabled origins of the channel are polled in priority order and the first online origin is used for marker evaluation. [Marker rules](./marker-detection.mdx) decide whether a detected marker creates a break, and detection history records every decision. -```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/origins' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "name": "Primary HLS origin", - "type": "HLS", - "url": "https://origin.example.com/live/sports-main/master.m3u8", - "enabled": true, - "priority": 0 - }' -``` +## Configuration reference -Lower `priority` values are tried first when detection is enabled. +| Field | Type | Default | Description | +| ---------------------- | -------------------- | -------- | --------------------------------------------------------------------------------------------------------------- | +| `timebase` | `wallclock` or `pts` | Required | Timeline used for break start times. See [Timebase](#timebase). | +| `dvrWindowMs` | integer | `300000` | Time-shifted viewing window. Determines which breaks the Break Manifest returns. See [DVR window](#dvr-window). | +| `liveOffsetMs` | integer | `0` | How far the player playhead is behind live. See [Live offset](#live-offset). | +| `pollingIdleSeconds` | integer | `10` | Break Manifest polling interval when no break is active. | +| `pollingActiveSeconds` | integer | `1` | Break Manifest polling interval during an active break. | +| `adPrefetchMs` | integer | `10000` | Lead time for announcing upcoming breaks to players. See [Ad prefetch window](#ad-prefetch-window). | +| `customAssetKey` | string | none | Google DAI custom asset key for pod serving. Unique within the organization. | +| `detectionEnabled` | boolean | `false` | Read-only state showing whether automatic marker detection is enabled. | + +For creating, updating, listing, and deleting channels programmatically, see the Ads API reference. diff --git a/ads/concepts/events.mdx b/ads/concepts/events.mdx index 9c3f79a1f4d4..09b862dd7e8e 100644 --- a/ads/concepts/events.mdx +++ b/ads/concepts/events.mdx @@ -5,10 +5,6 @@ sidebar_label: Events # Events -import RebrandingNotice from '../callouts/_rebranding_notice.md'; - - - 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 operational cue/punch workflow. Ahead of a live occurrence you prepare vendor pod breaks under the event without a start time, and during the broadcast you fire them at the exact moment with the punch endpoint. diff --git a/ads/concepts/marker-detection.mdx b/ads/concepts/marker-detection.mdx index 9aa9107c1e9e..5ae87abbbff6 100644 --- a/ads/concepts/marker-detection.mdx +++ b/ads/concepts/marker-detection.mdx @@ -5,10 +5,6 @@ sidebar_label: Break Detection # Break Detection -import RebrandingNotice from '../callouts/_rebranding_notice.md'; - - - Automatic marker detection turns ad markers found in an origin manifest into breaks by applying marker rules. Detection runs per channel when it is enabled. V2 detection currently supports HLS manifests only. See [Origins](./origins.mdx) for origin selection, priority ordering, and first-online behavior. Channels are scoped to an organization. API calls identify the organization with the `X-Org-ID` header and authenticate with an API key and secret using HTTP Basic authentication. diff --git a/ads/concepts/origins.mdx b/ads/concepts/origins.mdx index b715d1d2ce98..78ce11409faa 100644 --- a/ads/concepts/origins.mdx +++ b/ads/concepts/origins.mdx @@ -5,10 +5,6 @@ sidebar_label: Origins # Origins -import RebrandingNotice from '../callouts/_rebranding_notice.md'; - - - An origin is a manifest URL that a channel monitors for ad markers. When automatic marker detection is enabled, the worker 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. Origins are scoped to an organization and to a channel. API calls identify the organization with the `X-Org-ID` header and authenticate with an API key and secret using HTTP Basic authentication. diff --git a/ads/concepts/templates.mdx b/ads/concepts/templates.mdx index b1e2fea2538d..4e18c9b627e3 100644 --- a/ads/concepts/templates.mdx +++ b/ads/concepts/templates.mdx @@ -5,10 +5,6 @@ sidebar_label: Templates # Templates -import RebrandingNotice from '../callouts/_rebranding_notice.md'; - - - A template is a reusable break preset for OptiView Ads. It stores a break payload once so you can schedule consistent breaks quickly, either manually from the dashboard and API or automatically through marker rules. Templates are scoped to an organization. API calls identify the organization with the `X-Org-ID` header and authenticate with an API key and secret using HTTP Basic authentication. diff --git a/ads/vendors/google.mdx b/ads/vendors/google.mdx index d0e6cfff06b5..8861e5feade1 100644 --- a/ads/vendors/google.mdx +++ b/ads/vendors/google.mdx @@ -5,10 +5,6 @@ sidebar_label: Google Ad Manager # Google Ad Manager 360 -import RebrandingNotice from '../callouts/_rebranding_notice.md'; - - - Google Ad Manager 360 (GAM 360) is the first supported OptiView Ads vendor. It requires a GAM 360 account with Dynamic Ad Insertion (DAI) and pod serving enabled. ## Organization configuration diff --git a/ads/vendors/index.mdx b/ads/vendors/index.mdx index 29e8f3c7175d..72d770cdfc52 100644 --- a/ads/vendors/index.mdx +++ b/ads/vendors/index.mdx @@ -5,10 +5,6 @@ sidebar_label: Vendors # Vendors -import RebrandingNotice from '../callouts/_rebranding_notice.md'; - - - A vendor is the ad decisioning or serving integration that OptiView Ads signals breaks to. Google Ad Manager 360 is the first supported vendor. Vendors are not standalone REST resources in Ads V2. A break variant carries a **vendor asset**, while vendor configuration is applied at the organization and channel levels. Self-serve channel, break, and integration APIs use HTTP Basic authentication with an API key and secret, plus the `X-Org-ID` header. Organization-level Google configuration is administrator-managed; see [Google Ad Manager](./google). From d06e79572cea3695ded88f4627cc3124ac9c1b6a Mon Sep 17 00:00:00 2001 From: "maarten.rimaux" Date: Fri, 31 Jul 2026 09:31:33 +0000 Subject: [PATCH 12/22] ADS-112 rephrase integrations as SSAI with Google DAI Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ads/concepts/breaks.mdx | 16 ++++++++-------- ads/concepts/channels.mdx | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ads/concepts/breaks.mdx b/ads/concepts/breaks.mdx index e7c3cc3e9b00..4e3eb754a358 100644 --- a/ads/concepts/breaks.mdx +++ b/ads/concepts/breaks.mdx @@ -66,14 +66,14 @@ The Break also stores internal Google DAI fields such as `podId`, `assetKey`, `n ## Related resources -| Resource | Relationship | -| ---------------------------------- | ---------------------------------------------------------------------------------- | -| [Channels](/ads/concepts/channels) | Parent resource. The channel's timebase determines which start field a Break uses. | -| Events | Time windows that group related Breaks. | -| Templates | Reusable Break definitions merged into a new Break at creation time. | -| Origins | Manifest sources whose detected markers can create Breaks. | -| Marker rules and detection history | Rules and audit records associated with automatically detected Breaks. | -| Integrations | Channel-level delivery integrations, including SSAI DAI cue fan-out. | +| Resource | Relationship | +| ---------------------------------- | ---------------------------------------------------------------------------------------- | +| [Channels](/ads/concepts/channels) | Parent resource. The channel's timebase determines which start field a Break uses. | +| Events | Time windows that group related Breaks. | +| Templates | Reusable Break definitions merged into a new Break at creation time. | +| Origins | Manifest sources whose detected markers can create Breaks. | +| Marker rules and detection history | Rules and audit records associated with automatically detected Breaks. | +| Integrations | Channel-level delivery integrations, including Server-Side Ad Insertion with Google DAI. | ## Scheduling diff --git a/ads/concepts/channels.mdx b/ads/concepts/channels.mdx index e08e65744ee2..5b6f08c7ee56 100644 --- a/ads/concepts/channels.mdx +++ b/ads/concepts/channels.mdx @@ -25,15 +25,15 @@ Use stable channel IDs that match your operational names, such as `sports-main` A channel is the parent or lookup point for the rest of the OptiView Ads model: -| Resource | Relationship | -| ----------------- | ---------------------------------------------------------------------------- | -| Origins | Manifest URLs monitored for ad markers. A channel can have multiple origins. | -| Marker rules | Rules that turn detected markers into breaks. | -| Detection history | Audit records for marker detection decisions on the channel. | -| Breaks | Scheduled or detected ad opportunities for the channel. | -| Events | Time windows that group related breaks. | -| Templates | Reusable break presets that can be scheduled on the channel. | -| Integrations | Channel-level delivery integrations, such as SSAI DAI cue fan-out. | +| Resource | Relationship | +| ----------------- | -------------------------------------------------------------------------------------- | +| Origins | Manifest URLs monitored for ad markers. A channel can have multiple origins. | +| Marker rules | Rules that turn detected markers into breaks. | +| Detection history | Audit records for marker detection decisions on the channel. | +| Breaks | Scheduled or detected ad opportunities for the channel. | +| Events | Time windows that group related breaks. | +| Templates | Reusable break presets that can be scheduled on the channel. | +| Integrations | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. | In the Dashboard, opening a channel gives you access to each of these areas: From 1a1b5de73a888e53d91389c8ea07700a7214ad1f Mon Sep 17 00:00:00 2001 From: "maarten.rimaux" Date: Fri, 31 Jul 2026 09:33:57 +0000 Subject: [PATCH 13/22] ADS-112 move related resources to end with links Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ads/concepts/channels.mdx | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/ads/concepts/channels.mdx b/ads/concepts/channels.mdx index 5b6f08c7ee56..43d5350e04ca 100644 --- a/ads/concepts/channels.mdx +++ b/ads/concepts/channels.mdx @@ -21,20 +21,6 @@ organizationId + channelId Use stable channel IDs that match your operational names, such as `sports-main` or `news-east`. If you omit `id` on creation, one is generated for you. -## Related resources - -A channel is the parent or lookup point for the rest of the OptiView Ads model: - -| Resource | Relationship | -| ----------------- | -------------------------------------------------------------------------------------- | -| Origins | Manifest URLs monitored for ad markers. A channel can have multiple origins. | -| Marker rules | Rules that turn detected markers into breaks. | -| Detection history | Audit records for marker detection decisions on the channel. | -| Breaks | Scheduled or detected ad opportunities for the channel. | -| Events | Time windows that group related breaks. | -| Templates | Reusable break presets that can be scheduled on the channel. | -| Integrations | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. | - In the Dashboard, opening a channel gives you access to each of these areas: | Area | Use it for | @@ -110,3 +96,17 @@ Detection is controlled per channel with the read-only `detectionEnabled` state | `detectionEnabled` | boolean | `false` | Read-only state showing whether automatic marker detection is enabled. | 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 | +| ------------------------------------------- | -------------------------------------------------------------------------------------- | +| [Origins](./origins.mdx) | Manifest URLs monitored for ad markers. A channel can have multiple origins. | +| [Marker rules](./marker-detection.mdx) | Rules that turn detected markers into breaks. | +| [Detection history](./marker-detection.mdx) | Audit records for marker detection decisions on the channel. | +| [Breaks](./breaks.mdx) | Scheduled or detected ad opportunities for the channel. | +| [Events](./events.mdx) | Time windows that group related breaks. | +| [Templates](./templates.mdx) | Reusable break presets that can be scheduled on the channel. | +| [Integrations](../vendors/google.mdx) | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. | From b75f7fb2248c7bbfff133ca036a6a87d47424323 Mon Sep 17 00:00:00 2001 From: "maarten.rimaux" Date: Fri, 31 Jul 2026 11:24:25 +0000 Subject: [PATCH 14/22] ADS-112 channels feedback round 2 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ads/concepts/channels.mdx | 59 +++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/ads/concepts/channels.mdx b/ads/concepts/channels.mdx index 43d5350e04ca..6395fbdab2fb 100644 --- a/ads/concepts/channels.mdx +++ b/ads/concepts/channels.mdx @@ -11,6 +11,17 @@ Everything else in OptiView Ads hangs off a channel. Origins, marker rules, brea 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 player/origin quick actions. | +| [Breaks](./breaks.mdx) | Schedule, inspect, and delete breaks for the channel. | +| [Events](./events.mdx) | Manage event windows and event-scoped breaks. | +| [Origins](./origins.mdx) | Add, enable, disable, and prioritize manifest origins. | +| [Break Detection](./marker-detection.mdx) | Configure marker rules and review detection history. | +| [Break Integration](../vendors/google.mdx) | Manage channel-level delivery integrations. | + ## Channel identity Every channel has a customer-facing `id`. Together with the organization, it forms the unique identity of the channel: @@ -19,18 +30,7 @@ Every channel has a customer-facing `id`. Together with the organization, it for organizationId + channelId ``` -Use stable channel IDs that match your operational names, such as `sports-main` or `news-east`. If you omit `id` on creation, one is generated for you. - -In the Dashboard, opening a channel gives you access to each of these areas: - -| Area | Use it for | -| ----------------- | ------------------------------------------------------ | -| Overview | View channel settings and player/origin quick actions. | -| Breaks | Schedule, inspect, and delete breaks for the channel. | -| Events | Manage event windows and event-scoped breaks. | -| Origins | Add, enable, disable, and prioritize manifest origins. | -| Break Detection | Configure marker rules and review detection history. | -| Break Integration | Manage channel-level delivery integrations. | +We recommend using a UUID as the channel `id` and the `name` property when you want a human-readable name. If you omit `id` on creation, one is generated for you. ## Timing and delivery settings @@ -40,60 +40,53 @@ These channel settings determine how break start times are interpreted and how b The `timebase` determines which timeline break start times are expressed on: -- **`wallclock`** — breaks are scheduled with UTC wallclock timestamps (`startWallclock`). Use this when the stream carries wallclock timing, typically from HLS `EXT-X-PROGRAM-DATE-TIME` tags. +- **`wallclock`** — breaks are scheduled with UTC wallclock timestamps (`startWallclock`). 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 against the encoder's presentation timestamp timeline (`startPts`). The player needs to retrieve 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 when evaluating wallclock break start times: instead of comparing against raw server time, break timing is evaluated against the position viewers are actually watching, so breaks activate when the playhead reaches them. +OptiView Ads uses this offset when evaluating wallclock break start times: instead of comparing against raw server time, break timing is evaluated against the position viewers are actually watching, so breaks activate when the playhead reaches them. This is required because breaks cannot be scheduled in the past — without the offset, a break aimed at the viewer's playhead position would already lie behind the raw server time. ### Polling intervals The Break Manifest response tells players how often to poll for updates. Two channel settings control this cadence: -- **`pollingIdleSeconds`** — the polling interval advertised when no break is active. A slower cadence keeps request load low while nothing is happening. -- **`pollingActiveSeconds`** — the polling interval advertised while a break is active (also used for active manifest caching). A faster cadence lets players catch break transitions quickly. +- **`pollingIdleSeconds`** — the polling interval advertised when no break is active. A slower cadence keeps request load low while nothing is happening. Default: `10` seconds. +- **`pollingActiveSeconds`** — the polling interval advertised while a break is active. A faster cadence lets players catch break transitions quickly. Default: `1` second. ### 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. ## Google Ad Manager (pod serving) integration -`customAssetKey` connects the channel to Google Ad Manager for server-guided pod serving. It is the Google DAI custom asset key that identifies this live stream in Google Ad Manager, and it must be unique within your organization. +A channel can be connected to Google Ad Manager for server-guided pod serving: Google decisions the ad pods for the stream, and OptiView Ads announces the resulting breaks to players. See [Vendors → Google Ad Manager](../vendors/google.mdx) for the full setup. -Set it when the channel uses Google DAI pod serving; leave it unset otherwise. See the [Google vendor guide](../vendors/google.mdx) for the full Google Ad Manager setup. +On the channel itself you configure the `customAssetKey`: the Google DAI custom asset key that identifies this live stream in Google Ad Manager. It must be unique within your organization. Set it when the channel uses Google DAI pod serving; leave it unset otherwise. ## Marker 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. - -Detection is controlled per channel with the read-only `detectionEnabled` state and dedicated enable/disable actions (in the Dashboard under **Break Detection**). When detection is enabled, the enabled origins of the channel are polled in priority order and the first online origin is used for marker evaluation. [Marker rules](./marker-detection.mdx) decide whether a detected marker creates a break, and detection history records every decision. - -## Configuration reference +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. Detection is enabled or disabled per channel, in the Dashboard under **Break Detection**. -| Field | Type | Default | Description | -| ---------------------- | -------------------- | -------- | --------------------------------------------------------------------------------------------------------------- | -| `timebase` | `wallclock` or `pts` | Required | Timeline used for break start times. See [Timebase](#timebase). | -| `dvrWindowMs` | integer | `300000` | Time-shifted viewing window. Determines which breaks the Break Manifest returns. See [DVR window](#dvr-window). | -| `liveOffsetMs` | integer | `0` | How far the player playhead is behind live. See [Live offset](#live-offset). | -| `pollingIdleSeconds` | integer | `10` | Break Manifest polling interval when no break is active. | -| `pollingActiveSeconds` | integer | `1` | Break Manifest polling interval during an active break. | -| `adPrefetchMs` | integer | `10000` | Lead time for announcing upcoming breaks to players. See [Ad prefetch window](#ad-prefetch-window). | -| `customAssetKey` | string | none | Google DAI custom asset key for pod serving. Unique within the organization. | -| `detectionEnabled` | boolean | `false` | Read-only state showing whether automatic marker detection is enabled. | +See [Break detection](./marker-detection.mdx) for how origins are polled, how marker rules turn detected markers into breaks, and how detection history records every decision. For creating, updating, listing, and deleting channels programmatically, see the Ads API reference. From 704e625cf1d72b121b3d20a0bec392a7a54f53d4 Mon Sep 17 00:00:00 2001 From: "maarten.rimaux" Date: Fri, 31 Jul 2026 11:35:24 +0000 Subject: [PATCH 15/22] ADS-112 order related resources like sidebar Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ads/concepts/channels.mdx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ads/concepts/channels.mdx b/ads/concepts/channels.mdx index 6395fbdab2fb..51d051fb73b8 100644 --- a/ads/concepts/channels.mdx +++ b/ads/concepts/channels.mdx @@ -94,12 +94,12 @@ For creating, updating, listing, and deleting channels programmatically, see the A channel is the parent or lookup point for the rest of the OptiView Ads model: -| Resource | Relationship | -| ------------------------------------------- | -------------------------------------------------------------------------------------- | -| [Origins](./origins.mdx) | Manifest URLs monitored for ad markers. A channel can have multiple origins. | -| [Marker rules](./marker-detection.mdx) | Rules that turn detected markers into breaks. | -| [Detection history](./marker-detection.mdx) | Audit records for marker detection decisions on the channel. | -| [Breaks](./breaks.mdx) | Scheduled or detected ad opportunities for the channel. | -| [Events](./events.mdx) | Time windows that group related breaks. | -| [Templates](./templates.mdx) | Reusable break presets that can be scheduled on the channel. | -| [Integrations](../vendors/google.mdx) | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. | +| 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) | Time windows that group related breaks. | +| [Origins](./origins.mdx) | Manifest URLs monitored for ad markers. A channel can have multiple origins. | +| [Break detection](./marker-detection.mdx) | Rules that turn detected markers into breaks, and the audit history of every decision. | +| [Break Manifest](./break-manifest.mdx) | The endpoint that announces the channel's breaks to players. | +| [Integrations](../vendors/google.mdx) | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. | From b2c876c7c8ddeb62e655d7f8f1d57c293f7ebcb0 Mon Sep 17 00:00:00 2001 From: Devin AI Date: Mon, 10 Aug 2026 07:28:37 +0000 Subject: [PATCH 16/22] Restructure channels doc, rename Vendors to Integrations, move polling to Break Manifest --- ads/concepts/break-manifest.mdx | 11 +++- ads/concepts/breaks.mdx | 2 +- ads/concepts/channels.mdx | 65 ++++++++---------------- ads/concepts/events.mdx | 14 ++--- ads/concepts/marker-detection.mdx | 2 +- ads/{vendors => integrations}/google.mdx | 4 +- ads/{vendors => integrations}/index.mdx | 8 +-- sidebarsAds.ts | 8 +-- 8 files changed, 50 insertions(+), 64 deletions(-) rename ads/{vendors => integrations}/google.mdx (94%) rename ads/{vendors => integrations}/index.mdx (93%) diff --git a/ads/concepts/break-manifest.mdx b/ads/concepts/break-manifest.mdx index bfe75792e175..762cd5f01fad 100644 --- a/ads/concepts/break-manifest.mdx +++ b/ads/concepts/break-manifest.mdx @@ -57,6 +57,15 @@ The response carries a `Cache-Control` header so that players and CDNs poll at a The `max-age` on a successful response always uses the **active** polling interval, so that a cached copy is never held longer than the shortest polling cadence the channel advertises. Use the `polling` values inside the manifest body (see below) to decide how often to poll; use `Cache-Control` for CDN and HTTP cache behavior. +## Polling intervals + +The Break Manifest response tells players how often to poll for updates. Two channel settings control this cadence: + +- **`pollingIdleSeconds`** — the polling interval advertised when no break is active. A slower cadence keeps request load low while nothing is happening. Default: `10` seconds. +- **`pollingActiveSeconds`** — the polling interval advertised while a break is active. A faster cadence lets players catch break transitions quickly. Default: `1` second. + +These values are advertised to players through the `polling` object in the manifest envelope (see below). + ## Manifest envelope The response body is the Break Manifest envelope. The following descriptions are written from the service `breakManifestSchema`. @@ -222,6 +231,6 @@ When `breaks` is empty, the envelope is still returned with the channel `timebas ## Related resources -- [Channels](/ads/concepts/channels) — the timebase, polling policy (`pollingIdleSeconds`, `pollingActiveSeconds`), and delivery window (`dvrWindowMs`, `liveOffsetMs`) that shape the Break Manifest. +- [Channels](/ads/concepts/channels) — the timebase and delivery window (`dvrWindowMs`, `liveOffsetMs`) that shape the Break Manifest. - [Scheduling breaks](/ads/how-to-guides/scheduling-breaks) — how breaks are created and signaled for a channel. - [Getting started](/ads/getting-started/) — integrating the OptiView Player that polls the Break Manifest. diff --git a/ads/concepts/breaks.mdx b/ads/concepts/breaks.mdx index 4e3eb754a358..49cbe242c7ab 100644 --- a/ads/concepts/breaks.mdx +++ b/ads/concepts/breaks.mdx @@ -607,4 +607,4 @@ curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks/gam-cue - Templates — reusable Break definitions and Template-based scheduling. - Events — event windows and event-scoped Breaks. - Marker Detection — automatic marker evaluation and Break provenance. -- Vendors and Google DAI — vendor pod decisioning and delivery. +- Integrations and Google DAI — vendor pod decisioning and delivery. diff --git a/ads/concepts/channels.mdx b/ads/concepts/channels.mdx index 51d051fb73b8..aa101e371742 100644 --- a/ads/concepts/channels.mdx +++ b/ads/concepts/channels.mdx @@ -13,24 +13,18 @@ Channels are scoped to an organization. In the OptiView Unified Dashboard, open In the Dashboard, opening a channel gives you access to each of these tabs: -| Tab | Use it for | -| ------------------------------------------ | ------------------------------------------------------ | -| Overview | View channel settings and player/origin quick actions. | -| [Breaks](./breaks.mdx) | Schedule, inspect, and delete breaks for the channel. | -| [Events](./events.mdx) | Manage event windows and event-scoped breaks. | -| [Origins](./origins.mdx) | Add, enable, disable, and prioritize manifest origins. | -| [Break Detection](./marker-detection.mdx) | Configure marker rules and review detection history. | -| [Break Integration](../vendors/google.mdx) | Manage channel-level delivery integrations. | +| 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.mdx) | Manage channel-level delivery integrations. | ## Channel identity -Every channel has a customer-facing `id`. Together with the organization, it forms the unique identity of the channel: - -```text -organizationId + channelId -``` - -We recommend using a UUID as the channel `id` and the `name` property when you want a human-readable name. If you omit `id` on creation, one is generated for you. +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 @@ -40,7 +34,7 @@ These channel settings determine how break start times are interpreted and how b The `timebase` determines which timeline break start times are expressed on: -- **`wallclock`** — breaks are scheduled with UTC wallclock timestamps (`startWallclock`). 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). +- **`wallclock`** — breaks are scheduled based on the stream's wallclock timing: the break's start (`startWallclock`, a UTC timestamp) 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 against the encoder's presentation timestamp timeline (`startPts`). The player needs to retrieve 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. @@ -59,14 +53,7 @@ _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 when evaluating wallclock break start times: instead of comparing against raw server time, break timing is evaluated against the position viewers are actually watching, so breaks activate when the playhead reaches them. This is required because breaks cannot be scheduled in the past — without the offset, a break aimed at the viewer's playhead position would already lie behind the raw server time. - -### Polling intervals - -The Break Manifest response tells players how often to poll for updates. Two channel settings control this cadence: - -- **`pollingIdleSeconds`** — the polling interval advertised when no break is active. A slower cadence keeps request load low while nothing is happening. Default: `10` seconds. -- **`pollingActiveSeconds`** — the polling interval advertised while a break is active. A faster cadence lets players catch break transitions quickly. Default: `1` second. +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 @@ -76,30 +63,18 @@ _Default: `10000` ms (10 seconds)_ 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. -## Google Ad Manager (pod serving) integration - -A channel can be connected to Google Ad Manager for server-guided pod serving: Google decisions the ad pods for the stream, and OptiView Ads announces the resulting breaks to players. See [Vendors → Google Ad Manager](../vendors/google.mdx) for the full setup. - -On the channel itself you configure the `customAssetKey`: the Google DAI custom asset key that identifies this live stream in Google Ad Manager. It must be unique within your organization. Set it when the channel uses Google DAI pod serving; leave it unset otherwise. - -## Marker 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. Detection is enabled or disabled per channel, in the Dashboard under **Break Detection**. - -See [Break detection](./marker-detection.mdx) for how origins are polled, how marker rules turn detected markers into breaks, and how detection history records every decision. - 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) | Time windows that group related breaks. | -| [Origins](./origins.mdx) | Manifest URLs monitored for ad markers. A channel can have multiple origins. | -| [Break detection](./marker-detection.mdx) | Rules that turn detected markers into breaks, and the audit history of every decision. | -| [Break Manifest](./break-manifest.mdx) | The endpoint that announces the channel's breaks to players. | -| [Integrations](../vendors/google.mdx) | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. | +| 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) | Prepare breaks for an event on your live stream. | +| [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 endpoint that announces the channel's breaks to players. | +| [Integrations](../integrations/google.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 index 09b862dd7e8e..8f6c94a909c6 100644 --- a/ads/concepts/events.mdx +++ b/ads/concepts/events.mdx @@ -57,7 +57,7 @@ To list only the breaks that belong to an event, use the event's breaks endpoint ## Cue / punch workflow during an event -For a live occurrence you usually do not know the exact break times in advance, but you want the ad decision ready so the break can fire instantly. Events are where this "prepare ahead, fire live" workflow lives. The full break state machine and the Google DAI (vendor pod) prerequisites are documented in the Breaks and Vendors / Google sections; the flow below focuses on running an event. +For a live occurrence you usually do not know the exact break times in advance, but you want the ad decision ready so the break can fire instantly. Events are where this "prepare ahead, fire live" workflow lives. The full break state machine and the Google DAI (vendor pod) prerequisites are documented in the Breaks and Integrations / Google sections; the flow below focuses on running an event. ### Ahead of the event: prepare cued breaks @@ -260,9 +260,9 @@ curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks/halftim ## Related resources -| Resource | Relationship | -| ---------------- | ----------------------------------------------------------------------------------------------- | -| Channels | The parent of an event. An event always belongs to one channel. See [Channels](./channels.mdx). | -| Breaks | Attached to an event via `eventId`; the Breaks section documents the full status state machine. | -| Templates | Linked to events via `eventIds` for quick scheduling. | -| Vendors / Google | Provide the pod pre-decisioning that moves a cued vendor pod break from `PREPARING` to `CUED`. | +| Resource | Relationship | +| --------------------- | ----------------------------------------------------------------------------------------------- | +| Channels | The parent of an event. An event always belongs to one channel. See [Channels](./channels.mdx). | +| Breaks | Attached to an event via `eventId`; the Breaks section documents the full status state machine. | +| Templates | Linked to events via `eventIds` for quick scheduling. | +| Integrations / Google | Provide the pod pre-decisioning that moves a cued vendor pod break from `PREPARING` to `CUED`. | diff --git a/ads/concepts/marker-detection.mdx b/ads/concepts/marker-detection.mdx index 5ae87abbbff6..091d160ab6b0 100644 --- a/ads/concepts/marker-detection.mdx +++ b/ads/concepts/marker-detection.mdx @@ -5,7 +5,7 @@ sidebar_label: Break Detection # Break Detection -Automatic marker detection turns ad markers found in an origin manifest into breaks by applying marker rules. Detection runs per channel when it is enabled. V2 detection currently supports HLS manifests only. See [Origins](./origins.mdx) for origin selection, priority ordering, and first-online behavior. +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, in the Dashboard under **Break Detection**. V2 detection currently supports HLS manifests only. See [Origins](./origins.mdx) for origin selection, priority ordering, and first-online behavior. Channels are scoped to an organization. API calls identify the organization with the `X-Org-ID` header and authenticate with an API key and secret using HTTP Basic authentication. diff --git a/ads/vendors/google.mdx b/ads/integrations/google.mdx similarity index 94% rename from ads/vendors/google.mdx rename to ads/integrations/google.mdx index 8861e5feade1..662a9240a997 100644 --- a/ads/vendors/google.mdx +++ b/ads/integrations/google.mdx @@ -22,7 +22,9 @@ The organization-level values override the service defaults. `networkCode` and ` ## SGAI pod serving -SGAI is server-guided pod serving keyed by the channel's `customAssetKey`. A `customAssetKey` is unique within an organization when set. See [Channels](../../concepts/channels) for channel configuration. +SGAI is server-guided pod serving keyed by the channel's `customAssetKey`: Google decisions the ad pods for the stream, and OptiView Ads announces the resulting breaks to players. + +On the [channel](../../concepts/channels) you configure the `customAssetKey`: the Google DAI custom asset key that identifies this live stream in Google Ad Manager. It must be unique within your organization. Set it when the channel uses Google DAI pod serving; leave it unset otherwise. A GAM pod break uses a vendor asset with `vendorParameters.type` set to `"pod"`: diff --git a/ads/vendors/index.mdx b/ads/integrations/index.mdx similarity index 93% rename from ads/vendors/index.mdx rename to ads/integrations/index.mdx index 72d770cdfc52..9cb7ccbd2b97 100644 --- a/ads/vendors/index.mdx +++ b/ads/integrations/index.mdx @@ -1,11 +1,11 @@ --- sidebar_position: 1 -sidebar_label: Vendors +sidebar_label: Integrations --- -# Vendors +# Integrations -A vendor is the ad decisioning or serving integration that OptiView Ads signals breaks to. Google Ad Manager 360 is the first supported vendor. +An integration connects OptiView Ads to an ad decisioning or serving vendor that breaks are signaled to. Google Ad Manager 360 is the first supported integration. Vendors are not standalone REST resources in Ads V2. A break variant carries a **vendor asset**, while vendor configuration is applied at the organization and channel levels. Self-serve channel, break, and integration APIs use HTTP Basic authentication with an API key and secret, plus the `X-Org-ID` header. Organization-level Google configuration is administrator-managed; see [Google Ad Manager](./google). @@ -40,7 +40,7 @@ Example GAM pod asset: | --------------------------------- | ---------- | ------------------------------------- | | [Google Ad Manager 360](./google) | `gam` | SGAI pod serving and SSAI_DAI fan-out | -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 Vendors sidebar. +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. ## How vendors relate to the Ads V2 model diff --git a/sidebarsAds.ts b/sidebarsAds.ts index 1c597267f66c..7975a313b0b4 100644 --- a/sidebarsAds.ts +++ b/sidebarsAds.ts @@ -27,13 +27,13 @@ const sidebars: SidebarsConfig = { }, { type: 'category', - label: 'Vendors', - description: 'Configure vendor integrations for server-guided and server-side ad insertion.', + label: 'Integrations', + description: 'Configure integrations for server-guided and server-side ad insertion.', customProps: { icon: '🏷️', }, - link: { type: 'doc', id: 'vendors/index' }, - items: [{ type: 'autogenerated', dirName: 'vendors' }], + link: { type: 'doc', id: 'integrations/index' }, + items: [{ type: 'autogenerated', dirName: 'integrations' }], }, { type: 'category', From a875612eaa418d6f5dd7efda6bc0d45ca5003a1e Mon Sep 17 00:00:00 2001 From: "maarten.rimaux" Date: Fri, 14 Aug 2026 10:59:12 +0000 Subject: [PATCH 17/22] Rewrite Ads breaks documentation to be customer-facing - Simplify break identity, remove stored fields and internal mechanics - Rework scheduling: timebases, cued breaks, template snapshots, event preparation, constraints rationale - Rename lifecycle to Break Lifecycle with customer-facing states and diagram - Rename cue-and-punch to Break punching - Restructure break configuration: general, event based triggers, controls, layouts, variants, asset model - Add clickable layout overview with docs-colored visuals - Use regional production domains and UUIDs in examples - Align related resources tables across pages Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ads/assets/img/breaks/break-lifecycle.svg | 47 +- ads/assets/img/breaks/format-double.svg | 10 +- ads/assets/img/breaks/format-lshape-ad.svg | 10 +- .../img/breaks/format-lshape-content.svg | 12 +- ads/assets/img/breaks/format-overlay.svg | 6 +- ads/assets/img/breaks/format-single.svg | 2 +- ads/concepts/break-manifest.mdx | 17 +- ads/concepts/breaks.mdx | 654 +++++++----------- ads/concepts/channels.mdx | 8 +- ads/concepts/events.mdx | 32 +- ads/concepts/marker-detection.mdx | 24 +- ads/concepts/origins.mdx | 16 +- ads/concepts/templates.mdx | 26 +- ads/integrations/google.mdx | 11 +- 14 files changed, 369 insertions(+), 506 deletions(-) diff --git a/ads/assets/img/breaks/break-lifecycle.svg b/ads/assets/img/breaks/break-lifecycle.svg index 54c414a029f2..3938b695240a 100644 --- a/ads/assets/img/breaks/break-lifecycle.svg +++ b/ads/assets/img/breaks/break-lifecycle.svg @@ -1,30 +1,27 @@ - + - + - - initial - - PREPARING - - CUED - - READY - - ERROR - - SIGNALED - - - worker/EABN - - worker/EABN - - worker/health - - API punch - - manifest / proxy + + PREPARING + + CUED + + READY + + ERROR + + SIGNALED + + prepared, no start yet + + prepared, start set + + scheduling window missed + + punch + + announced to players diff --git a/ads/assets/img/breaks/format-double.svg b/ads/assets/img/breaks/format-double.svg index 978e9035f2eb..69df7a7e0231 100644 --- a/ads/assets/img/breaks/format-double.svg +++ b/ads/assets/img/breaks/format-double.svg @@ -1,8 +1,8 @@ - - - - + + + + CONTENT AD - side-by-side primary and companion windows + side-by-side boxes over a companion backdrop diff --git a/ads/assets/img/breaks/format-lshape-ad.svg b/ads/assets/img/breaks/format-lshape-ad.svg index 5f7a72692bfb..4aa600cc9554 100644 --- a/ads/assets/img/breaks/format-lshape-ad.svg +++ b/ads/assets/img/breaks/format-lshape-ad.svg @@ -1,8 +1,8 @@ - - + + AD - companion - backdrop - ad window with companion backdrop + companion + backdrop + ad window with companion backdrop diff --git a/ads/assets/img/breaks/format-lshape-content.svg b/ads/assets/img/breaks/format-lshape-content.svg index 96bdc0bdf71e..f17df9c31c49 100644 --- a/ads/assets/img/breaks/format-lshape-content.svg +++ b/ads/assets/img/breaks/format-lshape-content.svg @@ -1,8 +1,8 @@ - - - + + + CONTENT - companion - backdrop - live content window with companion backdrop + ad + backdrop + live content window with ad backdrop diff --git a/ads/assets/img/breaks/format-overlay.svg b/ads/assets/img/breaks/format-overlay.svg index f526f3b3bcc6..f275eb2b7779 100644 --- a/ads/assets/img/breaks/format-overlay.svg +++ b/ads/assets/img/breaks/format-overlay.svg @@ -1,7 +1,7 @@ - + CONTENT - + OVERLAY - semi-transparent overlay over live content + semi-transparent overlay over live content diff --git a/ads/assets/img/breaks/format-single.svg b/ads/assets/img/breaks/format-single.svg index a83e60ae25ca..9851f4b8426b 100644 --- a/ads/assets/img/breaks/format-single.svg +++ b/ads/assets/img/breaks/format-single.svg @@ -1,5 +1,5 @@ - + AD full-screen replacement diff --git a/ads/concepts/break-manifest.mdx b/ads/concepts/break-manifest.mdx index 762cd5f01fad..fed8b5665394 100644 --- a/ads/concepts/break-manifest.mdx +++ b/ads/concepts/break-manifest.mdx @@ -36,9 +36,13 @@ GET /manifest/v1/:orgId/channels/:channelId The Break Manifest 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. It differs from the [Channels](/ads/concepts/channels) management API, which is authenticated. Do not place secrets in the polling URL. ```bash -curl 'https://ads.example.com/manifest/v1/org_123/channels/sports-main' +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 | Status | Meaning | @@ -212,7 +216,7 @@ For a channel created with `timebase: pts`, each break `start` is a numeric pres "variant": [ { "format": "single", // default full-screen experience - "assets": [{ "id": "a1", "type": "vast", "mediaType": "video", "uri": "https://ads.example.com/vast.xml" }] + "assets": [{ "id": "a1", "type": "vast", "mediaType": "video", "uri": "https://adserver.example.com/vast.xml" }] }, { "format": "overlay", // alternative overlay experience @@ -231,6 +235,9 @@ When `breaks` is empty, the envelope is still returned with the channel `timebas ## Related resources -- [Channels](/ads/concepts/channels) — the timebase and delivery window (`dvrWindowMs`, `liveOffsetMs`) that shape the Break Manifest. -- [Scheduling breaks](/ads/how-to-guides/scheduling-breaks) — how breaks are created and signaled for a channel. -- [Getting started](/ads/getting-started/) — integrating the OptiView Player that polls the Break Manifest. +| Resource | Relationship | +| ------------------------------------------ | --------------------------------------------------------------------------------------------------- | +| [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.mdx) | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. | +| [Getting started](/ads/getting-started/) | Integrating the OptiView Player that polls the Break Manifest. | diff --git a/ads/concepts/breaks.mdx b/ads/concepts/breaks.mdx index 49cbe242c7ab..3088d5122453 100644 --- a/ads/concepts/breaks.mdx +++ b/ads/concepts/breaks.mdx @@ -5,104 +5,46 @@ sidebar_label: Breaks # Breaks -A break is the core monetization entity in OptiView Ads. It represents an ad opportunity scheduled on a channel and contains the timing, lifecycle state, playback controls, layout variants, and typed assets that a player or delivery service needs. +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. API calls authenticate with an API key and secret using HTTP Basic authentication and identify the organization with the `X-Org-ID` header. +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. -## Dashboard path - -In the OptiView Unified Dashboard, open **Ads → Channels**, open a channel, and select **Breaks**. The Breaks area is used to schedule, inspect, and delete breaks for the channel. +:::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 -The compound identity of a Break is: - -```text -orgId + channelId + id -``` - -`id` is optional when creating a Break. If omitted, the API generates an identifier. The identity is scoped by both the organization and channel, so the same `id` can exist on different channels or in different organizations. - -| Field | Type | Description | -| ------------ | ---------------- | --------------------------------------------------------------------------------------------------------------- | -| `id` | string | Break identifier. Auto-generated if omitted on create. | -| `orgId` | string | Organization scope. Supplied by the authenticated `X-Org-ID` context. | -| `channelId` | string | Parent channel identifier. | -| `originId` | string, optional | Provenance for an automatically detected Break. Set internally by the detection worker and not client-settable. | -| `templateId` | string, optional | Identifier of the Template used to create the Break, if any. | -| `eventId` | string, optional | Identifier of the Event under which the Break was scheduled, if any. | - -The Break also stores internal Google DAI fields such as `podId`, `assetKey`, `networkCode`, `customAssetKey`, and `daiAssetKeys`, plus the lifecycle `status`, optional `errorMessage`, timebase-specific start fields, denormalized indexes, and the raw `data` payload. - -### Stored fields - -| Field | Type | Required/default behavior | -| ------------------- | ---------------------- | ------------------------------------------------------------- | -| `id` | string | Required; generated when omitted on create. | -| `orgId` | string | Required organization scope. | -| `channelId` | string | Required channel scope. | -| `eventId` | string, optional | Event association. | -| `templateId` | string, optional | Template association retained after creation. | -| `podId` | string, optional | Google DAI pod identifier after vendor-pod decisioning. | -| `status` | enum | `PREPARING`, `CUED`, `READY`, `SIGNALED`, or `ERROR`. | -| `originId` | string, optional | Internal provenance for an automatically detected Break. | -| `markerRuleId` | string, optional | Marker rule associated with automatic detection. | -| `markerDetectionId` | string, optional | Detection-history record associated with automatic detection. | -| `assetKey` | string, optional | Google DAI asset key. | -| `networkCode` | string, optional | Organization Google DAI network-code snapshot. | -| `customAssetKey` | string, optional | Channel Google DAI custom-asset-key snapshot. | -| `daiAssetKeys` | string array, optional | Deduplicated SSAI DAI asset-key snapshot. | -| `errorMessage` | string, optional | Failure reason when status is `ERROR`. | -| `timebase` | `wallclock` or `pts` | Required; copied from the channel. | -| `startWallclock` | Date, optional | Wallclock start for wallclock channels. | -| `startPts` | number, optional | Numeric PTS start for PTS channels. | -| `duration` | number | Required duration in seconds. | -| `variantFormats` | string array, optional | Denormalized variant-format index. | -| `assetTypes` | string array, optional | Denormalized asset-type index. | -| `vendors` | string array, optional | Denormalized vendor index. | -| `data` | object | Required raw `BreakData` payload. | -| `createdAt` | Date | Automatically managed creation timestamp. | -| `updatedAt` | Date | Automatically managed modification timestamp. | - -## Related resources - -| Resource | Relationship | -| ---------------------------------- | ---------------------------------------------------------------------------------------- | -| [Channels](/ads/concepts/channels) | Parent resource. The channel's timebase determines which start field a Break uses. | -| Events | Time windows that group related Breaks. | -| Templates | Reusable Break definitions merged into a new Break at creation time. | -| Origins | Manifest sources whose detected markers can create Breaks. | -| Marker rules and detection history | Rules and audit records associated with automatically detected Breaks. | -| Integrations | Channel-level delivery integrations, including Server-Side Ad Insertion with Google DAI. | +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-dependent starts +### 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: -Every Break copies the timebase of its channel: +- **`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. -| Channel timebase | API `start` value | Stored field | Requirement | -| ---------------- | ------------------------ | ---------------- | ------------------------------------------------------- | -| `wallclock` | ISO 8601 datetime string | `startWallclock` | Optional. Omitting it creates a CUED no-start workflow. | -| `pts` | Non-negative number | `startPts` | Required. | +`duration` is always required and is expressed in seconds. It is the **maximum** duration of the break. -The API request field is named `start`; the service maps it to `startWallclock` or `startPts` according to the channel timebase. A PTS channel rejects a missing or non-numeric start. A wallclock channel accepts an omitted start, but a supplied start must be a valid ISO datetime. +### Cued breaks -`duration` is required and is expressed in seconds. A scheduled Break cannot overlap another Break on the same channel. Wallclock overlap is evaluated using wallclock instants; PTS overlap is evaluated using PTS values. +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. -The service also requires a scheduled start to be sufficiently ahead of the current effective playhead. GAM vendor pod Breaks must additionally clear the EABN decisioning margin. +See [Break punching](#break-punching) for how to fire a cued break. -### Create a Break directly +### Create a break directly -Create a Break by supplying its payload and, when required, its `start`: +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://ads.example.com/api/v1/channels/sports-main/breaks' \ +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": "break-2026-001", + "id": "8c3f6a2e-5b1d-4e7a-9c48-2d6f0b1a3e57", "start": "2026-07-16T12:15:00.000Z", "duration": 120, "resumeOffset": 0, @@ -125,99 +67,82 @@ curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks' \ }' ``` -### Create from a Template +### Create from a template -Supply `templateId` to use a Template as the base. At creation time, the service merges the Template's stored `data` and duration with the request overrides, validates the result, and snapshots the resolved payload into the new Break's `data`. Later Template edits do not change an existing Break. +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. -Supported creation overrides are: +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. -- `id` -- `eventId` -- `start` -- `duration` -- `variant` -- `assetParameters` +You can override parts of the template per break. Supported overrides are `id`, `eventId`, `start`, `duration`, and `variant`: ```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks' \ +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": "break-from-template-001", + "id": "9e4b7c1d-2a8f-4d35-b6e9-7c0a5f2d8b14", "start": "2026-07-16T12:20:00.000Z", - "eventId": "event-2026-final", - "assetParameters": { - "airingId": "airing-001" - } + "duration": 60 }' ``` -### Create under an Event +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. -An `eventId` must identify an Event belonging to the same organization and channel. For wallclock channels, the Break must satisfy all of these conditions: +### Prepare your breaks under an event -- `start >= event.startDate` -- `start <= event.endDate` -- `start + duration <= event.endDate` +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. -PTS channels still require the Event to exist on the same organization and channel, but the service does not compare a numeric PTS start to the Event's wallclock window. +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: -### Scheduling constraints +- 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`. -The API rejects starts that are too close to, or behind, the effective playhead. It also rejects any overlap with an existing Break on the channel. These checks apply to direct and Template-based creation. +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. -## Lifecycle +### Scheduling constraints -The exact Break status values are: +OptiView Ads enforces a few constraints when scheduling breaks. Each one protects the viewer experience: -```text -PREPARING -CUED -READY -SIGNALED -ERROR -``` +- **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.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. -`errorMessage` contains the human-readable reason when a Break is moved to `ERROR`. +## Break Lifecycle -### Initial status +A break moves through a small set of states: -| Break kind | Start supplied? | Initial status | -| ---------------------------------------------------------------- | --------------- | -------------- | -| GAM vendor pod (`vendor: "gam"`, `vendorParameters.type: "pod"`) | Either | `PREPARING` | -| Non-vendor Break | Yes | `READY` | -| Non-vendor Break on a wallclock channel | No | `CUED` | +- **`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.mdx). +- **`ERROR`** — the break could not be delivered, for example because it passed its scheduling window before preparation completed. ![Break lifecycle diagram](../assets/img/breaks/break-lifecycle.svg) -### Status transitions and owners - -| Transition | Owner | Behavior | -| ------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -| `PREPARING → CUED` | Worker / EABN | After Google DAI decisioning, a Break without a timebase-appropriate start becomes CUED. | -| `PREPARING → READY` | Worker / EABN | After Google DAI decisioning, a Break with a timebase-appropriate start becomes READY. | -| `PREPARING → ERROR` | Worker / health worker | A missed unsignaled Break is failed with `Break passed its scheduling window before it could be signaled`. | -| `CUED → READY` | API punch | Punching assigns `startWallclock` and makes the Break eligible for delivery. | -| `READY → SIGNALED` | Manifest service | The Break Manifest includes READY and SIGNALED Breaks, then changes returned READY Breaks to SIGNALED. | -| `READY → SIGNALED` | Proxy | After injecting HLS cues, the proxy changes the injected READY Breaks to SIGNALED. The update is scoped to READY and is idempotent. | +At a high level, the transitions are: -The worker can also reset a superseded active Google Break from `READY` or `SIGNALED` back to `PREPARING` when it is still outside the decision margin. +| 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. | -## Cue and punch workflow +## Break punching -Vendor pod Breaks can be prepared before their exact start is known: +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. -1. Create a GAM vendor pod Break without a start on a wallclock channel. It starts in `PREPARING`. -2. The worker/EABN service pre-decides the Break with Google DAI. -3. After decisioning, the Break receives a `podId` and becomes `CUED`. -4. Punch the Break when it should fire. Punching sets `startWallclock` and changes the status to `READY`. +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. -Only wallclock channels support punch. A GAM CUED Break must have a `podId` from EABN decisioning before it can be punched. The application allows only one no-start Break in `PREPARING` or `CUED` per channel; creating another one fails. +Punching is only available on wallclock channels. ```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks/gam-cued-001/punch' \ +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' \ @@ -226,203 +151,186 @@ curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks/gam-cue }' ``` -If the body is omitted, the punch uses the current time. A requested past time is clamped to now. +The body is optional: if you omit it, the break starts now. A requested start in the past is clamped to now. -## Break payload (`data`) +## Break configuration -The stored `data` object has this shape: +This section describes the different possibilities of a break: its general properties, event based triggers, playback controls, layouts, variants, and the asset model. -```ts -type BreakData = { - duration: number; // required, seconds, >= 0 - resumeOffset?: number; // seconds, >= 0 - controls?: { - skipOffset?: number; // seconds, >= 0 - snapback?: boolean; - }; - variant: BreakVariant | BreakVariant[]; // one object or a non-empty array -}; -``` +### General -| Field | Type | Description | -| --------------------- | ----------------- | -------------------------------------------------------------- | -| `duration` | number | Required Break duration in seconds. | -| `resumeOffset` | number, optional | Resume offset in seconds. | -| `controls.skipOffset` | number, optional | Minimum elapsed time before skipping is allowed. | -| `controls.snapback` | boolean, optional | Enables snapback behavior. | -| `variant` | object or array | One layout variant, or a non-empty array of targeted variants. | +Every break carries these general properties: -## Layouts and variants +| 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). | -This is the canonical V2 layout and variant reference. Templates use the same payload model and should refer to this section rather than duplicate the layout definitions. +### Event based triggers -![OptiView Ads format overview](../assets/img/ads_formats.svg) +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": }`. -### Asset model +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. -Every asset has these common fields: +#### Start -```ts -{ - id: string; - mediaType: "video" | "image"; - mimeType?: string; - duration?: number; - interaction?: { - clickThrough?: string; - }; -} -``` +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. -`id` is generated as a UUID when omitted. Asset `type` is one of: +#### Pause -| `type` | Fields | -| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `static` | `uri`: a URL string or an array of `{ value, targeting? }` objects. | -| `vast` | `uri`: a URL string or an array of `{ value, targeting? }` objects. | -| `vendor` | `vendor: "gam"`, `vendorParameters`, optional `assetParameters`, and `uri`. GAM vendor parameters require `type: "pod"`. The default `uri` is `"placeholder"`. | +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. -For URI arrays, each entry can include optional device targeting: +#### End -```ts -{ - value: string; - targeting?: { - deviceType?: "desktop" | "tablet" | "mobile" | "tv"; - }; -} -``` +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 -### `single` +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](../assets/img/breaks/format-single.svg)](#single) | [![Double Box](../assets/img/breaks/format-double.svg)](#double-box) | [![L-shape ad](../assets/img/breaks/format-lshape-ad.svg)](#l-shape-ad) | +| :----------------------------------------------------------: | :------------------------------------------------------------------: | :---------------------------------------------------------------------: | +| [Single](#single) | [Double Box](#double-box) | [L-shape ad](#l-shape-ad) | + +| [![L-shape content](../assets/img/breaks/format-lshape-content.svg)](#l-shape-content) | [![Overlay](../assets/img/breaks/format-overlay.svg)](#overlay) | +| :------------------------------------------------------------------------------------: | :-------------------------------------------------------------: | +| [L-shape content](#l-shape-content) | [Overlay](#overlay) | + +#### Single ![Single format](../assets/img/breaks/format-single.svg) -The `single` variant contains a non-empty plain `assets` array: +`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. -```ts +```json { - format: "single"; - targeting?: { deviceType?: "desktop" | "tablet" | "mobile" | "tv" }; - assets: Asset[]; + "format": "single", + "assets": [ + { + "type": "static", + "mediaType": "video", + "uri": "https://cdn.example.com/ads/fullscreen.m3u8" + } + ] } ``` -Use a full-screen creative. Optimize the asset size for the player and supply companion imagery separately when the player experience requires it. - -### `double` +#### Double Box ![Double format](../assets/img/breaks/format-double.svg) -The `double` variant contains a non-empty array in which every entry has a primary asset and a `companion` asset: +`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`. -```ts +```json { - format: "double"; - targeting?: { deviceType?: "desktop" | "tablet" | "mobile" | "tv" }; - assets: Array; + "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" + } + } + ] } ``` -Use 16:9 companion imagery where possible. The double box is unsupported on many smart TVs; provide a single-format fallback for those devices. - -### `lshape_ad` +#### L-shape ad ![L-shape ad format](../assets/img/breaks/format-lshape-ad.svg) -The `lshape_ad` variant uses the same companion-bearing asset shape as `double`: +`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`. -```ts +```json { - format: "lshape_ad"; - targeting?: { deviceType?: "desktop" | "tablet" | "mobile" | "tv" }; - assets: Array; + "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" + } + } + ] } ``` -The ad occupies the smaller window and the companion asset supplies the remaining backdrop. Use 16:9 companion imagery and optimize image dimensions for the target player. - -### `lshape_content` +#### L-shape content ![L-shape content format](../assets/img/breaks/format-lshape-content.svg) -The `lshape_content` variant uses a non-empty plain asset array: +`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. -```ts +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"; - targeting?: { deviceType?: "desktop" | "tablet" | "mobile" | "tv" }; - assets: Asset[]; + "format": "lshape_content", + "assets": [ + { + "type": "static", + "mediaType": "image", + "uri": "https://cdn.example.com/ads/lshape-ad.png" + } + ] } ``` -The live content occupies the smaller window and the remaining area is supplied by the layout's companion/backdrop treatment. - -### `overlay` +#### Overlay ![Overlay format](../assets/img/breaks/format-overlay.svg) -The `overlay` variant uses a non-empty plain asset array plus required position and size objects: +`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. -```ts +```json { - format: "overlay"; - targeting?: { deviceType?: "desktop" | "tablet" | "mobile" | "tv" }; - assets: Asset[]; - position: { - top?: number; - bottom?: number; - left?: number; - right?: number; - }; - size: { - width: number; - height: number; - }; - opacity?: number; + "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 } ``` -`position` requires at least one of `top` or `bottom` and at least one of `left` or `right`. All position and size values are fractions from `0` through `1`, not percentages. `opacity`, when supplied, is also a fraction from `0` through `1`. +### Variants -### Multiple variants and device targeting - -Set `variant` to an array when one Break contains multiple layouts for different devices. Each variant can have an optional `targeting.deviceType` value: +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": "single", - "targeting": { - "deviceType": "mobile" - }, - "assets": [ - { - "type": "static", - "mediaType": "video", - "uri": "https://cdn.example.com/ads/mobile.m3u8" - } - ] - }, - { - "format": "single", - "targeting": { - "deviceType": "tv" - }, - "assets": [ - { - "type": "static", - "mediaType": "video", - "uri": "https://cdn.example.com/ads/tv.m3u8" - } - ] - }, { "format": "double", - "targeting": { - "deviceType": "desktop" - }, + "targeting": { "deviceType": "desktop" }, "assets": [ { "type": "static", @@ -437,174 +345,122 @@ Set `variant` to an array when one Break contains multiple layouts for different ] }, { - "format": "double", - "targeting": { - "deviceType": "tablet" - }, + "format": "single", + "targeting": { "deviceType": "mobile" }, "assets": [ { "type": "static", "mediaType": "video", - "uri": "https://cdn.example.com/ads/tablet.m3u8", - "companion": { - "type": "static", - "mediaType": "image", - "uri": "https://cdn.example.com/ads/tablet-companion.jpg" - } + "uri": "https://cdn.example.com/ads/mobile.m3u8" } ] - } - ] -} -``` - -## Delivery overview - -### Break Manifest polling - -The Manifest Service returns `READY` and `SIGNALED` Breaks that remain within the channel's DVR window. It changes returned `READY` Breaks to `SIGNALED` and emits the timebase-specific start, duration, controls, resume offset, and variant data. Players poll the Manifest according to the channel's advertised idle and active polling intervals. - -### SSAI cue injection - -For wallclock GAM pod Breaks on channels with an SSAI DAI integration, the Proxy injects HLS `EXT-X-DATERANGE` OUT and IN cues into the media playlist. After the cues are written, it changes the injected Breaks from `READY` to `SIGNALED`. - -`PREPARING`, `CUED`, and `ERROR` Breaks are not delivered through either path. - -## API usage - -All examples use the same organization-scoped Basic authentication as the Channels API. - -### Create directly - -```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "id": "break-api-001", - "start": "2026-07-16T12:30:00.000Z", - "duration": 60, - "controls": { - "skipOffset": 10, - "snapback": false }, - "variant": { + { "format": "single", "assets": [ { - "type": "vast", + "type": "static", "mediaType": "video", - "uri": "https://ads.example.com/vast/creative-001.xml" + "uri": "https://cdn.example.com/ads/default.m3u8" } ] } - }' + ] +} ``` -### Create from a Template +The player picks the variant to render: -```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "templateId": "template-sports-spot", - "start": "2026-07-16T12:31:00.000Z", - "duration": 45 - }' -``` +- 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. -### List Breaks +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. -```bash -curl 'https://ads.example.com/api/v1/channels/sports-main/breaks?page=1&pageSize=20' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` +### Asset model -`page` defaults to `1`; `pageSize` defaults to `20` and has a maximum of `100`. Lists also accept the optional RSQL `filter` and `sort` parameters. +An asset describes one piece of ad media inside a variant. Every asset shares these base properties: -Filterable fields are: +| 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. | -```text -wallclock, assetType, format, eventId, templateId, duration, status, originId -``` +#### Static assets -Sortable fields are: +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: -```text -wallclock, duration, status, createdAt +```json +{ + "type": "static", + "mediaType": "video", + "mimeType": "application/x-mpegurl", + "uri": "https://cdn.example.com/ads/spot.m3u8" +} ``` -Filter by one status: - -```bash -curl 'https://ads.example.com/api/v1/channels/sports-main/breaks?filter=status==READY' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` +#### VAST assets -Filter by either READY or SIGNALED: +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`: -```bash -curl 'https://ads.example.com/api/v1/channels/sports-main/breaks?filter=status=in=(READY,SIGNALED)' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' +```json +{ + "type": "vast", + "mediaType": "video", + "mimeType": "application/xml", + "uri": "https://adserver.example.com/vast/creative-001.xml" +} ``` -The `/active` and `/current` variants are also available: - -```text -GET /api/v1/channels/{channelId}/breaks/active -GET /api/v1/channels/{channelId}/breaks/current -``` +#### Vendor assets -### Get one Break +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: -```bash -curl 'https://ads.example.com/api/v1/channels/sports-main/breaks/break-api-001' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' +```json +{ + "type": "vendor", + "vendor": "gam", + "mediaType": "video", + "vendorParameters": { + "type": "pod" + }, + "assetParameters": { + "airingId": "airing-001" + } +} ``` -### Delete one Break +`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 Ad Manager](../integrations/google.mdx) for the supported values. -```bash -curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main/breaks/break-api-001' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` +#### Asset URI targeting -### Bulk delete Breaks +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: -```bash -curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main/breaks' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "ids": ["break-api-001", "break-api-002"] - }' +```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" } + ] +} ``` -### Punch a CUED Break - -```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks/gam-cued-001/punch' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "start": "2026-07-16T12:35:00.000Z" - }' -``` +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. -## See also +## Related resources -- [Channels](/ads/concepts/channels) — channel timebases, polling policy, origins, marker detection, and delivery integrations. -- Templates — reusable Break definitions and Template-based scheduling. -- Events — event windows and event-scoped Breaks. -- Marker Detection — automatic marker evaluation and Break provenance. -- Integrations and Google DAI — vendor pod decisioning and delivery. +| 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.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 index aa101e371742..036f880fd5b2 100644 --- a/ads/concepts/channels.mdx +++ b/ads/concepts/channels.mdx @@ -34,8 +34,8 @@ These channel settings determine how break start times are interpreted and how b The `timebase` determines which timeline break start times are expressed on: -- **`wallclock`** — breaks are scheduled based on the stream's wallclock timing: the break's start (`startWallclock`, a UTC timestamp) 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 against the encoder's presentation timestamp timeline (`startPts`). The player needs to retrieve 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). +- **`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. @@ -73,8 +73,8 @@ A channel is the parent or lookup point for the rest of the OptiView Ads model: | ------------------------------------------ | -------------------------------------------------------------------------------------- | | [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) | Prepare breaks for an event on your live stream. | +| [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 endpoint that announces the channel's breaks to players. | +| [Break Manifest](./break-manifest.mdx) | The manifest that announces the channel's breaks to players. | | [Integrations](../integrations/google.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 index 8f6c94a909c6..7b83dbca34f7 100644 --- a/ads/concepts/events.mdx +++ b/ads/concepts/events.mdx @@ -121,7 +121,7 @@ Events live under a channel. Replace `sports-main` with your channel ID. Dashboard: open the channel, then **Events → New**. ```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/events' \ +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/events' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -150,7 +150,7 @@ Example response: ### Get an event ```bash -curl 'https://ads.example.com/api/v1/channels/sports-main/events/finals-2026' \ +curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/events/finals-2026' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -160,7 +160,7 @@ curl 'https://ads.example.com/api/v1/channels/sports-main/events/finals-2026' \ `id` cannot be changed. When you send `startDate` or `endDate`, the resulting window must still keep `startDate` before `endDate`. ```bash -curl -X PATCH 'https://ads.example.com/api/v1/channels/sports-main/events/finals-2026' \ +curl -X PATCH 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/events/finals-2026' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -173,7 +173,7 @@ curl -X PATCH 'https://ads.example.com/api/v1/channels/sports-main/events/finals ### List events ```bash -curl 'https://ads.example.com/api/v1/channels/sports-main/events?page=1&pageSize=20&sort=-createdAt' \ +curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/events?page=1&pageSize=20&sort=-createdAt' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -190,7 +190,7 @@ List endpoints share the same pagination shape. Events can be filtered by `name` ### List breaks for an event ```bash -curl 'https://ads.example.com/api/v1/channels/sports-main/events/finals-2026/breaks?pageSize=50' \ +curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/events/finals-2026/breaks?pageSize=50' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -198,7 +198,7 @@ curl 'https://ads.example.com/api/v1/channels/sports-main/events/finals-2026/bre ### Delete an event ```bash -curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main/events/finals-2026' \ +curl -X DELETE 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/events/finals-2026' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -206,7 +206,7 @@ curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main/events/final This also deletes every break attached to the event. To delete several events (and their breaks) at once, send their IDs to the collection endpoint: ```bash -curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main/events' \ +curl -X DELETE 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/events' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -218,7 +218,7 @@ curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main/events' \ Cue a vendor pod break under the event by omitting `start`: ```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks' \ +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/breaks' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -243,7 +243,7 @@ curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks' \ At the right moment, punch it. With no body the start defaults to now: ```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks/halftime-1/punch' \ +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/breaks/halftime-1/punch' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -251,7 +251,7 @@ curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks/halftim To punch with an explicit start (a past start is clamped to now): ```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks/halftime-1/punch' \ +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/breaks/halftime-1/punch' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -260,9 +260,9 @@ curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks/halftim ## Related resources -| Resource | Relationship | -| --------------------- | ----------------------------------------------------------------------------------------------- | -| Channels | The parent of an event. An event always belongs to one channel. See [Channels](./channels.mdx). | -| Breaks | Attached to an event via `eventId`; the Breaks section documents the full status state machine. | -| Templates | Linked to events via `eventIds` for quick scheduling. | -| Integrations / Google | Provide the pod pre-decisioning that moves a cued vendor pod break from `PREPARING` to `CUED`. | +| 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.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 index 091d160ab6b0..a6345c0ed149 100644 --- a/ads/concepts/marker-detection.mdx +++ b/ads/concepts/marker-detection.mdx @@ -20,13 +20,13 @@ Marker rules can be toggled with **Enable marker rule** and **Disable marker rul `detectionEnabled` is read-only on channel create and update requests. Toggle automatic detection with the dedicated channel actions: ```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/detection/enable' \ +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/detection/enable' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` ```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/detection/disable' \ +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/detection/disable' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -75,7 +75,7 @@ For example, this rule matches DATERANGE markers whose `CLASS` attribute is `com There is no dedicated marker-rule enable or disable endpoint. The Dashboard **Enable marker rule** / **Disable marker rule** actions map to a normal update: ```bash -curl -X PATCH 'https://ads.example.com/api/v1/channels/sports-main/markerRules/rule-123' \ +curl -X PATCH 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/markerRules/rule-123' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -98,7 +98,7 @@ All marker-rule endpoints are scoped to a channel: ### Create a marker rule ```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/markerRules' \ +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/markerRules' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -132,7 +132,7 @@ Example response: Use the same endpoint to change rule configuration or enable/disable participation: ```bash -curl -X PATCH 'https://ads.example.com/api/v1/channels/sports-main/markerRules/rule-123' \ +curl -X PATCH 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/markerRules/rule-123' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -145,7 +145,7 @@ curl -X PATCH 'https://ads.example.com/api/v1/channels/sports-main/markerRules/r ### List marker rules ```bash -curl 'https://ads.example.com/api/v1/channels/sports-main/markerRules?page=1&pageSize=20&sort=-createdAt' \ +curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/markerRules?page=1&pageSize=20&sort=-createdAt' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -162,7 +162,7 @@ List endpoints use the shared pagination shape: ### Bulk delete marker rules ```bash -curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main/markerRules' \ +curl -X DELETE 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/markerRules' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -181,7 +181,7 @@ Detection history is the audit trail of what automatic detection decided for eac ### List detection history ```bash -curl 'https://ads.example.com/api/v1/channels/sports-main/detection/history?page=1&pageSize=20&sort=-createdAt' \ +curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/detection/history?page=1&pageSize=20&sort=-createdAt' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -247,7 +247,7 @@ History is deduplicated per channel. Repeated polling of the same marker, includ 1. Add and enable an HLS origin for `sports-main`. See [Origins](./origins.mdx). ```bash - curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/origins' \ + curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -265,7 +265,7 @@ History is deduplicated per channel. Repeated polling of the same marker, includ 3. Create an enabled marker rule for a matching HLS marker: ```bash - curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/markerRules' \ + curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/markerRules' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -281,7 +281,7 @@ History is deduplicated per channel. Repeated polling of the same marker, includ 4. Enable detection on the channel: ```bash - curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/detection/enable' \ + curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/detection/enable' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -291,7 +291,7 @@ History is deduplicated per channel. Repeated polling of the same marker, includ 6. Confirm the result in Detection history: ```bash - curl 'https://ads.example.com/api/v1/channels/sports-main/detection/history?page=1&pageSize=20&sort=-createdAt' \ + curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/detection/history?page=1&pageSize=20&sort=-createdAt' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` diff --git a/ads/concepts/origins.mdx b/ads/concepts/origins.mdx index 78ce11409faa..8a8d07dd3b30 100644 --- a/ads/concepts/origins.mdx +++ b/ads/concepts/origins.mdx @@ -43,7 +43,7 @@ Dashboard: open the channel, then use **Origins → Add**. API: ```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/origins' \ +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -74,7 +74,7 @@ Example response: Add a lower-priority backup origin so detection can fall back if the primary source is unreachable: ```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/origins' \ +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -90,7 +90,7 @@ curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/origins' \ ## Get an origin ```bash -curl 'https://ads.example.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f' \ +curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -100,7 +100,7 @@ curl 'https://ads.example.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b- The update endpoint accepts `url`, `type`, `name`, and `priority`. It does not accept `enabled`. ```bash -curl -X PATCH 'https://ads.example.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f' \ +curl -X PATCH 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -117,13 +117,13 @@ Dashboard: **Origins → Enable origin** / **Disable origin**. API: ```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f/enable' \ +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f/enable' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` ```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f/disable' \ +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f/disable' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -133,7 +133,7 @@ Disabling an origin removes it from detection immediately. The origin record is ## List origins ```bash -curl 'https://ads.example.com/api/v1/channels/sports-main/origins?page=1&pageSize=20&sort=priority' \ +curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins?page=1&pageSize=20&sort=priority' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -150,7 +150,7 @@ List endpoints use the shared pagination shape. ## Delete an origin ```bash -curl -X DELETE 'https://ads.example.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f' \ +curl -X DELETE 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` diff --git a/ads/concepts/templates.mdx b/ads/concepts/templates.mdx index 4e18c9b627e3..bffb401acf7a 100644 --- a/ads/concepts/templates.mdx +++ b/ads/concepts/templates.mdx @@ -87,7 +87,7 @@ Dashboard: **Ads → Templates → New**. API: ```bash -curl -X POST 'https://ads.example.com/api/v1/templates' \ +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/templates' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -102,7 +102,7 @@ curl -X POST 'https://ads.example.com/api/v1/templates' \ { "type": "vast", "mediaType": "video", - "uri": "https://ads.example.com/vast/midroll.xml" + "uri": "https://adserver.example.com/vast/midroll.xml" } ] } @@ -123,7 +123,7 @@ Example response: { "type": "vast", "mediaType": "video", - "uri": "https://ads.example.com/vast/midroll.xml" + "uri": "https://adserver.example.com/vast/midroll.xml" } ] }, @@ -134,7 +134,7 @@ Example response: ## Get a template ```bash -curl 'https://ads.example.com/api/v1/templates/midroll-30s' \ +curl 'https://us.ads.optiview.dolby.com/api/v1/templates/midroll-30s' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -142,7 +142,7 @@ curl 'https://ads.example.com/api/v1/templates/midroll-30s' \ ## Update a template ```bash -curl -X PATCH 'https://ads.example.com/api/v1/templates/midroll-30s' \ +curl -X PATCH 'https://us.ads.optiview.dolby.com/api/v1/templates/midroll-30s' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -157,7 +157,7 @@ Updating a template does not change breaks already scheduled from it — see [Sn ## List templates ```bash -curl 'https://ads.example.com/api/v1/templates?page=1&pageSize=20&sort=-createdAt' \ +curl 'https://us.ads.optiview.dolby.com/api/v1/templates?page=1&pageSize=20&sort=-createdAt' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -185,21 +185,21 @@ Examples: ```bash # Overlay templates only -curl 'https://ads.example.com/api/v1/templates?filter=format==overlay' \ +curl 'https://us.ads.optiview.dolby.com/api/v1/templates?filter=format==overlay' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` ```bash # Short VAST templates (30s or less), sorted by duration -curl 'https://ads.example.com/api/v1/templates?filter=duration=le=30;assetType==vast&sort=duration' \ +curl 'https://us.ads.optiview.dolby.com/api/v1/templates?filter=duration=le=30;assetType==vast&sort=duration' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` ```bash # Templates that use a vendor asset (for example, Google Ad Manager pods) -curl 'https://ads.example.com/api/v1/templates?filter=vendor==gam' \ +curl 'https://us.ads.optiview.dolby.com/api/v1/templates?filter=vendor==gam' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -209,7 +209,7 @@ Combine multiple conditions with `;`. ## Delete a template ```bash -curl -X DELETE 'https://ads.example.com/api/v1/templates/midroll-30s' \ +curl -X DELETE 'https://us.ads.optiview.dolby.com/api/v1/templates/midroll-30s' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'X-Org-ID: org_123' ``` @@ -217,7 +217,7 @@ curl -X DELETE 'https://ads.example.com/api/v1/templates/midroll-30s' \ Delete multiple templates in one request: ```bash -curl -X DELETE 'https://ads.example.com/api/v1/templates' \ +curl -X DELETE 'https://us.ads.optiview.dolby.com/api/v1/templates' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -231,7 +231,7 @@ Deletes are permanent (hard delete). Existing breaks scheduled from the template Create a break on a channel and reference the template with `templateId`. The template payload is snapshotted onto the break at creation. ```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/breaks' \ +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/breaks' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -259,7 +259,7 @@ The created break records the source `templateId` alongside its own copied paylo { "type": "vast", "mediaType": "video", - "uri": "https://ads.example.com/vast/midroll.xml" + "uri": "https://adserver.example.com/vast/midroll.xml" } ] }, diff --git a/ads/integrations/google.mdx b/ads/integrations/google.mdx index 662a9240a997..6e230157a256 100644 --- a/ads/integrations/google.mdx +++ b/ads/integrations/google.mdx @@ -81,7 +81,7 @@ One or more daiAssetKeys are already used by another channel integration Create an integration with the self-serve API: ```bash -curl -X POST 'https://ads.example.com/api/v1/channels/sports-main/integrations' \ +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/integrations' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -156,6 +156,9 @@ If a break never leaves `PREPARING`, EABN may be skipping the signal because the ## Related resources -- [Channels](../../concepts/channels) -- [Scheduling breaks](../../how-to-guides/scheduling-breaks) -- [API reference](/ads/api) +| Resource | Relationship | +| ------------------------------------------------ | -------------------------------------------------------------------------- | +| [Channels](../concepts/channels.mdx) | Hold the `customAssetKey` used for Google server-guided pod serving. | +| [Breaks](../concepts/breaks.mdx) | Carry the vendor asset that Google 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. | From d53bf9ca214b01be42c08c926d7fada7adf1c36a Mon Sep 17 00:00:00 2001 From: "maarten.rimaux" Date: Fri, 14 Aug 2026 12:12:19 +0000 Subject: [PATCH 18/22] Rework templates page and align concept pages for customer-facing docs Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ads/concepts/break-manifest.mdx | 4 +- ads/concepts/events.mdx | 237 ++----------------------- ads/concepts/marker-detection.mdx | 281 ++++-------------------------- ads/concepts/origins.mdx | 158 ++--------------- ads/concepts/templates.mdx | 260 ++++----------------------- ads/integrations/google.mdx | 61 +++---- 6 files changed, 114 insertions(+), 887 deletions(-) diff --git a/ads/concepts/break-manifest.mdx b/ads/concepts/break-manifest.mdx index fed8b5665394..2e30b1c9e6b6 100644 --- a/ads/concepts/break-manifest.mdx +++ b/ads/concepts/break-manifest.mdx @@ -172,7 +172,7 @@ For a channel created with `timebase: wallclock`, each break `start` is a UTC IS }, "breaks": [ { - "id": "break-1", + "id": "9c1e2b34-5d6f-4a78-9b0c-1d2e3f4a5b6c", "start": "2026-07-16T12:30:00.000Z", // UTC wallclock start of the break "duration": 30, // seconds "resumeOffset": 0, // resume content at the break-in point @@ -210,7 +210,7 @@ For a channel created with `timebase: pts`, each break `start` is a numeric pres }, "breaks": [ { - "id": "break-9", + "id": "4d5e6f70-8a9b-4c1d-a2b3-c4d5e6f70819", "start": 5400000, // numeric PTS start on the channel media clock "duration": 30, // seconds "variant": [ diff --git a/ads/concepts/events.mdx b/ads/concepts/events.mdx index 7b83dbca34f7..b4afbeb8fb25 100644 --- a/ads/concepts/events.mdx +++ b/ads/concepts/events.mdx @@ -7,256 +7,45 @@ sidebar_label: 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 operational cue/punch workflow. Ahead of a live occurrence you prepare vendor pod breaks under the event without a start time, and during the broadcast you fire them at the exact moment with the punch endpoint. - -Events are scoped to an organization and a channel. API calls identify the organization with the `X-Org-ID` header and authenticate with an API key and secret using HTTP Basic authentication. - -## Dashboard path - -In the OptiView Unified Dashboard, open **Ads → Channels**, open a channel, then select **Events** from the channel navigation. From there you can create, edit, and delete events, and inspect the breaks scheduled under each event. +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`. The API stores it together with the organization ID and the parent channel ID, so the unique identity is: - -```text -organizationId + channelId + eventId -``` - -Because the identity includes the channel, the same `id` can exist under different channels. Use stable, descriptive event IDs that match your operational names, such as `finals-2026` or `week-1-home`. If you omit `id` on creation, the API generates one. +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`. -The window is enforced on the breaks scheduled under the event. When you create a break with an explicit start on a `wallclock` channel and attach it to an event, the API validates that the **entire** break interval fits inside the window: +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`. -If either check fails, the create request is rejected: - -| Condition | Response | -| --------------------------------- | ----------------------------------------------------------------------------- | -| Break start is outside the window | `Break start must be within the event's date range ( - )` | -| Break end is outside the window | `Break end time (start + duration) must be within the event's date range (…)` | - -The window validation applies to `wallclock` channels. On `pts` channels the event must still exist, but the break is not range-checked against the event dates. See [Channels](./channels.mdx) for the timebase model. - -A break created **without** a start time (a cued break, see below) is not range-checked at creation, because its start is not known yet. Its start is set when you punch it. +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. ## Relationship to breaks 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 cascades to every break whose `eventId` matches it. The event and its member breaks are removed together in a single transaction. Bulk-deleting events removes the breaks of all deleted events. There is no confirmation step in the API — delete an event only after confirming that none of its breaks are still needed. +Deleting an event also removes every break attached to it. Delete an event only after confirming that none of its breaks are still needed. ::: -To list only the breaks that belong to an event, use the event's breaks endpoint (see [API usage](#api-usage)). - -## Cue / punch workflow during an event - -For a live occurrence you usually do not know the exact break times in advance, but you want the ad decision ready so the break can fire instantly. Events are where this "prepare ahead, fire live" workflow lives. The full break state machine and the Google DAI (vendor pod) prerequisites are documented in the Breaks and Integrations / Google sections; the flow below focuses on running an event. - -### Ahead of the event: prepare cued breaks - -Create the vendor pod breaks under the event **without a `start`**. A vendor pod break with no start begins in `PREPARING`: OptiView Ads asks Google DAI to pre-decision the pod. Once the pod is decisioned, the break transitions to `CUED` and is ready to fire. - -A channel can hold **only one cued break at a time**. While a no-start break is `PREPARING` or `CUED` on a channel, creating another no-start break on the same channel is rejected: - -```text -Channel already has a CUED break -``` +## Break punching during an event -Punch (or delete) the outstanding break before cueing the next one. +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: -### During the event: punch the cued break +1. Create the event with a window that covers the occurrence, for example kickoff through the final whistle. +2. Ahead of the occurrence, create a break under the event without a `start`. OptiView Ads prepares it (including any ad decisioning) and it waits in the `CUED` state. +3. At the right moment — for example, half-time — punch the break. Its start is set and it is announced to players right away. +4. To prepare the next break, first punch (or delete) the current cued break, then cue the next one — a channel holds only one cued break at a time. -When the moment arrives, fire the cued break with the punch endpoint: - -```text -POST /api/v1/channels/:channelId/breaks/:breakId/punch -``` - -The request body is optional. It may contain a single `start` (UTC ISO 8601). If `start` is omitted it defaults to now, and a `start` in the past is clamped to now. A successful punch sets the break's start and transitions it from `CUED` to `READY`, after which it is delivered. - -Punching has these constraints: - -- **Wallclock only.** The channel must use the `wallclock` timebase. Punching a `pts` channel is rejected with `Only channels with a 'wallclock' timebase are allowed to punch breaks.` -- **Must be cued.** The break must be in `CUED` status; otherwise the request fails with `Break '' is not in CUED status`. -- **Pod must be decisioned.** For a vendor pod break, the Google DAI pod decision must have completed (the break must have left `PREPARING`); otherwise the request fails with `Ad break '' is not yet decisioned by EABN`. - -Because a punch clamps the start to the current time, punch a cued break only while the event is in progress. This keeps the break's start inside the event's `startDate`/`endDate` window. - -### Worked example: half-time break in a live game - -1. Create the event for the game with a window that covers kickoff through the final whistle. -2. Ahead of kickoff, create a vendor pod break under the event with no `start`. It enters `PREPARING`, then `CUED` once Google DAI has pre-decisioned the pod. -3. At half-time, punch the break with no body. Its start is set to now and it transitions to `READY`, so the pod is delivered immediately. -4. To prepare the next in-game break, first punch or delete the current cued break, then cue the next one — a channel holds only one cued break at a time. +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. ## Relationship to templates -Templates can be linked to one or more events through their `eventIds` array, so a reusable break preset can be surfaced for quick scheduling under those events. Listing templates for an event returns every template whose `eventIds` contains the event's `id`. - -## Configuration reference - -| Field | Type | Required | Description | -| ------------- | --------------- | -------- | ----------------------------------------------------------- | -| `id` | string | No | Customer-facing event ID. Generated if omitted on creation. | -| `name` | string | Yes | Human-readable event name. Must be non-empty. | -| `description` | string | No | Optional free-text description. | -| `startDate` | ISO 8601 string | Yes | Start of the event window. Must be before `endDate`. | -| `endDate` | ISO 8601 string | Yes | End of the event window. | - -The event response returns `id`, `name`, `description`, `startDate`, `endDate`, and `createdAt`. The organization and channel IDs are taken from the request context and are not part of the response body. - -## API usage - -Events live under a channel. Replace `sports-main` with your channel ID. - -### Create an event - -Dashboard: open the channel, then **Events → New**. - -```bash -curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/events' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "id": "finals-2026", - "name": "Finals 2026", - "description": "Championship final", - "startDate": "2026-07-20T18:00:00.000Z", - "endDate": "2026-07-20T22:00:00.000Z" - }' -``` - -Example response: - -```json -{ - "id": "finals-2026", - "name": "Finals 2026", - "description": "Championship final", - "startDate": "2026-07-20T18:00:00.000Z", - "endDate": "2026-07-20T22:00:00.000Z", - "createdAt": "2026-07-16T12:00:00.000Z" -} -``` - -### Get an event - -```bash -curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/events/finals-2026' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -### Update an event - -`id` cannot be changed. When you send `startDate` or `endDate`, the resulting window must still keep `startDate` before `endDate`. - -```bash -curl -X PATCH 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/events/finals-2026' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "name": "Finals 2026 (delayed)", - "endDate": "2026-07-20T23:00:00.000Z" - }' -``` - -### List events - -```bash -curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/events?page=1&pageSize=20&sort=-createdAt' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -List endpoints share the same pagination shape. Events can be filtered by `name`, `description`, `startDate`, and `endDate`, and sorted by `name`, `description`, `startDate`, `endDate`, or `createdAt`. - -| Query parameter | Default | Description | -| --------------- | ------------ | -------------------------------------------------------------------------- | -| `page` | `1` | Page number. | -| `pageSize` | `20` | Items per page. Maximum `100`. | -| `filter` | none | Optional RSQL filter expression. | -| `sort` | `-createdAt` | Comma-separated sort fields. Prefix a field with `-` for descending order. | - -### List breaks for an event - -```bash -curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/events/finals-2026/breaks?pageSize=50' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -### Delete an event - -```bash -curl -X DELETE 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/events/finals-2026' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -This also deletes every break attached to the event. To delete several events (and their breaks) at once, send their IDs to the collection endpoint: - -```bash -curl -X DELETE 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/events' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ "ids": ["finals-2026", "semifinal-2026"] }' -``` - -### Cue and punch a break in an event context - -Cue a vendor pod break under the event by omitting `start`: - -```bash -curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/breaks' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "id": "halftime-1", - "eventId": "finals-2026", - "duration": 90, - "variant": { - "format": "single", - "assets": [ - { - "type": "vendor", - "vendor": "gam", - "mediaType": "video", - "vendorParameters": { "type": "pod" } - } - ] - } - }' -``` - -At the right moment, punch it. With no body the start defaults to now: - -```bash -curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/breaks/halftime-1/punch' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -To punch with an explicit start (a past start is clamped to now): - -```bash -curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/breaks/halftime-1/punch' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ "start": "2026-07-20T20:00:00.000Z" }' -``` +[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. ## Related resources diff --git a/ads/concepts/marker-detection.mdx b/ads/concepts/marker-detection.mdx index a6345c0ed149..cdc153bf5729 100644 --- a/ads/concepts/marker-detection.mdx +++ b/ads/concepts/marker-detection.mdx @@ -5,231 +5,59 @@ 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, in the Dashboard under **Break Detection**. V2 detection currently supports HLS manifests only. See [Origins](./origins.mdx) for origin selection, priority ordering, and first-online behavior. +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. -Channels are scoped to an organization. API calls identify the organization with the `X-Org-ID` header and authenticate with an API key and secret using HTTP Basic authentication. +## How detection works -## Dashboard path - -Open the channel and select **Break Detection**. Use this area to configure marker rules and review **Detection history**. - -Marker rules can be toggled with **Enable marker rule** and **Disable marker rule**. These Dashboard actions use the marker-rule update endpoint with the `enabled` field; there are no dedicated marker-rule enable or disable endpoints. - -## Detection lifecycle - -`detectionEnabled` is read-only on channel create and update requests. Toggle automatic detection with the dedicated channel actions: - -```bash -curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/detection/enable' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -```bash -curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/detection/disable' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -When detection is enabled, a scheduler polls the channel's enabled origins in priority order. The first online origin is selected for the cycle. The worker parses its markers, evaluates enabled marker rules, creates breaks for matching markers, and records the result in Detection history. +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 -V2 marker detection supports HLS only. It recognizes two marker kinds: +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. `CUE-IN` and `CUE-SPAN` are ignored. | +| `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. The rule's `type` must match the marker kind, and every configured condition must match the marker attributes. Attribute keys are compared case-insensitively. +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. -### Configuration reference +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. -| Field | Type | Default | Description | -| ----------------- | -------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- | -| `streamType` | enum | Required | Currently only `HLS` is supported. | -| `type` | enum: `CUE` or `DATERANGE` | Required | Marker kind this rule matches. The value must be valid for the selected `streamType`. | -| `conditions` | object (map of string to string) | Required | 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` | string | Required | Non-empty ID of the break template to instantiate. The template must exist and be available to the channel. | -| `assetParameters` | object (map of string to string) | none | Optional parameters merged into the created break body, such as ad-targeting parameters passed downstream. | -| `enabled` | boolean | `true` | Whether the rule participates in detection. | +| 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`: +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": "preroll-30s", - "assetParameters": { "adType": "midroll" }, + "templateId": "3b8e5f0a-7c2d-4e91-a6b3-9d4f1c8e2a70", "enabled": true } ``` -There is no dedicated marker-rule enable or disable endpoint. The Dashboard **Enable marker rule** / **Disable marker rule** actions map to a normal update: - -```bash -curl -X PATCH 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/markerRules/rule-123' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ "enabled": false }' -``` - -## Marker rule endpoints - -All marker-rule endpoints are scoped to a channel: - -| Operation | Method | Path | -| ----------- | -------- | ------------------------------------------------------- | -| List | `GET` | `/api/v1/channels/:channelId/markerRules` | -| Get | `GET` | `/api/v1/channels/:channelId/markerRules/:markerRuleId` | -| Create | `POST` | `/api/v1/channels/:channelId/markerRules` | -| Update | `PATCH` | `/api/v1/channels/:channelId/markerRules/:markerRuleId` | -| Delete | `DELETE` | `/api/v1/channels/:channelId/markerRules/:markerRuleId` | -| Bulk delete | `DELETE` | `/api/v1/channels/:channelId/markerRules` | - -### Create a marker rule - -```bash -curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/markerRules' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "streamType": "HLS", - "type": "DATERANGE", - "conditions": { "CLASS": "com.example.ad" }, - "templateId": "preroll-30s", - "assetParameters": { "adType": "midroll" }, - "enabled": true - }' -``` - -Example response: - -```json -{ - "id": "rule-123", - "streamType": "HLS", - "type": "DATERANGE", - "conditions": { "CLASS": "com.example.ad" }, - "templateId": "preroll-30s", - "assetParameters": { "adType": "midroll" }, - "enabled": true, - "createdAt": "2026-07-16T12:00:00.000Z" -} -``` - -### Update a marker rule - -Use the same endpoint to change rule configuration or enable/disable participation: - -```bash -curl -X PATCH 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/markerRules/rule-123' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "conditions": { "CLASS": "com.example.ad", "X-CAMPAIGN": "sports" }, - "enabled": true - }' -``` - -### List marker rules - -```bash -curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/markerRules?page=1&pageSize=20&sort=-createdAt' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -List endpoints use the shared pagination shape: - -| Query parameter | Default | Description | -| --------------- | ------------ | -------------------------------------------------------------------------- | -| `page` | `1` | Page number. | -| `pageSize` | `20` | Items per page. Maximum `100`. | -| `filter` | none | Optional RSQL filter expression. | -| `sort` | `-createdAt` | Comma-separated sort fields. Prefix a field with `-` for descending order. | - -### Bulk delete marker rules - -```bash -curl -X DELETE 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/markerRules' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ "ids": ["rule-123", "rule-456"] }' -``` - ## Detection history -Detection history is the audit trail of what automatic detection decided for each marker. - -| Operation | Method | Path | -| --------- | ------ | ------------------------------------------------------------------ | -| List | `GET` | `/api/v1/channels/:channelId/detection/history` | -| Get | `GET` | `/api/v1/channels/:channelId/detection/history/:markerDetectionId` | - -### List detection history - -```bash -curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/detection/history?page=1&pageSize=20&sort=-createdAt' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -### Detection history response fields - -| Field | Type | Description | -| -------------- | ------ | -------------------------------------------- | -| `id` | string | Detection-history record ID. | -| `originId` | string | Origin that supplied the marker. | -| `markerRuleId` | string | Present when an enabled marker rule matched. | -| `breakId` | string | Present when a break was created. | -| `action` | enum | `CREATED`, `SKIPPED`, or `FAILED`. | -| `marker` | string | The raw manifest tag line. | -| `reason` | string | Optional machine-readable reason. | -| `createdAt` | string | Creation timestamp. | - -Example response row: - -```json -{ - "id": "detection-789", - "originId": "origin-123", - "markerRuleId": "rule-123", - "breakId": "break-456", - "action": "CREATED", - "marker": "#EXT-X-DATERANGE:ID=\"ad-1\",CLASS=\"com.example.ad\",START-DATE=\"2026-07-16T12:00:00.000Z\",DURATION=30", - "createdAt": "2026-07-16T12:00:01.000Z" -} -``` - -### Action values +Detection history is the audit trail of what automatic detection decided for each marker. Every processed marker is recorded with one of three outcomes: -| Action | Meaning | -| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `CREATED` | A rule matched and a break was scheduled. `markerRuleId` and `breakId` are set. | -| `SKIPPED` | No fault: the marker was ineligible because it was unparseable or had no resolvable start; no rule matched; no rules were configured; or an expected scheduling condition prevented creation. | -| `FAILED` | An eligible, rule-matched marker could not be scheduled for an unexpected reason such as misconfiguration, invalid data, or infrastructure failure. | +| 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. | -Common `reason` values include: - -- `NO_RULES_CONFIGURED` -- `NO_RULE_MATCHED` -- `MARKER_MISSING_START` -- `MARKER_MALFORMED` -- Scheduling-rejection reasons such as `BREAK_START_IN_PAST`, `DECISIONING_MARGIN`, and `BREAK_OVERLAP` - -History is deduplicated per channel. Repeated polling of the same marker, including seeing it on another origin, does not create duplicate rows. +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 @@ -238,62 +66,15 @@ History is deduplicated per channel. Repeated polling of the same marker, includ | 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`. | Create and enable a marker rule for the channel. | | 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 by the API 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 in this cycle. Check reasons such as `BREAK_START_IN_PAST`, `DECISIONING_MARGIN`, or `BREAK_OVERLAP`. | +| 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. | -## End-to-end example - -1. Add and enable an HLS origin for `sports-main`. See [Origins](./origins.mdx). - - ```bash - curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "name": "Primary HLS origin", - "type": "HLS", - "url": "https://origin.example.com/live/sports-main/master.m3u8", - "enabled": true, - "priority": 0 - }' - ``` - -2. Create a break template and note its ID, such as `preroll-30s`. The marker rule references this value as `templateId`. - -3. Create an enabled marker rule for a matching HLS marker: - - ```bash - curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/markerRules' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "streamType": "HLS", - "type": "DATERANGE", - "conditions": { "CLASS": "com.example.ad" }, - "templateId": "preroll-30s", - "enabled": true - }' - ``` - -4. Enable detection on the channel: - - ```bash - curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/detection/enable' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' - ``` - -5. When the selected origin manifest advertises a matching `#EXT-X-DATERANGE` or `#EXT-X-CUE-OUT` marker, the worker evaluates the rule and creates an automatic break. - -6. Confirm the result in Detection history: - - ```bash - curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/detection/history?page=1&pageSize=20&sort=-createdAt' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' - ``` +## Related resources - A successful detection has `action: "CREATED"` and includes both `markerRuleId` and `breakId`. +| 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 index 8a8d07dd3b30..0487dc64b8cf 100644 --- a/ads/concepts/origins.mdx +++ b/ads/concepts/origins.mdx @@ -5,156 +5,34 @@ sidebar_label: Origins # Origins -An origin is a manifest URL that a channel monitors for ad markers. When automatic marker detection is enabled, the worker 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. +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. -Origins are scoped to an organization and to a channel. API calls identify the organization with the `X-Org-ID` header and authenticate with an API key and secret using HTTP Basic authentication. +## Origin identity -## Dashboard path - -In the OptiView Unified Dashboard, open the channel and select **Origins** from the channel navigation. From there you can add an origin, edit it, delete it, set its priority, and use **Enable origin** / **Disable origin** to control whether detection considers it. +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. ## How multiple origins are used -Only origins with `enabled: true` are considered for detection. Enabled origins are ordered by `priority` ascending, then by creation time. The worker walks that ordered list and uses the **first online origin**: the first one whose manifest is fetched and parsed successfully. +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.** `priority` is an integer; lower values are tried before higher ones. +- **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. -- **Lowest `priority` value first.** `priority` is an integer; lower values are tried before higher ones. Negative values are allowed, so `-1` is tried before `0`. -- **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 and wins, so lower-priority origins are not consulted in the same cycle. 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 -The API accepts `HLS`, `DASH`, and `HESP` for `type`, but automatic marker detection currently parses **HLS** manifests only. `DASH` and `HESP` origins can be stored and prioritized, but they are skipped by detection today. Use `HLS` for origins you expect to drive automatic breaks. +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. ::: -## Configuration reference - -| Field | Type | Default | Description | -| ---------- | ------------------------ | -------- | -------------------------------------------------------------------------------------------------------------- | -| `url` | string | Required | Manifest URL to monitor. Must be a valid URL. | -| `type` | `HLS`, `DASH`, or `HESP` | Required | Manifest format. Only `HLS` is parsed by detection today; `DASH` and `HESP` are accepted but not yet detected. | -| `name` | string | none | Optional human-readable label shown in the Dashboard. | -| `enabled` | boolean | `false` | Whether detection considers this origin. Change it with the enable/disable actions, not with an update. | -| `priority` | integer | `0` | Selection order for detection. Lower values are tried first; negative values are allowed. | - -`enabled` cannot be changed through the update endpoint. Use the dedicated enable and disable actions instead. - -## Add an origin - -Dashboard: open the channel, then use **Origins → Add**. - -API: - -```bash -curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "name": "Primary HLS origin", - "type": "HLS", - "url": "https://origin.example.com/live/sports-main/master.m3u8", - "enabled": true, - "priority": 0 - }' -``` - -Example response: - -```json -{ - "id": "3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f", - "channelId": "sports-main", - "name": "Primary HLS origin", - "type": "HLS", - "url": "https://origin.example.com/live/sports-main/master.m3u8", - "enabled": true, - "priority": 0, - "createdAt": "2026-07-16T12:00:00.000Z" -} -``` - -Add a lower-priority backup origin so detection can fall back if the primary source is unreachable: - -```bash -curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "name": "Backup HLS origin", - "type": "HLS", - "url": "https://backup.example.com/live/sports-main/master.m3u8", - "enabled": true, - "priority": 1 - }' -``` - -## Get an origin - -```bash -curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -## Update an origin - -The update endpoint accepts `url`, `type`, `name`, and `priority`. It does not accept `enabled`. - -```bash -curl -X PATCH 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "name": "Primary HLS origin (HD)", - "priority": 0 - }' -``` - -## Enable or disable an origin - -Dashboard: **Origins → Enable origin** / **Disable origin**. - -API: - -```bash -curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f/enable' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -```bash -curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f/disable' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -Disabling an origin removes it from detection immediately. The origin record is kept, so you can re-enable it later without recreating it. - -## List origins - -```bash -curl 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins?page=1&pageSize=20&sort=priority' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -List endpoints use the shared pagination shape. - -| Query parameter | Default | Description | -| --------------- | ------------ | -------------------------------------------------------------------------- | -| `page` | `1` | Page number. | -| `pageSize` | `20` | Items per page. Maximum `100`. | -| `filter` | none | Optional RSQL filter expression. | -| `sort` | `-createdAt` | Comma-separated sort fields. Prefix a field with `-` for descending order. | - -## Delete an origin +## Enabling and disabling origins -```bash -curl -X DELETE 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/origins/3f9c0f8e-1a2b-4c3d-8e9f-0a1b2c3d4e5f' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` +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. -## Next steps +## Related resources -Origins supply the manifests; [marker detection](./marker-detection.mdx) decides which markers in those manifests become breaks. Configure at least one enabled `HLS` origin before enabling detection on the channel. +| 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 index bffb401acf7a..493fb866b7be 100644 --- a/ads/concepts/templates.mdx +++ b/ads/concepts/templates.mdx @@ -5,264 +5,64 @@ sidebar_label: Templates # Templates -A template is a reusable break preset for OptiView Ads. It stores a break payload once so you can schedule consistent breaks quickly, either manually from the dashboard and API or automatically through marker rules. - -Templates are scoped to an organization. API calls identify the organization with the `X-Org-ID` header and authenticate with an API key and secret using HTTP Basic authentication. - -## Dashboard path - -In the OptiView Unified Dashboard, templates are available in two places: - -| Path | Use it for | -| ------------------------------------------------------ | ----------------------------------------------------- | -| `/{organizationId}/ads/templates` | Manage every template in the organization. | -| `/{organizationId}/ads/channels/{channelId}/templates` | Manage the templates surfaced for a specific channel. | - -Both lists expose **New**, **Edit**, and **Delete** actions, plus a **Schedule now** action that immediately schedules a break on the channel from the selected template. +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 a customer-facing `id`. The API stores it together with the organization ID, so the unique identity is: - -```text -organizationId + templateId -``` - -Use stable template IDs that match your operational names, such as `midroll-30s` or `sponsor-lshape`. If you omit `id` on creation, the API generates one. +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 payload as a break's `data`, so anything you can express on a break you can preset on a template: - -- `variant` — one variant, or a list of variants with device `targeting`, using the same variant formats (`single`, `double`, `lshape_ad`, `lshape_content`, `overlay`) and typed assets as a break. -- `resumeOffset` and `controls` (skip offset, snapback) — optional playback behaviour. -- `duration` — optional on a template (it is required on a break). When set, it is copied onto breaks scheduled from the template. +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`. -The **Breaks** section is the canonical reference for variant formats, layouts, typed assets, and device targeting. This section cross-links there instead of repeating those details. +A few properties differ from a break: -Templates can also record associations that make them easier to organize and surface: - -| Field | Relationship | -| ------------ | ------------------------------------------------------------------------------------------ | -| `channelIds` | Channels the template is associated with (for example, in the per-channel dashboard list). | -| `eventIds` | **Events** the template is associated with. | +| 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 payload is **copied onto the new break** at creation. +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 payload. - -Templates are **hard-deleted**. Deleting a template removes it permanently; there is no soft-delete or archival state. Breaks previously created from the template are unaffected and still report their historical `templateId`, but that `templateId` no longer resolves to a template, and listing breaks by a deleted template returns a not-found error. +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 -You can schedule a break from a template in three ways: - -- **Dashboard** — use the **Schedule now** action on a template in either template list to create a break on the channel immediately. -- **API** — create a break on a channel and reference the template with `templateId` (see [Schedule a break from a template](#schedule-a-break-from-a-template) below). -- **Marker rules** — each marker rule targets a template through its `templateId`. When automatic detection matches a marker, the worker schedules a break from that template. See the **Marker Detection** section for how rules are configured and evaluated. - -In every case the template payload is snapshotted onto the resulting break, as described in [Snapshot semantics](#snapshot-semantics). - -## Configuration reference - -| Field | Type | Default | Description | -| -------------- | -------------------- | --------- | ----------------------------------------------------------------------------------- | -| `id` | string | generated | Customer-facing template ID, unique within the organization. | -| `name` | string | none | Human-readable label shown in the dashboard. | -| `channelIds` | string[] | none | Channels the template is associated with. | -| `eventIds` | string[] | none | Events the template is associated with. | -| `duration` | integer | none | Optional break duration in seconds, copied onto breaks scheduled from the template. | -| `variant` | variant or variant[] | Required | Break variant(s). See the **Breaks** section for formats, assets, and targeting. | -| `resumeOffset` | integer | none | Optional resume offset applied to breaks scheduled from the template. | -| `controls` | object | none | Optional playback controls: `skipOffset` and `snapback`. | - -## Create a template - -Dashboard: **Ads → Templates → New**. - -API: - -```bash -curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/templates' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "id": "midroll-30s", - "name": "Mid-roll 30s", - "channelIds": ["sports-main"], - "duration": 30, - "variant": { - "format": "single", - "assets": [ - { - "type": "vast", - "mediaType": "video", - "uri": "https://adserver.example.com/vast/midroll.xml" - } - ] - } - }' -``` - -Example response: - -```json -{ - "id": "midroll-30s", - "name": "Mid-roll 30s", - "channelIds": ["sports-main"], - "duration": 30, - "variant": { - "format": "single", - "assets": [ - { - "type": "vast", - "mediaType": "video", - "uri": "https://adserver.example.com/vast/midroll.xml" - } - ] - }, - "createdAt": "2026-07-16T12:00:00.000Z" -} -``` - -## Get a template - -```bash -curl 'https://us.ads.optiview.dolby.com/api/v1/templates/midroll-30s' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -## Update a template - -```bash -curl -X PATCH 'https://us.ads.optiview.dolby.com/api/v1/templates/midroll-30s' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ - "name": "Mid-roll 30s (VAST)", - "duration": 30 - }' -``` - -Updating a template does not change breaks already scheduled from it — see [Snapshot semantics](#snapshot-semantics). - -## List templates - -```bash -curl 'https://us.ads.optiview.dolby.com/api/v1/templates?page=1&pageSize=20&sort=-createdAt' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -List endpoints use the same pagination shape as the rest of the API. Templates can be sorted by `name`, `duration`, or `createdAt`. - -| Query parameter | Default | Description | -| --------------- | ------------ | -------------------------------------------------------------------------- | -| `page` | `1` | Page number. | -| `pageSize` | `20` | Items per page. Maximum `100`. | -| `filter` | none | Optional RSQL filter expression. | -| `sort` | `-createdAt` | Comma-separated sort fields. Prefix a field with `-` for descending order. | - -Templates maintain denormalized fields derived from their payload so you can filter without inspecting the full `variant`. The `filter` expression accepts these selectors: +Templates are used throughout the system to schedule breaks: -| Filter selector | Matches on | Operators | -| --------------- | --------------------------------------- | ------------------------------------------ | -| `name` | Template name | `==`, `!=`, `=like=`, `=in=` | -| `duration` | Template duration | `==`, `!=`, `=gt=`, `=ge=`, `=lt=`, `=le=` | -| `format` | Variant formats present on the template | `==`, `!=`, `=like=`, `=in=` | -| `assetType` | Asset types present on the template | `==`, `!=`, `=like=`, `=in=` | -| `vendor` | Vendors present on the template | `==`, `!=`, `=like=`, `=in=` | +- **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. -Examples: +An API example: ```bash -# Overlay templates only -curl 'https://us.ads.optiview.dolby.com/api/v1/templates?filter=format==overlay' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -```bash -# Short VAST templates (30s or less), sorted by duration -curl 'https://us.ads.optiview.dolby.com/api/v1/templates?filter=duration=le=30;assetType==vast&sort=duration' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -```bash -# Templates that use a vendor asset (for example, Google Ad Manager pods) -curl 'https://us.ads.optiview.dolby.com/api/v1/templates?filter=vendor==gam' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -Combine multiple conditions with `;`. - -## Delete a template - -```bash -curl -X DELETE 'https://us.ads.optiview.dolby.com/api/v1/templates/midroll-30s' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'X-Org-ID: org_123' -``` - -Delete multiple templates in one request: - -```bash -curl -X DELETE 'https://us.ads.optiview.dolby.com/api/v1/templates' \ - -u "$ADS_API_KEY:$ADS_API_SECRET" \ - -H 'Content-Type: application/json' \ - -H 'X-Org-ID: org_123' \ - -d '{ "ids": ["midroll-30s", "sponsor-lshape"] }' -``` - -Deletes are permanent (hard delete). Existing breaks scheduled from the template are not affected — see [Snapshot semantics](#snapshot-semantics). - -## Schedule a break from a template - -Create a break on a channel and reference the template with `templateId`. The template payload is snapshotted onto the break at creation. - -```bash -curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/breaks' \ +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": "midroll-30s", + "templateId": "3b8e5f0a-7c2d-4e91-a6b3-9d4f1c8e2a70", "start": "2026-07-16T13:00:00.000Z" }' ``` -`templateId` is the only required field. You can override the snapshotted payload per break with optional fields — `start`, `duration`, `variant`, `assetParameters`, `eventId`, and `id`. Start semantics depend on the channel timebase; see the **Channels** and **Breaks** sections for scheduling and lifecycle details. +`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. -The created break records the source `templateId` alongside its own copied payload: +Start semantics depend on the channel timebase; see [Timebase-based scheduling](./breaks.mdx#timebase-based-scheduling). -```json -{ - "id": "b_9f2c", - "channelId": "sports-main", - "templateId": "midroll-30s", - "status": "PREPARING", - "start": "2026-07-16T13:00:00.000Z", - "duration": 30, - "variant": { - "format": "single", - "assets": [ - { - "type": "vast", - "mediaType": "video", - "uri": "https://adserver.example.com/vast/midroll.xml" - } - ] - }, - "createdAt": "2026-07-16T12:30:00.000Z" -} -``` +## 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.mdx b/ads/integrations/google.mdx index 6e230157a256..b82fc01db8a3 100644 --- a/ads/integrations/google.mdx +++ b/ads/integrations/google.mdx @@ -9,16 +9,12 @@ Google Ad Manager 360 (GAM 360) is the first supported OptiView Ads vendor. It r ## Organization configuration -Google configuration is organization-level and administrator-managed. A Dolby OptiView administrator or account team configures these values; they are not configured through the self-serve Basic API. +Google configuration is organization-level and managed by your Dolby OptiView account team. It links your organization to your Google Ad Manager network (network code and service account) and tunes the signaling behavior: -| Field | Type | Required | Effective service default when unset | -| -------------------------------- | ---------------- | --------------------------------------------------------------- | ------------------------------------ | -| `google.networkCode` | string | Optional in the organization schema; required for GAM signaling | None | -| `google.serviceAccountPath` | string | Optional in the organization schema; required for GAM signaling | None | -| `google.eabnLookForwardTimeMs` | positive integer | Optional | `300000` ms | -| `google.eabnDecisioningMarginMs` | positive integer | Optional | `5000` ms | +- **Look-forward time** — how far ahead of a break's start OptiView Ads signals it to Google, so the ad decisioning can complete in time. Defaults to 5 minutes. +- **Decisioning margin** — the minimum lead time required before a break's start for decisioning. Breaks scheduled closer than this margin are rejected. Defaults to 5 seconds. -The organization-level values override the service defaults. `networkCode` and `serviceAccountPath` must be present before EABN can signal a break. +Contact your account team to set up or change these values. ## SGAI pod serving @@ -39,27 +35,14 @@ A GAM pod break uses a vendor asset with `vendorParameters.type` set to `"pod"`: } ``` -### EABN lifecycle +### Pod decisioning -1. A GAM pod break is created with status `PREPARING`. -2. EABN waits until the look-forward window opens, then signals a Google DAI ad break through the channel's `customAssetKey`. -3. Google returns a `podId`. The vendor asset's `uri` is set to that pod ID. -4. The break becomes `READY` when it has a start time, or `CUED` when it has no start time. -5. For a delivered HLS manifest, the proxy injects the cue and changes `READY` to `SIGNALED`. +1. A GAM pod break is created and starts in the `PREPARING` state. +2. Ahead of the break's start (within the look-forward time), OptiView Ads announces the break to Google DAI through the channel's `customAssetKey`, and Google decisions the ad pod. +3. Once decisioned, the vendor asset's `uri` carries the resulting pod ID. 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, it becomes `SIGNALED`. The player requests the pod manifest using the vendor asset `uri`. -The player then requests the pod manifest using the vendor asset `uri`, which is the decisioned `podId`. - -`google.eabnLookForwardTimeMs` controls when EABN signals a scheduled break: signaling begins when the effective live point reaches `start - eabnLookForwardTimeMs`. Its effective default is `300000` ms. - -`google.eabnDecisioningMarginMs` is the minimum lead time required for decisioning. If `start - effectiveNow` falls below this margin, the break is missed instead of being signaled. Its effective default is `5000` ms. - -### Cue-punch - -A `CUED` break has no start time and waits for a punch before it plays. Punching changes the status from `CUED` to `READY`. A GAM pod break cannot be punched until EABN has decisioned it: - -```text -Ad break '' is not yet decisioned by EABN -``` +A GAM pod break can only be punched after Google has decisioned it — that is, once it has left the `PREPARING` state. ## SSAI_DAI @@ -81,7 +64,7 @@ One or more daiAssetKeys are already used by another channel integration Create an integration with the self-serve API: ```bash -curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/integrations' \ +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/1f7f3a5a-9c2e-4a56-b1d4-3f8a2c9d6e01/integrations' \ -u "$ADS_API_KEY:$ADS_API_SECRET" \ -H 'Content-Type: application/json' \ -H 'X-Org-ID: org_123' \ @@ -96,13 +79,13 @@ curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/sports-main/inte ### Best-effort fan-out -At signal time, EABN snapshots the channel integration keys onto the break and fans the break out to each `daiAssetKey` through Google's by-asset-key ad break endpoint. The signals are best-effort: a failure for one key is logged and does not affect the primary signal or the other keys. An SSAI-only break has no `customAssetKey`, is not lifecycle-tracked, and never receives a `podId`. +When a break is signaled, it is announced to each of the integration's `daiAssetKeys`. The signals are best-effort: a failure for one key does not affect the other keys. -### Proxy cue injection +### SSAI cue injection -For an HLS wallclock channel with an `SSAI_DAI` integration, the proxy finds active wallclock GAM pod breaks within the DVR window and injects `EXT-X-DATERANGE` cues into the media playlist. The injected cues contain `SCTE35-OUT` and `SCTE35-IN` data. After injection, the proxy changes the affected breaks from `READY` to `SIGNALED`. +For an HLS wallclock channel with an `SSAI_DAI` integration, OptiView Ads injects `EXT-X-DATERANGE` cues (carrying `SCTE35-OUT` and `SCTE35-IN` data) for upcoming breaks into the media playlist delivered to players. After injection, the affected breaks become `SIGNALED`. -PTS channels receive passthrough manifests with no cue injection. A channel without an `SSAI_DAI` integration also receives a passthrough manifest with no ad cue injection. +PTS channels, and channels without an `SSAI_DAI` integration, receive their manifests unchanged, with no ad cue injection. ## Ad targeting parameters @@ -124,21 +107,17 @@ A break can be stored with status `ERROR` and: Break passed its scheduling window before it could be signaled ``` -This means the break missed its scheduling window because the remaining time fell below the decisioning margin, or the missed-break health check caught it. Schedule pod breaks at least the decisioning margin ahead of the live point and verify the EABN and Google configuration. +This means the break missed its scheduling window: the remaining time before its start fell below the decisioning margin before it could be signaled. Schedule pod breaks at least the decisioning margin ahead of the live point and verify the Google configuration. ### GAM configuration error -Break creation returns HTTP `400` when the organization network code, service-account path, or channel custom asset key is missing: +Break creation returns HTTP `400` when the organization's Google configuration or the channel's custom asset key is missing: ```text Vendor asset of type GAM requires organization.google.networkCode, organization.google.serviceAccountPath and channel.customAssetKey to be configured ``` -Verify all three values: - -- `organization.google.networkCode` -- `organization.google.serviceAccountPath` -- `channel.customAssetKey` +Verify that your organization's [Google configuration](#organization-configuration) is set up (contact your account team) and that the channel has a `customAssetKey`. ### Pod break too close to live @@ -152,13 +131,13 @@ The default `` is `5000`. ### Break remains `PREPARING` -If a break never leaves `PREPARING`, EABN may be skipping the signal because the organization is missing `networkCode` or `serviceAccountPath` at signal time. Check the organization Google configuration and confirm that the channel has the required delivery key: `customAssetKey` for SGAI, or an `SSAI_DAI` integration with `daiAssetKeys`. +If a break never leaves `PREPARING`, the signal to Google may be skipped because the organization's Google configuration is incomplete. Check the [organization configuration](#organization-configuration) with your account team and confirm that the channel has the required delivery key: `customAssetKey` for SGAI, or an `SSAI_DAI` integration with `daiAssetKeys`. ## Related resources | Resource | Relationship | | ------------------------------------------------ | -------------------------------------------------------------------------- | | [Channels](../concepts/channels.mdx) | Hold the `customAssetKey` used for Google server-guided pod serving. | -| [Breaks](../concepts/breaks.mdx) | Carry the vendor asset that Google decisions. | +| [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. | From 91b493e6e6976814302be5cbcb0b97c738ee03f2 Mon Sep 17 00:00:00 2001 From: "maarten.rimaux" Date: Fri, 14 Aug 2026 13:11:01 +0000 Subject: [PATCH 19/22] Rework events page sections per review feedback Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ads/concepts/events.mdx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/ads/concepts/events.mdx b/ads/concepts/events.mdx index b4afbeb8fb25..4a184e298e70 100644 --- a/ads/concepts/events.mdx +++ b/ads/concepts/events.mdx @@ -24,7 +24,7 @@ Preparing breaks under an event is supported on **wallclock** channels. The wind 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. -## Relationship to breaks +## 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. @@ -32,20 +32,23 @@ A break is attached to an event by setting `eventId` to the event's `id` on the 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 a break under the event without a `start`. OptiView Ads prepares it (including any ad decisioning) and it waits in the `CUED` state. -3. At the right moment — for example, half-time — punch the break. Its start is set and it is announced to players right away. -4. To prepare the next break, first punch (or delete) the current cued break, then cue the next one — a channel holds only one cued break at a time. +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. -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. +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. -## Relationship to templates - -[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. +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 From 6531d8e7b053ce98f4798f2f8e80f5cec26af828 Mon Sep 17 00:00:00 2001 From: "maarten.rimaux" Date: Fri, 14 Aug 2026 14:16:14 +0000 Subject: [PATCH 20/22] Apply origins and break detection review feedback Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ads/concepts/marker-detection.mdx | 6 ++++-- ads/concepts/origins.mdx | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ads/concepts/marker-detection.mdx b/ads/concepts/marker-detection.mdx index cdc153bf5729..69e1e2cb8b62 100644 --- a/ads/concepts/marker-detection.mdx +++ b/ads/concepts/marker-detection.mdx @@ -49,7 +49,9 @@ For example, this rule matches `DATERANGE` markers whose `CLASS` attribute is `c ## Detection history -Detection history is the audit trail of what automatic detection decided for each marker. Every processed marker is recorded with one of three outcomes: +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 | | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | @@ -64,7 +66,7 @@ Each record includes the raw manifest tag line, the origin that supplied the mar | 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`. | Create and enable a marker rule for the channel. | +| 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). | diff --git a/ads/concepts/origins.mdx b/ads/concepts/origins.mdx index 0487dc64b8cf..d140a0f60374 100644 --- a/ads/concepts/origins.mdx +++ b/ads/concepts/origins.mdx @@ -11,11 +11,15 @@ An origin is a manifest URL that a channel monitors for ad markers. When [break 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.** `priority` is an integer; lower values are tried before higher ones. +- **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. @@ -24,10 +28,6 @@ This lets you configure a primary origin and one or more lower-priority backups: 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. ::: -## 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. - ## Related resources | Resource | Relationship | From c3024e07e461bac5448ec42b7b98facdedc46188 Mon Sep 17 00:00:00 2001 From: "maarten.rimaux" Date: Fri, 14 Aug 2026 14:53:43 +0000 Subject: [PATCH 21/22] Rework Break Manifest concept page for customer-facing docs Reframe the page around the backend/player contract and side-loading (with a data-flow diagram), simplify the endpoint and caching text, document the 1.1.0 envelope properties, and defer break details to the Breaks page. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../img/break-manifest/side-loading.svg | 25 ++ ads/concepts/break-manifest.mdx | 230 +++--------------- 2 files changed, 59 insertions(+), 196 deletions(-) create mode 100644 ads/assets/img/break-manifest/side-loading.svg 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 @@ + + + + + + + + OptiView Ads API + + Dolby CDN + + Origin + + Customer CDN + + OptiView Player + + break manifest + + break manifest + + media stream + + media stream + diff --git a/ads/concepts/break-manifest.mdx b/ads/concepts/break-manifest.mdx index 2e30b1c9e6b6..9062b068ed99 100644 --- a/ads/concepts/break-manifest.mdx +++ b/ads/concepts/break-manifest.mdx @@ -5,35 +5,29 @@ sidebar_label: Break Manifest # Break Manifest -The Break Manifest is the canonical, machine-readable description of the ad breaks that are currently relevant for a [channel](/ads/concepts/channels). It is a small JSON document that the OptiView Player polls on a fixed cadence to learn which breaks to prepare and play. +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. -The Break Manifest is **side-loaded**: it is served from its own endpoint, separately from the media (HLS/DASH) manifest. The player fetches the media manifest from your CDN as usual and, in parallel, polls the Break Manifest to drive ad break scheduling. This is different from server-side ad insertion (SSAI), where ad cues are injected directly into the media manifest. +## Side-loading -## Side-loading versus SSAI cue injection +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. -OptiView Ads can deliver break timing to the player in two distinct ways. A channel can use either mechanism depending on how the workflow is integrated. +![Side-loading diagram](../assets/img/break-manifest/side-loading.svg) -| Delivery mechanism | Where the break information lives | Who consumes it | -| ----------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | -| Side-loaded (this page) | A separate JSON Break Manifest served from a dedicated endpoint. | The OptiView Player, which polls the endpoint and schedules breaks client-side. | -| SSAI cue injection | `#EXT-X-DATERANGE` cues rewritten inline into the proxied HLS media playlist. | Any player that reads the manifest; used for Google DAI server-guided pods. | +Side-loading has some important advantages: -With **side-loading**, the media manifest is untouched: the player merges the break timeline it reads from the Break Manifest with the content timeline it reads from the media manifest. This keeps the media manifest cacheable and lets the player own the ad experience (layout, skip, snapback). - -With **SSAI cue injection**, OptiView Ads proxies the upstream HLS playlist and inserts `#EXT-X-DATERANGE` cues in place. Cue injection applies only to `wallclock` channels that have a Google DAI (`SSAI_DAI`) integration configured, because `#EXT-X-DATERANGE` requires a `START-DATE`, which has no `pts` equivalent. +- **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 ``` -| Path parameter | Description | -| -------------- | --------------------------------------- | -| `orgId` | The organization that owns the channel. | -| `channelId` | The channel to read breaks for. | - -The Break Manifest 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. It differs from the [Channels](/ads/concepts/channels) management API, which is authenticated. Do not place secrets in the polling URL. +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' @@ -43,201 +37,45 @@ curl 'https://us.markers.optiview.dolby.com/manifest/v1/org_123/channels/1f7f3a5 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 - -| Status | Meaning | -| ------ | -------------------------------------------------------------------------------------- | -| `200` | The channel exists. Returns the Break Manifest JSON document described below. | -| `404` | No channel with `channelId` exists in the organization. Returns a JSON error envelope. | - -### Caching - -The response carries a `Cache-Control` header so that players and CDNs poll at a rate the channel controls. - -| Case | `Cache-Control` | Source | -| ------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------- | -| `200` (any channel) | `public, max-age=` | The channel's active polling interval (`pollingActiveSeconds`), in seconds. | -| `404` (not found) | `public, max-age=` | A short negative-cache window (default `5` seconds) so a missing channel is not hammered. | - -The `max-age` on a successful response always uses the **active** polling interval, so that a cached copy is never held longer than the shortest polling cadence the channel advertises. Use the `polling` values inside the manifest body (see below) to decide how often to poll; use `Cache-Control` for CDN and HTTP cache behavior. +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. -## Polling intervals +## Manifest envelope -The Break Manifest response tells players how often to poll for updates. Two channel settings control this cadence: +The Break Manifest document contains the following top-level properties: -- **`pollingIdleSeconds`** — the polling interval advertised when no break is active. A slower cadence keeps request load low while nothing is happening. Default: `10` seconds. -- **`pollingActiveSeconds`** — the polling interval advertised while a break is active. A faster cadence lets players catch break transitions quickly. Default: `1` second. +| 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. | -These values are advertised to players through the `polling` object in the manifest envelope (see below). +Everything inside a break entry — `start`, `duration`, `resumeOffset`, `controls`, and `variant` — is described on the [Breaks](./breaks.mdx) page. -## Manifest envelope +### Polling -The response body is the Break Manifest envelope. The following descriptions are written from the service `breakManifestSchema`. +The `polling` object advertises how often the player should refresh the manifest, with two cadences: -| Field | Type | Description | -| ---------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------- | -| `version` | string | Break Manifest format version. Currently `1.0.0`. Use it to guard against future format changes. | -| `timebase` | `wallclock` or `pts` | The channel timebase. Determines how each break's `start` is expressed (see [Channels → Timebase](/ads/concepts/channels)). | -| `polling` | object | Advertised polling cadence, in seconds. Contains `idle` and `active`. | -| `polling.idle` | integer | Interval to poll at when no break is active (from the channel `pollingIdleSeconds`). | -| `polling.active` | integer | Interval to poll at while a break is active (from the channel `pollingActiveSeconds`). | -| `breaks` | array | The breaks currently relevant for the channel. May be empty. Each entry is described in [Break entries](#break-entries). | +- **`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. -## Break entries +Both cadences are configured on the channel through its `pollingIdleSeconds` and `pollingActiveSeconds` settings. -Each element of `breaks` describes one ad break. The fields are written from the service break schema. +### Which breaks are included -| Field | Type | Required | Description | -| -------------- | --------------------------- | :------: | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `id` | string | Yes | Stable identifier of the break, unique within the channel. | -| `start` | ISO 8601 string, or number | Yes | Break start on the channel timebase. A UTC ISO 8601 timestamp when `timebase` is `wallclock`; a numeric presentation timestamp when `timebase` is `pts`. | -| `duration` | number (seconds) | Yes | Length of the break, in seconds. | -| `resumeOffset` | number (seconds) | No | Where content playback resumes relative to the break, in seconds. Omitted when the break does not override the default resume behavior. | -| `controls` | object | No | Playback controls for the break. See [Controls](#controls). | -| `variant` | object, or array of objects | Yes | The ad experience(s) to render for the break. A single variant object, or a non-empty list of variants. See [Variants](#variants). | - -A break only appears once its timebase-specific start is known: `wallclock` breaks require a resolved start timestamp, and `pts` breaks require a numeric start. Breaks missing that value for the channel timebase are not included. - -### Controls - -When present, `controls` refines how the player treats the break. - -| Field | Type | Description | -| ------------ | ---------------- | --------------------------------------------------------------------------------- | -| `skipOffset` | number (seconds) | How long into the break before it becomes skippable. Omit to make it unskippable. | -| `snapback` | boolean | When `true`, the player snaps back to the break-in point after seeking past it. | - -### Variants - -`variant` carries the ad experience. Each variant has a `format` and a set of `assets`; some formats add layout fields. Provide a single variant, or a list when the break offers more than one experience (for example, targeted by device type). - -| `format` | Description | -| ---------------- | --------------------------------------------------------------------------------- | -| `single` | Full-screen ad insertion that replaces the content. | -| `double` | Double Box: content continues alongside the ad and a companion asset. | -| `lshape_ad` | L-shape with the ad in the main area and a companion asset. | -| `lshape_content` | L-shape with content scaled into the main area. | -| `overlay` | Overlay ad positioned and sized over the content (`position`, `size`, `opacity`). | - -## Which breaks are included - -The Break Manifest reflects the breaks that are currently relevant for delivery, not the channel's entire break history. Selection is driven by two channel settings, [`dvrWindowMs` and `liveOffsetMs`](/ads/concepts/channels): - -- A cutoff time is computed as `now − liveOffsetMs − dvrWindowMs`. -- A break is included when its end (its `start` plus `duration`) is at or after that cutoff. This keeps breaks whose window still overlaps the DVR buffer, and keeps upcoming breaks, while dropping breaks that ended before the DVR look-back. -- Only breaks in the `READY` or `SIGNALED` [status](#break-lifecycle) are eligible. Breaks that are still `PREPARING` or `CUED`, or that have `ERROR`, are never exposed. - -`liveOffsetMs` lets a channel account for live latency by shifting the effective "now" backward, so breaks remain visible relative to the live playhead rather than raw server time. `dvrWindowMs` (default `300000`, i.e. 5 minutes) sets how far back the look-back extends. - -## Break lifecycle - -A break moves through a small set of statuses. Two of them are visible in the Break Manifest. - -| Status | In manifest | Meaning | -| ----------- | :---------: | --------------------------------------------------------------------------- | -| `PREPARING` | No | The break is being prepared (for example, awaiting a Google DAI pod asset). | -| `CUED` | No | The break is pre-decisioned and awaiting a confirmed start time. | -| `READY` | Yes | The break is ready to be delivered and is eligible for the manifest. | -| `SIGNALED` | Yes | The break has been served in the Break Manifest at least once. | -| `ERROR` | No | The break failed to prepare and is not delivered. | - -### READY → SIGNALED - -Serving the Break Manifest is what advances a break from `READY` to `SIGNALED`. When a poll includes one or more `READY` breaks, the service returns them in the response **and** transitions them to `SIGNALED` as a side effect of that read. A break that is already `SIGNALED` continues to be returned (while it remains within the DVR window) without any further status change. This makes the first appearance of a break in the manifest the moment it is considered signaled to players. - -## Player polling - -The OptiView Player consumes the Break Manifest by polling the endpoint: - -1. Fetch the Break Manifest for the channel. -2. Read `polling.idle` and `polling.active` (seconds) to set the next poll delay: poll at the `idle` cadence when no break is active, and at the `active` cadence while a break is active. -3. Merge each `break` onto the content timeline using `start` (interpreted with `timebase`) and `duration`, and render the `variant`. -4. Honor `controls` (`skipOffset`, `snapback`) and `resumeOffset` when playing the break and resuming content. - -Because the endpoint sets `Cache-Control` from the channel's active polling interval, a shared cache never serves a manifest older than the fastest advertised cadence. - -## Annotated examples - -### Wallclock channel - -For a channel created with `timebase: wallclock`, each break `start` is a UTC ISO 8601 timestamp. - -```json -{ - "version": "1.0.0", - "timebase": "wallclock", - "polling": { - "idle": 10, - "active": 1 - }, - "breaks": [ - { - "id": "9c1e2b34-5d6f-4a78-9b0c-1d2e3f4a5b6c", - "start": "2026-07-16T12:30:00.000Z", // UTC wallclock start of the break - "duration": 30, // seconds - "resumeOffset": 0, // resume content at the break-in point - "controls": { - "skipOffset": 5, // skippable 5s in - "snapback": true // snap back to the break if the viewer seeks past it - }, - "variant": { - "format": "single", - "assets": [ - { - "id": "a1", - "type": "static", - "mediaType": "video", - "uri": "https://cdn.example.com/ads/ad.m3u8" - } - ] - } - } - ] -} -``` +The Break Manifest reflects the breaks that are currently relevant for delivery, not the channel's entire break history: -### PTS channel - -For a channel created with `timebase: pts`, each break `start` is a numeric presentation timestamp on the channel's media clock instead of a wallclock timestamp. The envelope and the rest of each break entry are otherwise identical. - -```json -{ - "version": "1.0.0", - "timebase": "pts", - "polling": { - "idle": 10, - "active": 1 - }, - "breaks": [ - { - "id": "4d5e6f70-8a9b-4c1d-a2b3-c4d5e6f70819", - "start": 5400000, // numeric PTS start on the channel media clock - "duration": 30, // seconds - "variant": [ - { - "format": "single", // default full-screen experience - "assets": [{ "id": "a1", "type": "vast", "mediaType": "video", "uri": "https://adserver.example.com/vast.xml" }] - }, - { - "format": "overlay", // alternative overlay experience - "assets": [{ "id": "a2", "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 - } - ] - } - ] -} -``` - -When `breaks` is empty, the envelope is still returned with the channel `timebase` and `polling` values, and the player keeps polling at the `idle` cadence. +- 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.mdx) | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. | -| [Getting started](/ads/getting-started/) | Integrating the OptiView Player that polls the Break Manifest. | From be8d12bbb142ba2372d24ad12cbd8fd0f012fae9 Mon Sep 17 00:00:00 2001 From: "maarten.rimaux" Date: Fri, 14 Aug 2026 15:18:19 +0000 Subject: [PATCH 22/22] Rework Integrations overview page for customer-facing docs Simplify the intro around vendor assets, keep the supported vendors table without fan-out wording, and replace the Ads V2 model mapping with a related resources section. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ads/integrations/index.mdx | 60 +++++++------------------------------- 1 file changed, 11 insertions(+), 49 deletions(-) diff --git a/ads/integrations/index.mdx b/ads/integrations/index.mdx index 9cb7ccbd2b97..cec5ea5e85aa 100644 --- a/ads/integrations/index.mdx +++ b/ads/integrations/index.mdx @@ -5,59 +5,21 @@ sidebar_label: Integrations # Integrations -An integration connects OptiView Ads to an ad decisioning or serving vendor that breaks are signaled to. Google Ad Manager 360 is the first supported integration. - -Vendors are not standalone REST resources in Ads V2. A break variant carries a **vendor asset**, while vendor configuration is applied at the organization and channel levels. Self-serve channel, break, and integration APIs use HTTP Basic authentication with an API key and secret, plus the `X-Org-ID` header. Organization-level Google configuration is administrator-managed; see [Google Ad Manager](./google). - -## Vendor assets - -An asset with `"type": "vendor"` represents a vendor-delivered ad. The current vendor enum contains only `"gam"`. - -| Field | Type | Required/default | Description | -| ------------------ | ------------------------ | --------------------------- | ------------------------------------------------------------------------------------------------ | -| `type` | string literal | Required: `"vendor"` | Selects the vendor asset type. Other asset types are `"static"` and `"vast"`. | -| `vendor` | enum | Required: `"gam"` | Identifies the ad vendor. | -| `uri` | string | Defaults to `"placeholder"` | Holds the vendor result. For a decisioned GAM pod, it is replaced with the Google `podId`. | -| `vendorParameters` | `Record` | Required | Vendor-specific parameters. GAM assets must include a `type` key whose current value is `"pod"`. | -| `assetParameters` | `Record` | Optional | Ad-tag and targeting parameters forwarded during decisioning. | - -Example GAM pod asset: - -```json -{ - "type": "vendor", - "vendor": "gam", - "uri": "placeholder", - "vendorParameters": { - "type": "pod" - } -} -``` +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) is the first supported integration. ## Supported vendors -| Vendor | Enum value | Delivery | -| --------------------------------- | ---------- | ------------------------------------- | -| [Google Ad Manager 360](./google) | `gam` | SGAI pod serving and SSAI_DAI fan-out | +| Vendor | Enum value | Delivery | +| --------------------------------- | ---------- | ----------------------------- | +| [Google Ad Manager 360](./google) | `gam` | SGAI pod serving and SSAI DAI | 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. -## How vendors relate to the Ads V2 model - -| Resource | Relationship | -| -------------------------------------------- | -------------------------------------------------------------------------------------------- | -| [Channels](../concepts/channels) | Hold the channel-level `customAssetKey` used for Google server-guided pod serving. | -| [Breaks](../how-to-guides/scheduling-breaks) | Carry the vendor asset in a break variant. | -| Templates | Reusable break presets that can be scheduled on channels. See the [API reference](/ads/api). | -| Integrations | Configure channel-level delivery integrations such as `SSAI_DAI` and its `daiAssetKeys`. | - -Vendor assets are validated as part of break and template requests. A GAM vendor asset must use: +## Related resources -```json -{ - "vendor": "gam", - "vendorParameters": { - "type": "pod" - } -} -``` +| 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.mdx) | The first supported vendor: SGAI pod serving and the `SSAI_DAI` channel integration. |