Non-Protected Content (Clear Delivery)
Clear delivery lets a project ingest and serve selected videos without DRM, using the exact same upload, transcoding and webhook pipeline as protected content. It is designed for platform-managed promotional assets such as user-created trailers.
Overview
By default, every piece of content in a Media Protection project is packaged with DRM. When clear delivery is enabled for a project, individual contents can be created with protectionMode: "clear". These contents are:
- Uploaded and imported through the same endpoints as protected content.
- Transcoded into the same quality ladder, producing HLS, DASH and, when available, a progressive rendition — all unencrypted.
- Served from your project's custom subdomain through signed URLs that your backend can generate offline (no API call required).
- Still fully covered by EngageKit: snapshots, previews and auto-generated trailers are produced as usual.
Key properties:
| Property | Behavior |
|---|---|
protectionMode | "drm" (default when absent) or "clear". Immutable after creation. |
| Input types | Video and audio only. Images are rejected for clear contents, even if image processing is enabled. |
| Playback tokens | Not applicable. generatePlaybackToken returns 409 for clear contents. |
| Player | Bring your own (hls.js, Shaka, dash.js, native HLS or a plain <video> tag). The Fastevo Player is DRM-only. |
| Forensic watermarking | Not applied. Clear content is downloadable by anyone holding a valid URL. |
Clear content is not protected. Anyone with a valid signed URL can watch and download it until the URL expires. Only use clear delivery for content you are comfortable distributing openly, such as trailers and teasers.
To get a playable URL after processing finishes, either
request a signed URL from the API
or sign it in your own backend. Clear content does not
use generatePlaybackToken or the Fastevo Player.
Enabling Clear Delivery for a Project
Clear delivery is disabled by default and must be enabled per project.
In the Dashboard, open Project Settings → General → Unprotected content, enable Allow unprotected content creation, and save the change. When this setting is disabled, clear mode is not offered for new uploads or imports, and the overflow menu omits Create unprotected version when no linked clear variant exists. Existing clear contents and useful linked-variant actions remain available.
| Method | Endpoint | Description |
|---|---|---|
GET | https://api.fastevo.net/api/v1/projects/mediaProtection/configurations/general/clearDelivery | Returns the current configuration. |
PUT | https://api.fastevo.net/api/v1/projects/mediaProtection/configurations/general/clearDelivery | Updates the configuration. |
Request Body (PUT)
{
"isCreationEnabled": true,
"maxTokenTtlSeconds": 21600
}| Field | Type | Description |
|---|---|---|
isCreationEnabled | boolean | Gates the creation of new clear contents. Disabling it later does not interrupt the serving of existing clear contents; it only blocks new creations. |
maxTokenTtlSeconds | number | Maximum lifetime of a delivery URL, in seconds. 60 to 604800 (7 days). Default 21600 (6 hours). |
Clear delivery requires the project to use the tenant deployment type (the default for new projects). The endpoint returns 409 otherwise.
Creating Clear Content
Add protectionMode: "clear" to the regular creation call — upload, multipart upload and URL import all support it:
POST https://api.fastevo.net/api/v1/projects/mediaProtection/contents/upload
Authorization: Bearer <token>
Content-Type: application/json
{
"title": "Trailer - My Video",
"protectionMode": "clear",
"multipartUpload": true
}Rules:
- Omitting
protectionMode(or sending"drm") keeps the default protected behavior — existing integrations continue to work unchanged. - Sending
"clear"when the project gate is disabled returns403. protectionModeis immutable: it cannot be changed on update or reprocess. To reuse a retained original, create an independent protected or non-protected variant.- Image files are rejected for clear contents during processing.
Reading Clear Content
Content Object
The public content object of a clear content includes two additional fields:
{
"_id": "6a0000000000000000000001",
"title": "Trailer - My Video",
"status": "ready",
"protectionMode": "clear",
"delivery": {
"activeGenerationId": "gen-6a0000000000000000000002",
"outputContentType": "video",
"sources": [
{ "format": "hls", "mimeType": "application/vnd.apple.mpegurl", "relativePath": "master.m3u8" },
{ "format": "dash", "mimeType": "application/dash+xml", "relativePath": "master.mpd" },
{ "format": "progressive", "mimeType": "video/mp4", "relativePath": "progressive/main.mp4" }
]
}
}| Field | Description |
|---|---|
delivery.activeGenerationId | The identifier of the current packaging generation. It changes on every reprocess, which automatically invalidates previously signed URLs. |
delivery.outputContentType | "video" or "audio". Needed to build delivery URLs. |
delivery.sources[] | Every available format with its relativePath — everything your backend needs to sign URLs offline. progressive is included only when the active generation has a progressive rendition; its MIME type is video/mp4 for video or audio/mp4 for audio. Use the returned path instead of assuming a filename. |
Content list items include protectionMode as well, so you can distinguish clear contents without fetching each one.
Webhook
The ready webhook notification for a clear content additively includes:
{
"protectionMode": "clear",
"clearDeliveryGenerationId": "gen-6a0000000000000000000002",
"delivery": {
"generationId": "gen-6a0000000000000000000002",
"outputContentType": "video",
"sources": [
{ "format": "hls", "mimeType": "application/vnd.apple.mpegurl", "relativePath": "master.m3u8" },
{ "format": "dash", "mimeType": "application/dash+xml", "relativePath": "master.mpd" },
{ "format": "progressive", "mimeType": "video/mp4", "relativePath": "progressive/main.mp4" }
]
}
}After a reprocess, a new ready notification carries the new generationId: regenerate your URLs when you receive it.
A reprocess is not a seamless swap. The previous output is removed before the new one is
uploaded, so between those two moments the old URLs stop resolving, and a viewer already
playing will see the stream fail. The generationId in the path versions the output — it
does not keep the previous generation alive alongside the new one.
Plan reprocesses for a maintenance window if the content is being watched, and treat any
ready notification as "the previous URLs are already gone".
Delivery URLs
Clear content is served from your project's custom subdomain with the authorization token embedded in the URL path:
https://{yourSubdomain}/clear/v1/{contentId}/{outputContentType}/{generationId}/s/{expires}/{signature}/{relativePath}| Segment | Description |
|---|---|
contentId | The Fastevo content ID (24 hex chars). |
outputContentType | video or audio (from delivery.outputContentType). |
generationId | The active generation (from delivery.activeGenerationId). |
expires | Unix timestamp (seconds) when the URL stops working. Bounded by maxTokenTtlSeconds. |
signature | HMAC-SHA256 signature, lowercase hex (see below). |
relativePath | Any relativePath from delivery.sources[] — e.g. master.m3u8. |
Because the token lives in the path, HLS and DASH manifests — which reference their segments with relative URLs — inherit the authorization automatically. One signed URL for master.m3u8 covers every playlist, segment and byte-range request underneath it. No player configuration, request interception or query-string forwarding is needed. This works out of the box with hls.js, Shaka, dash.js, Safari/iOS native HLS, and plain <video src> for the progressive MP4.
A signature authorizes the entire generation of that content: the same expires/signature pair is valid for any relativePath of that generation. Sign once per playback session, not once per file.
Signing URLs Yourself
Your backend can generate delivery URLs offline using the project's Token Signing Key (the same secret used for playback tokens and EngageKit signing — available in the panel under API settings).
The Token Signing Key is not the project API key. Keep the requested lifetime
within the project's maxTokenTtlSeconds; the CDN rejects an expiration beyond
that limit.
The string to sign is:
fastevo-clear-delivery-v1\n{contentId}\n{outputContentType}\n{generationId}\n{expires}The signature is HMAC-SHA256(tokenSigningKeySecret, stringToSign), hex-encoded, lowercase.
Sign server-side only. Never ship the Token Signing Key to a browser or mobile app.
Node.js
const crypto = require("crypto");
function buildClearDeliveryUrl({ subdomain, contentId, outputContentType, generationId, ttlSeconds, tokenSigningKeySecret, relativePath }) {
const expires = Math.floor(Date.now() / 1000) + ttlSeconds;
const stringToSign = ["fastevo-clear-delivery-v1", contentId, outputContentType, generationId, String(expires)].join("\n");
const signature = crypto.createHmac("sha256", tokenSigningKeySecret).update(stringToSign).digest("hex");
return `https://${subdomain}/clear/v1/${contentId}/${outputContentType}/${generationId}/s/${expires}/${signature}/${relativePath}`;
}PHP
function buildClearDeliveryUrl(
string $subdomain,
string $contentId,
string $outputContentType,
string $generationId,
int $ttlSeconds,
string $tokenSigningKeySecret,
string $relativePath
): string {
$expires = time() + $ttlSeconds;
$stringToSign = implode("\n", [
"fastevo-clear-delivery-v1",
$contentId,
$outputContentType,
$generationId,
(string) $expires,
]);
$signature = hash_hmac("sha256", $stringToSign, $tokenSigningKeySecret);
return "https://{$subdomain}/clear/v1/{$contentId}/{$outputContentType}/{$generationId}/s/{$expires}/{$signature}/{$relativePath}";
}Test Vector
Use this vector to validate your implementation:
| Input | Value |
|---|---|
| Key | test-secret-key |
contentId | 6a0000000000000000000001 |
outputContentType | video |
generationId | gen-6a0000000000000000000002 |
expires | 1800000000 |
| Expected signature | cfa2390049a1f3f15f7a5ef538a9a442105ae3becfbfbeb15340cf8b94337d6a |
Requesting Signed URLs from the API
This is the simplest option if you do not want to implement signing. Once the
content has protectionMode: "clear" and status: "ready", make this request
from your backend using an active API key for the same project. You do not need
to obtain or use the Token Signing Key for this option:
POST https://api.fastevo.net/api/v1/projects/mediaProtection/contents/{contentId}/generateDeliveryAuthorization
Authorization: Bearer <PROJECT_API_KEY>
Content-Type: application/json
{ "expiresIn": "2h" }Response (201)
{
"protectionMode": "clear",
"generationId": "gen-6a0000000000000000000002",
"expiresAt": 1800000000,
"sources": [
{ "format": "hls", "mimeType": "application/vnd.apple.mpegurl", "url": "https://..../master.m3u8" },
{ "format": "dash", "mimeType": "application/dash+xml", "url": "https://..../master.mpd" },
{ "format": "progressive", "mimeType": "video/mp4", "url": "https://..../progressive/main.mp4" }
]
}The effective TTL is min(expiresIn, maxTokenTtlSeconds). The endpoint returns 409 when the content is not clear, not ready, or has no active generation yet.
Choose the desired entry in sources by its format and pass its url to your
own player. The URL is already signed: the player does not send your API key
and does not need a playback token. The progressive entry is returned only
when that rendition is available.
Lifecycle Notes
- Reprocessing produces a new generation. Old URLs stop resolving as soon as the new generation is active — regenerate URLs when the
readywebhook arrives. - Trash: sending a clear content to the trash starts file quarantine and CDN cache invalidation. Existing URLs stop working once those operations complete, even before they expire; do not treat the delete response as instantaneous revocation of cached files. Restoring the content makes the same generation servable again.
- Long videos: sign with a TTL that covers the full expected playback duration — segment requests made after
expiresare rejected with403.
Error Reference
| Status | Where | Meaning |
|---|---|---|
403 (CDN) | Delivery URL | Missing/invalid/expired signature, malformed path, or clear delivery not enabled for the tenant. |
403 (API) | Content creation | protectionMode: "clear" sent while the project gate is disabled. |
409 (API) | generateDeliveryAuthorization | Content is not clear, not ready, or has no active generation. |
409 (API) | generatePlaybackToken | Content is clear — playback tokens do not apply; use delivery URLs instead. |
409 (API) | Configuration | Project does not use the tenant deployment type. |