> For the complete documentation index, see [llms.txt](https://help.citrusad.com/retail-media-interface/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.citrusad.com/retail-media-interface/integration/generating-ads/banner-x-responsive/bannerx-video-captions.md).

# Banner X video captions

## Overview

The Epsilon Retail Media platform supports closed caption and transcript files for Banner X video campaigns. Advertisers upload caption files (`.vtt`, `.srt`) and transcript files (`.txt`) alongside their video assets. Your integration consumes these file URLs from the ad response and renders captions in your video player.

This integration helps you meet accessibility requirements such as the European Accessibility Act (EAA), which mandates user-controlled closed captions on public-facing video content.

* Caption files are time-synced subtitle tracks for your video player.
* Transcript files are plain-text representations of the video's audio content for SEO and screen reader use.

This guide covers:

* How caption and transcript files appear in VAST and JSON ad responses
* VAST 4.3 `<ClosedCaptionFile>` element structure
* JSON `videoTranscriptFiles[]` array structure
* How to render captions using the provided CDN URLs
* Testing and validation

### Delivery model

Caption file URLs (`.vtt` / `.srt`) are delivered only via VAST, not JSON.\
Transcript file URLs (`.txt`) are delivered only via JSON, not VAST.\
Captions are consumed by your video player; transcripts are consumed at the page level.

## Integration flow

Caption and transcript files follow the same delivery path as video assets. No additional API calls are required beyond your existing Banner X ad request.

1. An advertiser uploads video, caption, and transcript files via the campaign wizard.
2. Files are validated, stored, and served via CDN.
3. Your existing Banner X ad request returns caption URLs in VAST and transcript URLs in JSON.
4. Your video player reads the caption file URL and renders subtitles.
5. Your page optionally displays transcript text for accessibility or SEO.

## Supported file types

| Asset           | Formats                          | Max size                                                      |
| --------------- | -------------------------------- | ------------------------------------------------------------- |
| Closed captions | `.vtt` (WebVTT), `.srt` (SubRip) | Configurable per retailer content standard (typically 1–4 MB) |
| Transcript      | `.txt` (plain text)              | Configurable per retailer content standard (typically 1–4 MB) |

Files are served as-is; the platform does not convert formats. Your video player is responsible for rendering captions.

## Prerequisites

Before integrating captions and transcripts, confirm the following:

* Existing Banner X video integration — you already consume `<MediaFiles>` from the VAST tag in the adm field. See [Video ads](/retail-media-interface/integration/generating-ads/banner-x-responsive/video-ads-banner-x.md) for a complete ad response example that includes caption and transcript file delivery.
* Video player with caption support — WebVTT or SRT caption tracks (e.g. HTML5 `<track>` element, or equivalent native player API).
* Feature enabled for your namespace — contact your account team to enable caption and transcript uploads for your namespace.
* CORS configured — allow cross-origin fetch of `.vtt` files from the Epsilon CDN domain when using HTML5 `<track>` elements.

## Authentication and security

No additional authentication is required. Caption and transcript files are served via the same CDN as video assets. File URLs returned in the ad response are publicly accessible, using the same security model as video file URLs.

If your video player uses HTML5 `<track>` elements to load `.vtt` files, ensure your page's Content Security Policy allows fetching from the Epsilon CDN domain. The CDN sets appropriate CORS headers for cross-origin `<track>` loading.

## Step 1: Parse caption files from the VAST response

**Purpose:** Retrieve time-synced caption file URLs from the VAST XML in the adm field for rendering in your video player.

### What you need to do

* In your VAST parser, look for the `<ClosedCaptionFiles>` element within each `<Linear>` creative (inside `<MediaFiles>`).
* Each `<ClosedCaptionFile>` child element contains the CDN URL as its text content.
* The `type` attribute indicates the MIME type (`text/vtt` or `application/x-subrip` for SRT).
* The `language` attribute indicates the caption language (e.g. `en`).

### Example VAST response

```xml
<Linear>
  <Duration></Duration>
  <MediaFiles>
    <ClosedCaptionFiles>
      <ClosedCaptionFile type="text/vtt" language="en"><![CDATA[https://dev12.flavedo.io./citrus/0f04cbc2-c933-4384-8d37-772e939d8d02]]></ClosedCaptionFile>
    </ClosedCaptionFiles>
    <Mezzanine><![CDATA[https://dev12.flavedo.io./citrus/37bb15a2-fd4d-4eb1-9dd7-7630b5df29d1]]></Mezzanine>
    <MediaFile delivery="progressive" type="video/mp4" width="1280" height="720" bitrate="8700" codec="h264"><![CDATA[https://dev12.flavedo.io./citrus/37bb15a2-fd4d-4eb1-9dd7-7630b5df29d1]]></MediaFile>
  </MediaFiles>
  <VideoClicks>
    <ClickTracking><![CDATA[https://integration.dev12.citrusad.com/v1/resource/second-c/...]]></ClickTracking>
    <ClickThrough></ClickThrough>
  </VideoClicks>
</Linear>
```

### Validation

* Confirm your VAST parser extracts the `<ClosedCaptionFile>` URL when present.
* Confirm your parser gracefully handles the absence of `<ClosedCaptionFiles>` (not all campaigns include captions).

### Common errors

| Error                            | Cause                                                                                                                                                             |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `<ClosedCaptionFiles>` not found | The campaign may not have an approved caption file, or caption delivery is not yet enabled for your environment. This is expected for campaigns without captions. |

## Step 2: Parse transcript files from the JSON response

**Purpose:** Retrieve plain-text transcript file URLs from the JSON ad response for SEO or screen reader accessibility.

### What you need to do

* In the Banner X generate response, look for the `videoTranscriptFiles` array on each ad object.
* Each entry includes a `videoFileId` so you can match transcripts to their corresponding video.
* Transcripts are plain text (`.txt`) — use them for SEO metadata, screen readers, or alternative text display.

### Example JSON response

```json
"videoTranscriptFiles": [
  {
    "videoFileId": "a6f4c4a6-6982-4a8c-9a81-ecdfd4b3fa36",
    "format": "txt",
    "url": "https://dev12.flavedo.io./citrus/03d98ae2-67d5-49d3-9e9b-c8f278520657",
    "language": "en"
  }
]
```

### Validation

* Confirm your JSON parser reads the `videoTranscriptFiles` array when present.
* Confirm graceful handling when the array is empty or absent.

{% hint style="info" %}
**Caption file URLs (for `.vtt`/`.srt`) are only delivered via VAST, not JSON. Transcript files (`.txt`) are only delivered via JSON, not VAST. This separation reflects the different consumption patterns: captions are for video players (VAST), transcripts are for page-level accessibility (JSON).**
{% endhint %}

## Step 3: Render captions in your video player

**Purpose:** Display closed captions to shoppers during video playback.

### What you need to do

* After parsing the caption URL from VAST, add a `<track>` element to your HTML5 video player (or equivalent in your native player SDK).
* Set `kind="captions"`. Use the `default` attribute if you want captions enabled by default.
* For .srt files, some players require conversion to WebVTT at runtime (prepend `WEBVTT\n\n` and replace comma with period in timestamps).

### Example HTML5 implementation

```html
<video controls crossorigin="anonymous">
  <source src="https://cdn.citrusad.com/video/abc123.mp4" type="video/mp4">
  <track
    kind="captions"
    src="https://cdn.citrusad.com/captions/def456.vtt"
    srclang="en"
    label="English"
    default
  >
</video>
```

### Validation

* Play the video and confirm captions appear as an overlay.
* Confirm the shopper can toggle captions on/off via player controls.
* Confirm captions are time-synced with the video audio.

### Common errors

| Error                                       | Solution                                                                                                            |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Captions don't load (CORS error in console) | Ensure your Content Security Policy and CORS configuration allow fetching `.vtt` files from the Epsilon CDN domain. |
| Captions appear garbled or empty            | Verify the file URL returns valid WebVTT content. Fetch the URL directly to inspect the file.                       |

## Step 4: (Optional) Display transcript text

**Purpose:** Provide accessible text content alongside or below the video for screen readers and SEO.

### What you need to do

* Fetch the transcript URL from the `videoTranscriptFiles` entry in the JSON response.
* Use the transcript text for accessibility, SEO, screen readers, or other retailer-specific use cases.

### Example

```html
<details>
  <summary>Video transcript</summary>
  <p id="transcript-content"></p>
</details>
<script>
  fetch('https://cdn.citrusad.com/transcripts/ghi789.txt')
    .then(r => r.text())
    .then(text => {
      document.getElementById('transcript-content').textContent = text;
    });
</script>
```

## Data models and field definitions

### VAST: `ClosedCaptionFile` element

| Attribute / field | Type         | Required | Description                   | Accepted values                  |
| ----------------- | ------------ | -------- | ----------------------------- | -------------------------------- |
| `type`            | string       | Yes      | MIME type of the caption file | `text/vtt, application/x-subrip` |
| `language`        | string       | No       | Language of the caption track | e.g. `en`                        |
| Element text      | string (URL) | Yes      | CDN URL of the caption file   | HTTPS URL                        |

### JSON: `videoTranscriptFiles`

| Field         | Type         | Required | Description                                     | Accepted values |
| ------------- | ------------ | -------- | ----------------------------------------------- | --------------- |
| `videoFileId` | string       | Yes      | ID of the video file this transcript belongs to | UUID            |
| `format`      | string       | Yes      | File format of the transcript                   | `txt`           |
| `url`         | string (URL) | Yes      | CDN URL of the transcript file                  | HTTPS URL       |
| `language`    | string       | Yes      | Language of the transcript                      | e.g. `en`       |

## Backward compatibility

These additions are fully backward-compatible:

* If no caption files are attached to a campaign, the `<ClosedCaptionFiles>` element is omitted from VAST entirely.
* If no transcript files are attached, `videoTranscriptFiles` is absent or an empty array in JSON.
* Existing integrations that do not parse these new elements continue to work without changes.

## Testing, sandbox, and go-live

### Sandbox / test environment

* Request access to a test namespace with caption and transcript uploads enabled (contact your account team).
* Upload a test campaign with a sample `.vtt` caption file via the campaign wizard.
* Approve the campaign through the review cycle.
* Call the Banner X generate endpoint and inspect the VAST and JSON response.

### Sample test cases

| Test                    | Steps                                                   | Expected result                                         |
| ----------------------- | ------------------------------------------------------- | ------------------------------------------------------- |
| VAST caption present    | Request ad for campaign with approved `.vtt` caption    | VAST contains `<ClosedCaptionFiles>` with valid CDN URL |
| VAST caption absent     | Request ad for campaign without captions                | No `<ClosedCaptionFiles>` element in VAST               |
| JSON transcript present | Request ad for campaign with approved `.txt` transcript | JSON contains `videoTranscriptFiles` array with entry   |
| Caption file accessible | Fetch the CDN URL from the VAST response directly       | Returns valid `WebVTT` content with WEBVTT header       |
| Player renders captions | Load video + caption track in your player               | Captions display time-synced with video audio           |
| CORS for track element  | Load `.vtt` via `<track>` cross-origin                  | No CORS errors in browser console; captions render      |

### Go-live checklist

* [ ] VAST parser extracts `<ClosedCaptionFile>` URLs when present
* [ ] JSON parser reads `videoTranscriptFiles[]` when present
* [ ] Graceful fallback when captions or transcripts are absent
* [ ] Video player renders captions from CDN URL
* [ ] Shopper can toggle captions on/off
* [ ] CORS validated for cross-origin `.vtt` fetch
* [ ] Transcript text accessible (if implementing Step 4)
* [ ] Tested with both `.vtt` and `.srt` caption formats

## Troubleshooting and FAQ

### `<ClosedCaptionFiles>` element missing from VAST response

**Likely cause:** The campaign does not have an approved caption file, or caption delivery is not enabled in your environment.

**Solution:**

* Confirm the campaign has an approved caption file (uploaded and passed review).
* Contact your account team to verify the feature is enabled for your namespace.

### CORS error when loading `.vtt` via `<track>` element

**Likely cause:** Content Security Policy or browser CORS enforcement blocking cross-origin fetch of the `.vtt` file.

**Solution:**

* Add the Epsilon CDN domain to your Content Security Policy's `connect-src` and `media-src` directives.
* Set `crossorigin="anonymous"` on the parent `<video>` element if required.

### Captions render but are not synced with video

**Likely cause:** The caption file was authored with incorrect timestamps, or the file is an SRT being loaded as VTT without conversion.

**Solution:**

* Fetch the caption file URL and inspect timestamps against video playback.
* If the file is .srt and your player only supports .vtt, convert at runtime (prepend `WEBVTT\n\n`, replace `,` with `.` in timestamps).

### Transcript URL returns 404

**Likely cause:** The campaign was updated and the transcript file was removed, or CDN propagation delay.

**Solution:**

* Re-fetch the ad response to get the current file URL.
* If the issue persists, contact support.

When contacting support, include:

* Request ID or correlation ID from the generate call
* Timestamp (UTC) of the ad request
* The `<ClosedCaptionFile>` or `videoTranscriptFiles` URL that is failing
* Browser console error (for CORS issues)
* Namespace and campaign ID

## Related articles

* [Video ads](/retail-media-interface/integration/generating-ads/banner-x-responsive/video-ads-banner-x.md)
* [Generate banner X ads for different placements](/retail-media-interface/integration/generating-ads/banner-x-responsive/requesting-banner-x-ads.md)
* [Banner X reference](/retail-media-interface/integration/references/banner-x-reference-1.md)
* [Banner X previewer](/retail-media-interface/integration/generating-ads/banner-x-responsive/integrating-your-banner-x-previewer.md)

<br>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://help.citrusad.com/retail-media-interface/integration/generating-ads/banner-x-responsive/bannerx-video-captions.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
