Integration by Closed Caption Creator

Seamless Caption Editing, Anywhere.

Embed our powerful subtitle editor

Give your users professional-grade caption editing without building it yourself. Our new IFrame Embed support lets you integrate our intuitive caption editor into your platform with minimal effort. Your users can edit, review, and correct captions without ever leaving your ecosystem, while you save valuable development time and resources.

🎬 Watch Free CourseView Documentation

Browser-based Subtitle Editor

Subtitle Editor, Ready for Your Platform.

Embed our editor on your website using an IFrame tag and a couple lines of code.

Embedding our caption editor is remarkably simple—just a few lines of code to get started. With support for over 30 different closed caption formats, we seamlessly integrate with virtually any captioning workflow. Whether you're handling standard SRT files or specialized broadcast formats, our editor has you covered.

Designed specifically for broadcast manufacturers and video platforms, our embedded editor fills the critical gap for services that support video captioning but lack editing capabilities. We're committed to your success with free technical support and training throughout the development process, ensuring a smooth implementation that meets your specific needs.

  • Easy to setup and configure
  • No backend required
  • Import and publish captions
  • Import multiple subtitle tracks and languages
  • Media support for HLS streams,  cloud storage, YouTube URL, Vimeo links, and more
  • Theming options for seamless brand integration
  • Flexible pricing options
Read Documentation  Watch Course
ElevenLabs Display Photo showing a female robot and the text "Eleven Labs AI Voice Generator & Best Text to Speech"

Have Questions?

Contact our team for a free demo.

Book a Demo

Developer Docs

IFrame Remote Documentation

Build your own integration with Closed Caption Creator using our IFrame Remote.

Leverage Closed Caption Creator's incredible flexibility for all your captioning and timed-text metadata needs. This section provides the essential reference documentation—including setup guides, API details, and practical examples—to get your integration up and running fast.

Introduction

Overview

The Closed Caption Creator IFrame Remote provides a powerful, embeddable subtitle and timed-text editor designed to seamlessly integrate into your web-based applications. Instead of building complex captioning functionalities from scratch, you can leverage our professional-grade editor to empower your users with advanced editing capabilities directly within your platform.

How It Works: Communication with PenPal JS

Our IFrame Remote is designed for secure and efficient cross-window communication using PenPal JS. This lightweight library facilitates reliable two-way communication between your parent application and the embedded editor, allowing you to:

  • Programmatically control the editor's behavior (e.g., load media, import captions, apply settings).
  • Receive updates and metadata including markers, captions, speakers, etc.

This approach ensures a robust and flexible integration, enabling you to build highly interactive and customized captioning workflows.

Getting Started Resources

To help you begin immediately, here are two essential links:

  • IFrame POC System: Use the following link as the source attribute in your IFrame tag to begin: https://iframe-demo.closedcaptioncreator.com
  • PenPal JS CDN: Include this library in your parent application to enable communication with the IFrame Remote: https://unpkg.com/penpal@^7/dist/penpal.min.js

Dive into the Getting Started section below to begin your integration.

Getting Started

This section will walk you through the fundamental steps to embed the Closed Caption Creator IFrame Remote into your web application and establish communication for controlling the editor.

Prerequisites

To follow along, you should have a basic understanding of HTML and JavaScript.

Step 1: Import Penpal JS

Import PenPal JS using NPM, or CDN.

Code Snippet
HTML Code
<!DOCTYPE html>
<html lang="en">
<head>
    ...
</head>
<body>
    ...
    <script src="https://unpkg.com/penpal@^7/dist/penpal.min.js"></script>
</body>
</html>
Step 2: Embed the IFrame

Begin by adding an element to your HTML where you want the editor to appear. Ensure it points to the Closed Caption Creator IFrame demo URL as its src (https://iframe-demo.closedcaptioncreator.com). Giving your iframe an id (e.g., editor) will make it easy to reference in JavaScript.

Code Snippet
HTML Code
<div class="iframe-container">
    <iframe id="editor" 
        src="https://iframe-demo.closedcaptioncreator.com/" 
        class="w-100 rounded" 
        style="height: 90vh;" 
        frameborder="0" 
        allow="accelerometer; autoplay; clipboard-write; encrypted-media"
        allowfullscreen>
    </iframe>
</div>
  • id="editor": Used to easily select the iframe element in JavaScript.
  • src="https://iframe-demo.closedcaptioncreator.com/": This is the URL of the Closed Caption Creator IFrame editor itself.
  • style="height: 90vh;": Sets the initial height of the iframe to take up 90% of the viewport height. Adjust this as needed for your layout.
  • allowfullscreen: Allows the user to toggle the iframe into fullscreen mode.
JavaScript Code
document.addEventListener('DOMContentLoaded', () => {
    const iframe = document.getElementById('editor');
});
Step 3: Establish Connection with PenPal

Once the IFrame is loaded, you'll establish a connection using PenPal. This connection provides a remote object that allows you to call functions exposed by the IFrame, and optionally, define methods on the parent window that the IFrame can call.

Code Snippet
JavaScript Code
document.addEventListener('DOMContentLoaded', () => {
    const iframe = document.getElementById('editor'); // Get reference to your iframe
    let iframeConnection = null; // Will store the remote object for calling iframe functions

    const connectToIframe = async () => {
        try {
            // Create a messenger to communicate with the iframe
            const messenger = new Penpal.WindowMessenger({
                remoteWindow: iframe.contentWindow,
                allowedOrigins: ['*'] // IMPORTANT: For production, restrict this to your iframe's origin!
            });

            // Connect to the iframe
            const connection = Penpal.connect({
                messenger,
                // Define methods the parent exposes to the iframe (optional)
                methods: {
                    //...
                }
            });

            // Wait for the connection to be established
            const remote = await connection.promise;
            console.log('Connected to iframe');
            iframeConnection = remote; // Store the remote object for later use

            // ... (initial editor configuration calls will go here)
            
        } catch (error) {
            console.error('Connection error:', error);
        }
    };

    // Initialize connection when iframe loads
    iframe.addEventListener('load', connectToIframe);
});
  • Penpal.WindowMessenger: Configures how PenPal will communicate. remoteWindow points to the iframe's content window. allowedOrigins: ['*'] is set for demo purposes, but you must specify the exact origin(s) of your iframe (e.g., https://iframe-demo.closedcaptioncreator.com) in a production environment for security.
  • Penpal.connect: Initiates the connection. The methods object allows you to define functions on your parent page that the IFrame can call (e.g., parentMethod, notifyParent).
  • connection.promise: This Promise resolves once the connection to the IFrame is successfully established. The resolved value (remote) is the object containing all the functions exposed by the IFrame Remote that you can call.
Step 4: Initial Editor Configuration

Once connected, you can immediately start interacting with the editor. A common first step is to configure basic project settings and load media/captions. This often involves showing a loading modal to the user.

Code Snippet
JavaScript Code
await remote.updateModalTitle("Project Import");
await remote.toggleStatusModal(true); // Show the modal regardless of its current state
await remote.updateModalStatus({ // Update modal progress and message
    progress : 5,
    msg : "Setting project settings..."
});

/* Set Project Settings (framerate, dropFrame, incode) */
await remote.setProjectName("IFrame Demo Project");
await remote.setProjectDescription("This is a demo project for iframe communication.");
await remote.setProjectFrameRate(29.97);
await remote.setProjectDropFrame(true);
await remote.setProjectIncode("00:00:00:00");

/* Step 2. Loading Subtitle File */
await remote.updateModalStatus({
    progress : 50,
    msg : "Loading Subtitles..."
});

await remote.importSubtitle(subtitleUrl, subtitleProfile);

/* Step 3. Loading Video File */
await remote.updateModalStatus({
    progress : 75,
    msg : "Loading media..."
});

await remote.importMedia(mediaUrl, "https://m.media-amazon.com/images/M/MV5BMTljNGI3NTAtYTU2ZC00ZGQzLTg2ZDAtNzQ1NDk1YzBiY2M5XkEyXkFqcGc@._V1_QL75_UX522_.jpg");

/* Audio Peak Data */
await remote.setMediaPeaksPath(audioPeaksUrl);
await remote.toggleTimeline(true);

await remote.toggleStatusModal(false); // Hide the modal
await remote.alertUser({ // Show a success alert
    title : "Project Import",
    text : "Project imported successfully!",
});
  • This sequence demonstrates calling various functions on the remote object to set up the project, update a status modal, import media, and load subtitles.
  • remote.updateModalTitle(), remote.toggleStatusModal(), and remote.updateModalStatus() are examples of controlling the editor's UI state.
  • remote.setProjectName(), remote.setProjectFrameRate(), etc., configure the project's metadata.
  • remote.importSubtitle() and remote.importMedia() are key functions for loading content into the editor.
  • remote.setMediaPeaksPath() and remote.toggleTimeline() control additional visual elements like audio waveforms.

You now have the fundamental structure for embedding and communicating with the Closed Caption Creator IFrame Remote. Proceed to the Functions Reference to explore all available methods for deeper integration.

Support / Feedback

We are committed to ensuring your successful integration of the Closed Caption Creator IFrame Remote. If you encounter any questions during development, require personalized assistance, or have feedback on our documentation or product, please don't hesitate to reach out. We offer dedicated developer training and direct support to help with all aspects of your integration. Visit our Contact Page to connect with our team.

Functions

The Closed Caption Creator IFrame Remote exposes a set of asynchronous methods that your parent application can call via the PenPal connection. These methods allow you to control various aspects of the editor, from loading content to managing project settings and retrieving data.

All calls to these functions should be awaited, as they return Promises.

Function Name Arguments Description Return
updateModalStatus(statusInfo) statusInfo: { progress: number, msg: string } Updates the progress bar and status message in the IFrame remote modal. Promise<boolean>
toggleStatusModal(show) show: boolean Shows or hides the IFrame remote status modal. If show is true, displays the modal; otherwise toggles its state. Promise<void>
updateModalTitle(title) title: string Updates the title text displayed in the IFrame remote modal header. Promise<boolean>
completeImport() None Displays a success alert and closes the modal after a successful import operation. Promise<void>
failImport(msg) msg: string Displays an error alert with the provided message and closes the modal after a failed import. Promise<void>
alertUser(alertObj) alertObj: { title?: string, text?: string } Displays a SweetAlert2 modal with a custom title and message to alert the user. Promise<void>
setProjectId(id) id: string Sets the unique identifier for the current project. Promise<void>
setProjectName(name) name: string Sets the display name of the current project. Promise<void>
setProjectDescription(description) description: string Sets the description for the current project. Promise<void>
setProjectFrameRate(frameRate) frameRate: number (e.g., 23.976, 25, 29.97, 30) Sets the project's video frame rate. Promise<void>
setProjectDropFrame(dropFrame) dropFrame: boolean Sets whether the project uses drop-frame timecode. Promise<void>
setProjectIncode(incode) incode: string (timecode string, e.g., "00:00:00:00") Sets the project's incode (start timecode). Promise<void>
setProjectState(state) state: object (Partial project state object) Updates multiple project state properties at once. Promise<void>
setDisplaySettings(settings) settings: { displayWidth?: number (1-100), displayHeight?: number (1-50), xPadding?: number (0-50), yPadding?: number (0-50), lineSpacing?: number (0-50) } Updates caption display settings including width, height, padding, and line spacing. Settings are validated and saved to localStorage. Promise<{ success: boolean, message: string, settings?: object }>
setMediaPeaksPath(path) path: string (URL to audio peaks JSON) Sets the URL for the audio waveform peaks data to be displayed in the timeline. Promise<void>
setMediaPeaksData(data) data: object (Audio peaks data object) Directly sets the audio waveform peaks data as an object, bypassing a URL fetch. Promise<void>
useFallbackTimeline() None Instructs the timeline to use a fallback visual representation if peaks data is unavailable or problematic. Promise<void>
useDefaultTimeline() None Ensures the timeline attempts to use provided peaks data rather than a fallback. Promise<void>
toggleTimeline(enable) enable: boolean Toggles the visibility of the timeline UI element. If enable is true, shows the timeline. If false, toggles its current state. Promise<void>
setPoster(thumbnailUrl) thumbnailUrl: string Sets the poster image (thumbnail) for the video player. Promise<void>
importMedia(mediaUrl, thumbnailUrl, mediaType, storageType) mediaUrl: string
thumbnailUrl: string (Optional)
mediaType: string (e.g., "video/mp4", default "video/mp4")
storageType: string (e.g., "Cloud Storage", "Vimeo", "YouTube", "HLS Manifest", default "Cloud Storage")
Loads a video or audio file into the editor from a specified URL. Promise<{ success: boolean, message: string }>
setTranscript(transcript, profile) transcript: object (Transcription data object)
profile: string (Source profile, e.g., "deepgram", default "deepgram")
Sets the raw transcript data to be processed by the editor. Promise<void>
loadTranscript() None Processes the transcript data previously set via setTranscript and imports it into the editor as a new event group. Promise<{ success: boolean, message: string }>
importSubtitle(subtitleUrl, profile, decodeOptions, evgOptions, autoFormatOptions) subtitleUrl: string (URL to subtitle file)
profile: string (Subtitle format profile, e.g., "subRip", "webVtt")
decodeOptions: object (Optional, format-specific decoding options)
evgOptions: object (Optional, event group options like type, name, language)
autoFormatOptions: { enable?: boolean, maxLines?: number, maxChars?: number, minDuration?: number, allowOrphanWords?: boolean } (Optional)
Loads a subtitle file from a URL into the editor, converting it to the internal project format. Optionally applies auto-formatting. Promise<{ success: boolean, message: string }>
exportSubtitle(profile, encodeOptions, saveAsFile) profile: string (Target subtitle format profile)
encodeOptions: object (Optional, format-specific encoding options)
saveAsFile: boolean (If true, triggers a file download; otherwise, returns data)
Exports the current captions from the editor into a specified format. Can return the data or trigger a file download. Promise<{ success: boolean, message: string, data?: string, fileName?: string }>
getMarkers() None Retrieves the current marker data from the editor, including all marker lists and their markers. Promise<object> (Copy of marker state)
setMarkers(markers) markers: object (Marker state object with lists and selected properties) Sets the entire marker state for the editor, allowing you to load custom markers and lists. Promise<void>
createMarkerList(markerList) markerList: { name: string, color?: string } Creates a new named marker list within the editor. Promise<void>
insertMarker(marker, markerListId) marker: object (_Marker object, e.g., { time: number, comment: string })
markerListId: number (Optional, ID of the target marker list, default 0)
Inserts a new marker into a specified marker list. Promise<void>
loadStyleGuide(styleGuideData) styleGuideData: object (Style guide configuration object) Loads or updates a style guide in the editor's local storage. If a guide with the same ID exists, it will be updated. Promise<{ success: boolean, message: string, id?: string }>
getStyleGuides() None Retrieves all style guides currently stored in local storage. Promise<{ success: boolean, data: array, count: number, message: string }>
qcEventGroup(eventGroupId, styleGuideId) eventGroupId: string (ID of event group to validate)
styleGuideId: string (ID of style guide to use for validation)
Runs quality control validation on a specified event group using a specified style guide. Returns all validation errors found. Promise<{ success: boolean, errors: array, errorCount: number, eventGroupId: string, eventGroupName: string, styleGuideId: string, styleGuideName: string, message: string }>
getSelectedEventGroupId() None Retrieves the ID and details of the currently selected event group in the editor. Promise<{ success: boolean, id: string|null, index: number|null, name?: string, type?: string, eventCount?: number, message: string }>
loadProjectData(ccprj) ccprj: object or string (Closed Caption Project JSON data) Loads a complete Closed Caption Creator project (in .ccprj format) into the editor. Promise<{ success: boolean, message: string }>
getProjectData() None Retrieves the entire current project data from the editor in .ccprj JSON format. Promise<object> (Complete project JSON)
getSelectedEvents() None Retrieves all currently selected events from the active event group. Promise<array> (Array of selected event objects)
selectEventById(id) id: string (Event ID) Selects a specific event in the current event group by its unique ID. Promise<void>
selectEventByIndex(index) index: number | array (Event index or array of indices) Selects one or more events in the current event group by their index position(s). Accepts a single index or an array of indices for multiple selection. Promise<void>
scrollToEventById(id) id: string (Event ID) Scrolls the event list to make the event with the specified ID visible in the editor. Promise<void>
scrollToEventByIndex(index) index: number (Event index) Scrolls the event list to make the event at the specified index visible in the editor. Promise<void>

Importing & Exporting Subtitles

A core functionality of the Closed Caption Creator IFrame Remote is the ability to seamlessly import existing subtitle files and export edited captions into various formats. This section details the methods and key parameters involved in these crucial operations.

Importing Subtitles

To load subtitles into the editor, you will use the importSubtitle method. This method is designed to handle a wide array of caption formats and allows for granular control over how the data is interpreted.

importSubtitle(subtitleUrl, profile, decodeOptions, eventGroupOptions)

  • subtitleUrl: 
    • This required parameter specifies the direct URL from which the subtitle file will be read. The editor will fetch the content from this location.
  • profile (Source Profile): 
    • This required string parameter instructs Closed Caption Creator on how to correctly parse and decode the incoming caption data from the subtitleUrl. Each profile corresponds to a specific subtitle format (e.g., "subRip" for SRT, "webVtt" for VTT).
    • A comprehensive list of supported source profiles is available via our API documentation:
      https://api.closedcaptionconverter.com/help/profiles/source
  • decodeOptions:
    • This optional object parameter allows you to provide special, profile-specific settings that influence how the caption data is decoded. For instance, a profile might have options for handling specific character encodings or timecode offsets.
    • While optional, understanding these can be crucial for precise imports. A full list of decode options for each profile is detailed in our API documentation: https://api.closedcaptionconverter.com/help/profiles/all
  • eventGroupOptions:
    • This optional object parameter provides settings for the "Event Group" that will be created or updated within the editor. Event Groups are fundamental for real-time error tracking and validation of subtitle properties.
    • Key properties you can set include:
      • maxChars: Maximum characters per caption event.
      • maxLines: Maximum number of lines per caption event.
      • minDuration: Minimum duration for a caption event.
      • maxDuration: Maximum duration for a caption event.
      • overlap: Defines behavior for overlapping caption events.
      • maxCps: Maximum characters per second (readability check).
      • maxWpm: Maximum words per minute (readability check).
Exporting Subtitles

To retrieve the edited captions from the editor, you will use the exportSubtitle method. This allows you to generate output in your desired target format.

exportSubtitle(profile, encodeOptions, saveAsFile)

  • profile (Target Profile):
    • This required string parameter specifies the format in which the editor should encode the current caption data. This must match one of our supported target profiles.
    • A complete list of supported target profiles is available via our API documentation:
      https://api.closedcaptionconverter.com/help/profiles/target
  • encodeOptions: 
    • Similar to decodeOptions, this optional object parameter provides special, profile-specific settings used by the encoder when generating the output subtitle file. These settings can affect output formatting, metadata inclusion, or specific technical requirements of the target format.
    • A full list of encode options for each profile is available via our API documentation:
      https://api.closedcaptionconverter.com/help/profiles/all
  • saveAsFile:
    • This boolean flag dictates the behavior of the exportSubtitle method.
      • If true, the editor will trigger a file download in the user's browser, allowing them to save the exported subtitle file directly.
      • If false (default), the method will return the file contents (as a string or binary data, depending on the format) within the Promise resolution, allowing your parent application to handle the data programmatically (e.g., upload to cloud storage, display in UI).
QC & Review Workflows

One of the most powerful features of the Closed Caption Creator IFrame Remote is the ability to implement sophisticated quality control (QC) and review workflows directly within your application. By leveraging custom style guides, you can enforce caption quality standards, catch common errors, and ensure that all subtitles meet your specific requirements before they're finalized or published.

This section introduces the QC capabilities available through the IFrame Embed solution, with a focus on custom style guides and how to integrate validation checks into your approval workflows.

Understanding Custom Style Guides

Custom style guides are configurable rulesets that define quality standards and formatting requirements for your captions. These guides can check for a wide range of issues, including:

  • Reading speed violations - Captions that display too quickly or slowly for comfortable reading
  • Maximum duration limits - Captions that stay on screen longer than recommended
  • Minimum duration limits - Captions that flash by too quickly to be readable
  • Line length restrictions - Captions with too many characters per line
  • Caption count limits - Too many lines displayed simultaneously
  • Gap detection - Excessive gaps between consecutive captions
  • Text formatting rules - Requirements for capitalization, punctuation, and special characters

Style guides provide flexibility for different use cases—whether you're creating captions for broadcast television, online streaming platforms, educational content, or accessibility compliance. Each use case may have unique requirements, and custom style guides allow you to enforce those standards programmatically.

Creating and Loading Style Guides

Style guides can be created and loaded in two primary ways:

1. User-Created Style Guides

Users can create their own style guides directly within the Closed Caption Creator interface, defining rules that match their specific workflow needs. These guides are saved to the user's account and can be applied to any project.

2. Dynamically Loaded Style Guides

For more advanced integrations, your parent application can dynamically create and load style guides based on the specific project, client requirements, or platform standards. This approach is ideal when different projects require different validation rules, or when you want to maintain centralized control over caption quality standards across your platform.

The loadStyleGuide function enables you to programmatically load a style guide into the editor, allowing for automated QC workflows.

Style Guide JSON Schema

Below is the JSON schema that defines the structure of a custom style guide. This schema allows you to specify the rules and thresholds that will be applied during validation:

Code Snippet
JSON Schema
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Style Guide Configuration",
  "description": "Configuration object for caption/subtitle style guide validation rules",
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Unique identifier for the style guide (UUID v4 format)",
      "example": "550e8400-e29b-41d4-a716-446655440000"
    },
    "name": {
      "type": "string",
      "description": "Display name for the style guide",
      "default": "Untitled Style Guide",
      "example": "Netflix Style Guide"
    },
    "enabled": {
      "type": "boolean",
      "description": "Whether this style guide is currently active for validation",
      "default": true
    },
    "totalMaxChars": {
      "type": "number",
      "description": "Maximum total characters allowed across all lines in a single caption event",
      "default": 9999,
      "minimum": 0
    },
    "minLines": {
      "type": "number",
      "description": "Minimum number of lines allowed per caption event",
      "default": 1,
      "minimum": 1
    },
    "maxLines": {
      "type": "number",
      "description": "Maximum number of lines allowed per caption event",
      "default": 4,
      "minimum": 1
    },
    "maxChars": {
      "type": "number",
      "description": "Maximum characters allowed per line",
      "default": 32,
      "minimum": 1
    },
    "maxDuration": {
      "type": "number",
      "description": "Maximum duration (in seconds) a caption event can be displayed",
      "default": 6,
      "minimum": 0
    },
    "minDuration": {
      "type": "number",
      "description": "Minimum duration (in seconds) a caption event must be displayed",
      "default": 0.2,
      "minimum": 0
    },
    "minCps": {
      "type": "number",
      "description": "Minimum characters per second reading speed",
      "default": 0,
      "minimum": 0
    },
    "maxCps": {
      "type": "number",
      "description": "Maximum characters per second reading speed (typically 15-20 for comfortable reading)",
      "default": 9999,
      "minimum": 0
    },
    "minWpm": {
      "type": "number",
      "description": "Minimum words per minute reading speed",
      "default": 0,
      "minimum": 0
    },
    "maxWpm": {
      "type": "number",
      "description": "Maximum words per minute reading speed",
      "default": 9999,
      "minimum": 0
    },
    "minWordsPerLine": {
      "type": "number",
      "description": "Minimum number of words allowed per line",
      "default": 1,
      "minimum": 1
    },
    "maxWordsPerLine": {
      "type": "number",
      "description": "Maximum number of words allowed per line",
      "default": 10,
      "minimum": 1
    },
    "minEventGap": {
      "type": "number",
      "description": "Minimum gap (in seconds) required between consecutive caption events",
      "default": 0,
      "minimum": 0
    },
    "maxEventGap": {
      "type": "number",
      "description": "Maximum gap (in seconds) allowed between consecutive caption events",
      "default": 6,
      "minimum": 0
    },
    "minEventGapTolerance": {
      "type": "number",
      "description": "Tolerance threshold (in seconds) for minimum event gap violations",
      "default": 0,
      "minimum": 0
    },
    "maxEventGapTolerance": {
      "type": "number",
      "description": "Tolerance threshold (in seconds) for maximum event gap violations",
      "default": 6,
      "minimum": 0
    },
    "overlap": {
      "type": "boolean",
      "description": "Flag to detect overlapping caption events (when enabled, overlaps are flagged as violations)",
      "default": true
    },
    "illegalChars": {
      "type": "boolean",
      "description": "Flag to detect illegal or unsupported characters in captions",
      "default": false
    },
    "hyphenSpace": {
      "type": "boolean",
      "description": "Flag to detect improper spacing around hyphens",
      "default": false
    },
    "hasUnderscore": {
      "type": "boolean",
      "description": "Flag to detect underscores in caption text (often not allowed)",
      "default": false
    },
    "periods": {
      "type": "boolean",
      "description": "Flag to validate period usage in captions",
      "default": false
    },
    "missingSpeaker": {
      "type": "boolean",
      "description": "Flag to detect captions missing speaker identification",
      "default": false
    },
    "useEllipses": {
      "type": "boolean",
      "description": "Flag to validate ellipses usage in captions",
      "default": false
    },
    "spellNumbers": {
      "type": "boolean",
      "description": "Flag to require numbers to be spelled out as words",
      "default": false
    },
    "spellNumbersAtStart": {
      "type": "boolean",
      "description": "Flag to require numbers at the start of sentences to be spelled out",
      "default": false
    },
    "netflixGlyphs": {
      "type": "boolean",
      "description": "Flag to validate Netflix-specific glyph requirements",
      "default": false
    },
    "partialItalics": {
      "type": "boolean",
      "description": "Flag to detect partial italic formatting within a caption event",
      "default": false
    },
    "fullItalics": {
      "type": "boolean",
      "description": "Flag to detect fully italicized caption events",
      "default": false
    },
    "partialBold": {
      "type": "boolean",
      "description": "Flag to detect partial bold formatting within a caption event",
      "default": false
    },
    "fullBold": {
      "type": "boolean",
      "description": "Flag to detect fully bolded caption events",
      "default": false
    },
    "partialUnderline": {
      "type": "boolean",
      "description": "Flag to detect partial underline formatting within a caption event",
      "default": false
    },
    "fullUnderline": {
      "type": "boolean",
      "description": "Flag to detect fully underlined caption events",
      "default": false
    },
    "repeatWords": {
      "type": "boolean",
      "description": "Flag to detect repeated words in caption text",
      "default": false
    },
    "fitSubtitles": {
      "type": "boolean",
      "description": "Flag to require all subtitles to fit on a single row",
      "default": false
    },
    "leadingTrailingSpace": {
      "type": "boolean",
      "description": "Flag to detect leading or trailing whitespace in caption text",
      "default": false
    },
    "whitespace": {
      "type": "boolean",
      "description": "Flag to detect excessive or improper whitespace in captions",
      "default": false
    },
    "blankLines": {
      "type": "boolean",
      "description": "Flag to detect blank lines within caption events",
      "default": false
    },
    "positionTopLeft": {
      "type": "boolean",
      "description": "Flag to validate captions positioned in the top-left area",
      "default": false
    },
    "positionTopCenter": {
      "type": "boolean",
      "description": "Flag to validate captions positioned in the top-center area",
      "default": false
    },
    "positionTopRight": {
      "type": "boolean",
      "description": "Flag to validate captions positioned in the top-right area",
      "default": false
    },
    "positionCenterLeft": {
      "type": "boolean",
      "description": "Flag to validate captions positioned in the center-left area",
      "default": false
    },
    "positionCenter": {
      "type": "boolean",
      "description": "Flag to validate captions positioned in the center area",
      "default": false
    },
    "positionCenterRight": {
      "type": "boolean",
      "description": "Flag to validate captions positioned in the center-right area",
      "default": false
    },
    "positionBottomLeft": {
      "type": "boolean",
      "description": "Flag to validate captions positioned in the bottom-left area",
      "default": false
    },
    "positionBottomCenter": {
      "type": "boolean",
      "description": "Flag to validate captions positioned in the bottom-center area",
      "default": false
    },
    "positionBottomRight": {
      "type": "boolean",
      "description": "Flag to validate captions positioned in the bottom-right area",
      "default": false
    },
    "positionYOffset": {
      "type": "boolean",
      "description": "Flag to validate vertical position offset values",
      "default": false
    },
    "positionXOffset": {
      "type": "boolean",
      "description": "Flag to validate horizontal position offset values",
      "default": false
    },
    "approvalPassed": {
      "type": "boolean",
      "description": "Flag to filter/validate captions marked as approved",
      "default": false
    },
    "approvalFailed": {
      "type": "boolean",
      "description": "Flag to filter/validate captions marked as failed approval",
      "default": false
    },
    "approvalNotSet": {
      "type": "boolean",
      "description": "Flag to filter/validate captions with no approval status set",
      "default": false
    },
    "notes": {
      "type": "boolean",
      "description": "Flag to validate presence of notes/annotations on captions",
      "default": false
    },
    "tags": {
      "type": "boolean",
      "description": "Flag to validate presence of tags on captions",
      "default": false
    },
    "forced": {
      "type": "boolean",
      "description": "Flag to validate forced narrative captions (non-dialogue captions)",
      "default": false
    }
  },
  "examples": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Netflix Standard",
      "enabled": true,
      "totalMaxChars": 84,
      "minLines": 1,
      "maxLines": 2,
      "maxChars": 42,
      "maxDuration": 7,
      "minDuration": 0.833,
      "minCps": 0,
      "maxCps": 20,
      "minWpm": 0,
      "maxWpm": 9999,
      "minWordsPerLine": 1,
      "maxWordsPerLine": 10,
      "minEventGap": 0.083,
      "maxEventGap": 6,
      "minEventGapTolerance": 0,
      "maxEventGapTolerance": 6,
      "overlap": true,
      "netflixGlyphs": true,
      "leadingTrailingSpace": true,
      "whitespace": true
    },
    {
      "id": "660e8400-e29b-41d4-a716-446655440001",
      "name": "BBC Standard",
      "enabled": true,
      "totalMaxChars": 9999,
      "minLines": 1,
      "maxLines": 2,
      "maxChars": 37,
      "maxDuration": 6,
      "minDuration": 0.3,
      "minCps": 0,
      "maxCps": 17,
      "minWpm": 0,
      "maxWpm": 200,
      "minWordsPerLine": 1,
      "maxWordsPerLine": 10,
      "minEventGap": 0,
      "maxEventGap": 6,
      "overlap": true,
      "illegalChars": true,
      "leadingTrailingSpace": true
    }
  ]
}
    Implementing QC Validation Workflows

    Once a style guide is loaded, the user or the parent application can trigger validation checks and retrieve any errors or warnings that are found. This enables you to build sophisticated approval workflows where captions must pass QC validation before they can be published or saved.

    A typical QC workflow involves these steps:

    1. Load the project - Import media and subtitles as usual
    2. Load the style guide - Use loadStyleGuide() to apply the appropriate quality standards
    3. Trigger validation - Run the style guide checks against the current Event Group
    4. Review results - Retrieve any errors or warnings found during validation
    5. Handle outcomes - Decide whether to allow publishing (if no critical errors) or require corrections (if errors are found)
    6. Provide feedback - Display errors  to the user using the status modal so they can make corrections

    This approach gives you complete control over when and how quality checks are performed, and allows you to integrate caption validation seamlessly into your existing content approval pipelines.

    More Solutions

    Closed Captioning

    Create closed captioning and subtitles for your broadcast videos using Closed Caption Creator. Create, edit, and review closed captioning using our intuitive timed-text editor.

    Learn More

    Translation & Localization

    Translate and localize closed captioning, and subtitles in our dedicated Translation UI. View the original source language alongside the translated text to ensure an accurate translation.

    Learn More

    Transcripts & Dialogue Lists

    Create as-broadcast scripts, and dialogue lists with custom notes, tags, Speaker IDs, and more using Closed Caption Creator. Support for Microsoft Word (.docx), CSV, and plaintext formats. 

    Learn More

    Audio Description & DV

    Create lifelike audio descriptions and described video (DV) using the power of synthetic voice. Unlock the ability to create closed captioning and audio descriptions all in one application. 

    Learn More

    Embed & Extract 608/708 Closed Captioning

    Package broadcast-ready files with the CC Embed export option for Closed Caption Creator. Supports MXF, MP4, MOV, and more.

    Learn More

    File Converter API

    Automate your closed caption and subtitle workflows using our dedicated Closed Caption Converter API. Support over 30 different closed caption and subtitle file formats. 

    Learn More