The short version
- A video plays only with a playback token.
- Your server creates the token with your API key.
- The token names one video, one verified domain and an expiry.
- The CDN checks the token and the domain on every request.
- A copied link does not play, because the other site’s domain does not match the token.
Part 1: Playback tokens
A playback token is a string that starts withv1.. It is signed with
HMAC-SHA256, so we can check it is real, and it holds:
The signature is the important part. Nobody can change the token’s domain or
expiry without the secret key, so a viewer cannot “unlock” a video or move it to
another site.
Default life is 4 hours. You choose it with
expiresInSeconds (60 to
86400). A shorter life is safer; make it at least as long as the page’s cache.
Part 2: Verified domains
A domain tells us which sites may play a project’s videos. Add it in the dashboard or withPOST /v1/projects/{projectId}/domains, then prove you own it
with a TXT record. After that its status is verified.
- We match the exact host.
courses.example.comandwww.courses.example.comare two different domains. Add both if you use both. - The API refuses a token for a domain that is not verified.
- A free account can verify 1 domain. See Usage and plan limits.
- Remove a domain and we stop issuing tokens for it. Tokens already issued keep working until they expire.
What the CDN checks on every request
Each playback request goes through the same gate, in this order:- Token present? Missing →
403 token_missing. - Signature valid, and for this video? No →
403 token_invalid. - Not expired? Expired →
403 token_expired. - Domain. The request’s
Originheader (or the origin of itsReferer) must be exactlyhttps://+ the token’s domain. Missing →403 origin_missing; different host →403 domain_mismatch. (Our own CDN host is also accepted, for the player’s own requests.) - Not removed, and inside the plan’s quality. Removed →
403 video_unavailable; over the plan quality →403 plan_limit_reached. - Serve. We add the token to every link inside the playlist, so the player keeps working, and we serve the file from the cache under its clean path — the token is not part of the cache key.
Content-Security-Policy: frame-ancestors https://{domain}, so only your domain
can frame it. A copied embed URL on another site is refused by the browser.
Why a copied link does not play
A token is locked to one host. Copy the iframe to another site and:- the embed page cannot be framed there (
frame-ancestorsblocks it); - if someone copies only the video URL, the CDN sees the new site’s
Origin, which is not the token’s domain, and answers403 domain_mismatch; - an expired or missing token answers
403 token_expiredor403 token_missing.
What this does not stop
Protection is about where a video can play, not who can watch it.- It does not lock a token to one viewer. Anyone on your verified domain with the link can watch it until the token expires. Use short tokens, and your own login on top, if you need that.
- It does not stop screen recording.
- It does not stop someone who can publish pages on your verified domain.
hls.js video on the open web. If you need
per-viewer limits, keep the token life short and put playback behind your own
sign-in.
The player’s logo
On the Free and Starter plans the player shows our logo. On Pro and Agency you can white-label it: hide our logo and use your own logo and colour. The plan and branding travel inside the signed token.Where to go next
- Create playback tokens on your server
- Embed a video on any website
- Add segmnt to WordPress
- Accounts, projects, API keys and domains — add and verify a domain.
Tested in TEST
Checked on TEST on 15 Oct 2026:GET https://test-cdn.segmnt.cloud/e/{videoId}with no token →403withX-Segmnt-Error: token_missing.GET https://test-cdn.segmnt.cloud/v/{videoId}/master.m3u8with no token →403.- The token check, the expiry, the
Origin/Refererdomain check, theframe-ancestorspolicy and the playlist rewrite are covered by the playback Worker tests (playback-worker/src/token.test.ts,playback-worker/src/security.test.ts,playback-worker/src/embed.ts).