# Freshpaint Video Platform

## What is Freshpaint Video?&#x20;

Freshpaint Video is a self-hosted video streaming solution for you to use in the place of a third party video hosting platform like YouTube. On a high level, it takes source videos uploaded from customers, encodes them into streaming formats, and serves them to your end-users via an embedded video player which can be dropped into your websites.

## Why is video important?

Serving embedded videos from platforms on your websites without a Business Associate Agreement (BAA) may present a risk of violating HIPAA. This is because these upstream hosts collect data about the user when serving these videos.&#x20;

Let's take YouTube for example. With the out-of-the-box YouTube embed, the end user’s computer will download the video directly from YouTube's servers. This will expose data about the user, such as their IP address, to YouTube. If the user is viewing a video about a specific medical condition, YouTube will have both an identifier (IP address) and health information about the user resulting in exposed Protected Health Information (PHI).

## How can Freshpaint help?

By using Freshpaint Video to host your embedded videos, you can continue serving content on your website without sharing PHI or any unintended data with third-party hosting platforms. Since Freshpaint Video operates entirely within the Freshpaint technology ecosystem, all data collection remains securely covered under your BAA with Freshpaint.

## Getting Started

### Upload your videos to Freshpaint

First, you'll need to transfer your videos to the Freshpaint ecosystem. Follow the steps below using the Freshpaint Video management UI. If you have any questions or feedback regarding this process, please reach out to us <support@freshpaint.io>.&#x20;

{% hint style="info" %}
If your team doesn't have access to the source video files or anticipates any issues uploading them to Freshpaint, please reach out to us at <support@freshpaint.io>.
{% endhint %}

1. Click "Upload Video" in the management UI.<br>

   <figure><img src="https://1666823438-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MA7aDqsXMFbUsWVqonF%2Fuploads%2Ftg1FhawicS8BqbmzeWvB%2FScreenshot%202025-11-13%20at%2018.04.44.png?alt=media&#x26;token=a55bd8fb-13a4-4a36-a55d-a6b47d08fea2" alt=""><figcaption></figcaption></figure>
2. Choose a file, click upload and wait for the file to finish uploading <br>

   <figure><img src="https://1666823438-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MA7aDqsXMFbUsWVqonF%2Fuploads%2FF9YkWNeHPwvXb9wwS0lB%2FScreenshot%202025-11-13%20at%2018.03.16.png?alt=media&#x26;token=c3112c4a-f1e9-407f-80a5-79d13ef6b655" alt=""><figcaption></figcaption></figure>
3. Give the video a title and description and add a custom thumbnail image, if desired. If you do not select a thumbnail image, one will be automatically generated which can be replaced at any time.\
   &#x20;![](https://1666823438-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MA7aDqsXMFbUsWVqonF%2Fuploads%2FplQCjW9DYW530A4euTCl%2Fimage.png?alt=media\&token=79179caf-acb7-46fa-ba62-0f0d3ee37778)
4. Optionally, upload captions for the video. Captions must be in the `.vtt` file format. The label is a readable name represented in the video player. The language code for the caption must represent a valid [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag).

   <figure><img src="https://1666823438-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MA7aDqsXMFbUsWVqonF%2Fuploads%2FBX537NiOmZd0wlACanE1%2FScreenshot%202024-09-24%20at%202.48.02%E2%80%AFPM.png?alt=media&#x26;token=57c79531-f822-4f59-8a11-b68f87f77bc6" alt=""><figcaption></figcaption></figure>

Videos that have just been uploaded will initially be in the "processing" state while Freshpaint encodes the video and prepares it for use in web streaming. This typically takes 5–15 minutes depending on the size of the video.

That's it! Once your video files are uploaded to Freshpaint, all you'll need to do is install the embedded videos onto your application.

### Copy Video URL

Click the "copy" button to the right of the Video Link to copy the URL to your clipboard.&#x20;

{% hint style="warning" %}
The Video Link URL returns an HTML page containing a video player with your selected video. It does not return a video file so may be incompatible with some content management platforms or media players.&#x20;
{% endhint %}

<figure><img src="https://1666823438-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MA7aDqsXMFbUsWVqonF%2Fuploads%2FP4sm6wlXPXAH3fQ4fJic%2Fvideo5.png?alt=media&#x26;token=0bd30d47-8b1f-4d3c-8389-a7f86840e09b" alt=""><figcaption></figcaption></figure>

### Install the embedded videos onto your site

This process may differ depending on your setup. For example, if your team is able to directly edit HTML, or use a CMS for this purpose, all they'd need to do is place the Freshpaint Video embed code that's provided on your application.&#x20;

<figure><img src="https://1666823438-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MA7aDqsXMFbUsWVqonF%2Fuploads%2FiAqtE5u40spLQkCzVcxc%2Fvideo3.png?alt=media&#x26;token=b291271d-5968-487a-be3d-8fdb1d06e1d5" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1666823438-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MA7aDqsXMFbUsWVqonF%2Fuploads%2F1Pr9qE1VCmRNtFxOSD5h%2Fvideo4.png?alt=media&#x26;token=45aade99-8e9f-4ba9-8c7c-34206042cdc0" alt=""><figcaption></figcaption></figure>

If you currently have a YouTube player or similar embedded on your site, you can simply replace the `src` in the iframe with the Freshpaint URL. This will maintain the same settings, such as player size.

You can retrieve the thumbnail for a video by adding  `/thumbnail` to the end of the Freshpaint Video Link.

Please reach out to <support@freshpaint.io> for any assistance needed.&#x20;

#### Automatically Selecting Captions

If you want captions or subtitles to show in your embedded player by default, you can do so by adding the following query parameter to the embed URL using the caption track's Language Code.&#x20;

```
?caption=<lang_code>

# eg. English caption track
?caption=en
```

You can determine the available language codes for a given video on the video edit page in the captions section.

<figure><img src="https://1666823438-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MA7aDqsXMFbUsWVqonF%2Fuploads%2FghBlBTtHwtEroVzi8WT3%2FScreenshot%202025-04-21%20at%2010.27.54.png?alt=media&#x26;token=b9b84c3e-890a-4e1d-aa61-6acc6d8f0862" alt=""><figcaption></figcaption></figure>

### Autoplay and more

Our video player supports boolean attributes including `autoplay`, `muted`, `loop`, `controls`, and more. Unlike standard HTML5 video elements where boolean attributes can only be enabled (presence means true, absence means false), our player allows you to explicitly enable or disable these attributes using query parameters.

#### Boolean Attribute Syntax

To enable a boolean attribute, you can:

* Add it as a query parameter with value `1` or `true`: `?autoplay=1` or `?autoplay=true`
* Add it as a query parameter with an empty value: `?autoplay` (presence alone enables it)

To explicitly disable a boolean attribute, use `0` or `false`:

* `?autoplay=0` or `?autoplay=false`

This behavior differs from the HTML5 specification, where boolean attributes cannot be explicitly disabled, meaning they can only be present (true) or absent (false). Our implementation provides more flexibility by allowing explicit disabling.

**Supported Boolean Attributes**

The following boolean attributes are supported:

* `autoplay` - Automatically start playing the video when the page loads
* `muted` - Start the video muted
* `loop` - Loop the video when it reaches the end
* `controls` - Show/hide the video player controls
* `playsinline` - Play video inline on mobile devices
* `disablepictureinpicture` - Disable picture-in-picture mode
* `disableremoteplayback` - Disable remote playback
* `showplaytext` - Display a "Play Video" text label next to the play button

#### Examples

**Enable autoplay and muted:**

```
https://freshpaint-video.com/<env_id>/<video_id>?autoplay=1&muted=1
```

**Disable controls:**

```
https://freshpaint-video.com/<env_id>/<video_id>?controls=0
```

**Disable autoplay:**

```
https://freshpaint-video.com/<env_id>/<video_id>?autoplay=0
```

**Show the play button text label:**

{% code overflow="wrap" %}

```
https://freshpaint-video.com/<env_id>/<video_id>?showplaytext=1
```

{% endcode %}

#### Controls and Autoplay Behavior

When you hide the video player controls by setting `controls=0`, the video will automatically enable `autoplay` and `muted` to ensure it can still play. This is because modern browsers typically require videos to be muted for autoplay to work, and without controls, users cannot manually start playback.

If you want to hide controls but prevent autoplay, you can explicitly disable it:

```
https://freshpaint-video.com/<env_id>/<video_id>?controls=0&autoplay=0
```

**Note:** Most browsers block click-to-play functionality when controls are hidden, so disabling both controls and autoplay may result in a video that cannot be played (except in Safari). This configuration is generally not recommended.

#### Browser Autoplay Policies

Modern browsers have special rules about when videos can be automatically played (see more in the [Mozilla Autoplay Guide](https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide)). Most browsers only allow autoplay if the video is muted by default. To ensure autoplay works across browsers, always include `muted=1` when using `autoplay=1`:

```
https://freshpaint-video.com/<env_id>/<video_id>?autoplay=1&muted=1
```

### SEO Structured Data (schema.json)

Every Freshpaint-hosted video has an automatically generated `schema.json` file that provides [schema.org VideoObject](https://schema.org/VideoObject) structured data. This helps search engines display rich video results and makes it easy for CMS platforms to programmatically fetch structured data for your videos.

{% hint style="info" %}
If you previously used the YouTube Data API to retrieve structured video data for your CMS/website, `schema.json` is the Freshpaint equivalent — no API key or response parsing required.
{% endhint %}

#### Accessing schema.json

Each video's structured data is publicly available at:

{% code overflow="wrap" %}

```
https://freshpaint-video.com/videos/<env_id>/<video_id>/schema.json
```

{% endcode %}

This URL is also included in the video's `metadata.json` as the `schemaURL` field.

#### Example Response

{% code overflow="wrap" %}

```
{"@context": "https://schema.org","@type": "VideoObject","name": "Welcome to Our Practice","description": "Learn about the services we offer and meet our care team.","thumbnailUrl": "https://freshpaint-video.com/videos/<env_id>/<video_id>/thumb.webp","uploadDate": "2025-03-15T14:30:00Z","duration": "PT2M45S","embedUrl": "https://freshpaint-video.com/<env_id>/<video_id>"}
```

{% endcode %}

#### Field Reference

| Field          | Description                                                | Notes                              |
| -------------- | ---------------------------------------------------------- | ---------------------------------- |
| `@context`     | Always `https://schema.org`                                | Constant                           |
| `@type`        | Always `VideoObject`                                       | Constant                           |
| `name`         | Video title                                                | Required                           |
| `description`  | Video description                                          | Omitted if empty                   |
| `thumbnailUrl` | URL to the video thumbnail                                 | Omitted if no thumbnail is set     |
| `uploadDate`   | Upload timestamp in RFC 3339 UTC format                    | Omitted if not available           |
| `duration`     | Video length in ISO 8601 duration format (e.g., `PT4M13S`) | Omitted if duration is unavailable |
| `embedUrl`     | Embeddable video player URL                                | Always included                    |

{% hint style="info" %}
Optional fields are omitted from the response when the underlying value is empty or not set. Your CMS integration should handle the possible absence of these fields gracefully.
{% endhint %}

#### CMS Integration

Your web development team or CMS vendor can fetch `schema.json` and inject its contents as a `<script type="application/ld+json">` block on any page that embeds the video. This enables Google and other search engines to surface rich video results for those pages.

1. When a page includes a Freshpaint video embed, fetch the corresponding `schema.json` URL.
2. Insert the JSON response into the page's `<head>` as structured data:

{% code overflow="wrap" %}

```
<script type="application/ld+json"><!-- Paste or dynamically inject the schema.json contents here --></script>
```

{% endcode %}

3. Validate the result with [Google's Rich Results Test](https://search.google.com/test/rich-results).

#### When schema.json Updates

The `schema.json` file is regenerated automatically whenever you:

* Save changes to a video's title or description
* Upload or replace a thumbnail
* Upload, edit, or delete a caption track
* Initially upload a video

{% hint style="info" %}
Bulk video edits (editing multiple videos at once) do not currently trigger a `schema.json` refresh. If you use bulk edits, open and save each affected video individually to update its structured data.
{% endhint %}

### Video Engagement Tracking <a href="#video-engagement-tracking" id="video-engagement-tracking"></a>

Freshpaint automatically captures video engagement events when users interact with Freshpaint-hosted videos embedded on your site. These events track when a viewer starts, progresses through, and completes a video.

{% hint style="info" %}
Video engagement events are only generated for Freshpaint-hosted videos. Direct embeds from other video providers (YouTube, Vimeo, etc.) do not auto-produce these events.
{% endhint %}

#### How It Works

When a Freshpaint-hosted video is embedded on a page that has the Freshpaint SDK installed, the video player sends playback signals to the SDK automatically. The SDK translates these signals into `$video_engagement` events that appear in Live View as:

* Video Started — the viewer clicked play or autoplay began
* Video Progress — the viewer reached a progress milestone during playback
* Video Completed — the video played through to the end

#### Sending Events to GA4 (Built-in)

If you use Google Analytics 4, Freshpaint provides a turnkey integration for video engagement. Enable the Video Engagement setting in your GA4 destination configuration, and events are forwarded automatically with no additional mapping required.

<figure><img src="https://1666823438-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MA7aDqsXMFbUsWVqonF%2Fuploads%2FF7G9gIxsyOXDkET0UI50%2FScreenshot%202026-03-30%20at%2012.55.24%E2%80%AFPM.png?alt=media&#x26;token=db3e0177-1a66-42b8-88ae-9635fc9adb2a" alt="" width="375"><figcaption></figcaption></figure>

For setup details, see the [GA4 Proxy Reference — Video Engagement](https://documentation.freshpaint.io/integrations/destinations/apps/google-analytics-4-proxy/google-analytics-4-proxy-reference#video-engagement).

#### Sending Events to Other Destinations

For destinations other than GA4, you can use the video engagement data layer properties to configure your own tracking. This is a manual process where you map the properties listed below to the fields your destination expects.

**Event Types**

Each `$video_engagement` event includes a `$video_event_type` property indicating what happened:

| Event Type       | When It Fires                           |
| ---------------- | --------------------------------------- |
| `video_start`    | The viewer begins playback              |
| `video_progress` | The viewer reaches a progress milestone |
| `video_complete` | The video plays to the end              |

**Video Properties**

These properties are specific to video engagement events and are the primary fields you'll map when configuring a destination:

| Property             | Type    | Description                                          |
| -------------------- | ------- | ---------------------------------------------------- |
| `$video_event_type`  | string  | `video_start`, `video_progress`, or `video_complete` |
| `video_provider`     | string  | Video hosting provider (e.g., `freshpaint`)          |
| `video_title`        | string  | Title of the video                                   |
| `video_url`          | string  | URL of the video player                              |
| `video_duration`     | number  | Total video duration in seconds                      |
| `video_percent`      | number  | Playback progress percentage (0–100)                 |
| `video_current_time` | number  | Current playback position in seconds                 |
| `visible`            | boolean | Whether the video is visible in the viewport         |

In addition to the video-specific properties above, each event also includes standard Freshpaint properties such as `$device_id`, `$session_id`, `$current_url`, UTM parameters, and referrer information. These are the same properties included on all Freshpaint events and can be used for additional filtering or segmentation in your destination.

{% hint style="info" %}
To see these events in real time, open Live View in your Freshpaint dashboard. Video engagement events appear as "Video Started", "Video Progress", and "Video Completed".&#x20;
{% endhint %}

### FAQ

**What are supported file formats and encodings?**

* We support all video encodings supported by AWS MediaConvert (<https://docs.aws.amazon.com/mediaconvert/latest/ug/supported-containers-codecs-details.html>). The container extensions we look for on upload are ".3gp", ".3g2", ".wmv", ".avi", ".flv", ".mkv", ".mov", ".mp4", ".mpeg", ".mpg", ".mp1", ".mpv", ".mxf", ".webm".

**Are there request rate limits?**

* No, we currently have no throttling here. In practice all our customers have pretty low traffic; it's likely not something we want to be forever unlimited, but at the moment it's not costing enough to put anything into place.

**Is there support for adaptive rendering, like based on network bandwidth, viewport etc.,?**

* Yes, we use ABR streaming via HLS and DASH, the two most common ABR formats on the web. We generate variable quality streams based on the upload quality (this is automatically done by MediaConvert), and our player selects the appropriate stream. We don't currently have functionality to manually switch stream quality.

**Does Freshpaint use any CDN to deliver hosted videos?**

* Yes, AWS CloudFront.

**Can the data layer capture usage and interaction of video?**

* Yes. Freshpaint automatically captures `$video_engagement` events — including `video_start`, `video_progress`, and `video_complete` — for all Freshpaint-hosted videos. These events are available in the data layer and appear in Live View. You can use them in two ways:
  * Google Analytics 4: Enable the built-in [Video Engagement](https://documentation.freshpaint.io/integrations/destinations/apps/google-analytics-4-proxy/google-analytics-4-proxy-reference#video-engagement) setting in your GA4 destination for turnkey forwarding.
  * Other destinations: Map the video engagement properties to your destination manually. See the [Video Engagement Tracking](https://app.gitbook.com/o/-MA7b_ezO9UcpNU5sTg_/s/-MA7aDqsXMFbUsWVqonF/~/edit/~/changes/1457/integrations/freshpaint-video-platform#video-engagement-tracking) section above for the full list of available properties.

**Is it possible to add/edit metadata for videos and what is the current schema. Can custom metadata attributes be added?**

* The video's title, description, and thumbnail are included as metadata in the player HTML.

The list of tags we populate is: "title" "description" "og:url" "og:title" "og:description" "og:video:url" "og:video:secure\_url" "og:updated\_time" "og:image" "og:image:secure\_url" "og:image:type" "twitter:player" "twitter:url" "twitter:title" "twitter:description" "twitter:image" (note that many of these are populated with identical values), which is sufficient for the majority of web crawlers, social media embeds, and CMS platforms to recognize the video.

In addition, every video has a `schema.json` endpoint that provides [schema.org VideoObject](https://schema.org/VideoObject) structured data for SEO and CMS integration. See the [SEO Structured Data](#seo-structured-data-schema.json) section above for the URL pattern, example response, and field reference.

Custom metadata attributes are not supported. You can edit a video's title, description, and thumbnail through the Freshpaint UI, and these values are reflected in both the HTML meta tags and `schema.json`, but you cannot add additional metadata fields beyond these.

**Are you able to send video engagement events to Google Analytics 4?**

* Yes, engagement events such as `video_start`, `video_progress`, and `video_complete`  for videos hosted in Freshpaint can be optionally enabled to be sent to GA4. Please [click here](https://documentation.freshpaint.io/integrations/destinations/apps/google-analytics-4-proxy/google-analytics-4-proxy-reference#video-engagement) to learn how to configure the GA4 destination to receive these events.

### What's Next?

Now that your videos are hosted on Freshpaint, here are some next steps:

* Configure GA4 video engagement — Enable turnkey video event forwarding in your [GA4 destination settings](https://documentation.freshpaint.io/integrations/destinations/apps/google-analytics-4-proxy/google-analytics-4-proxy-reference#video-engagement).
* Add structured data to your site — Have your web team fetch each video's `schema.json` and inject it as structured data for richer search results.
* Explore destination integrations — Browse the full list of [supported destinations](https://documentation.freshpaint.io/integrations/destinations) to route video engagement data where your team needs it.
