Skip to main content

How playback works

Understand the two-step Videas playback flow — the player token, signed stream URLs, and the security model — before you embed a player.

To play a Videas asset in your own page you don’t stream from a fixed URL. Instead you go through a short two-step flow that keeps your API key secret and hands the browser only short-lived, signed URLs. This page explains the model; the next two show the actual code (JavaScript SDK and REST API).

You may not need any of this. If all you want is the video on a page, every Videas media gives you an embed code — a script tag or an iframe — to paste as-is, player, chapters and subtitles included. This guide is for the case where you drive playback yourself: your own player, a mobile app, a server-rendered page.

The two steps

  1. Mint a player token — call GET /videos/{uid}/player/ (or /assets/{uid}/player/). This returns the player metadata (config, theme, overlays, items) and a playback_token valid for 4 hours.
  2. Resolve the stream URLs — call POST /playback/info/ with the playback_token and the asset_id. This returns the signed, time-limited media URLs (hls_url, …) you feed into a video player.

The split exists so the token — safe to expose to a browser — is separated from your secret key, and so the actual media URLs are signed and expire quickly.

The security model (read this first)

The two endpoints authenticate differently, and this is the single most important thing to get right:

Step Endpoint Auth Where to call it
1. Mint token GET …/player/ API key (Authorization: Bearer sk_…) Server only
2. Resolve URLs POST …/playback/info/ playback_token (no API key) Browser is fine

Golden rule. Your sk_ key is a secret — never ship it to a browser or a mobile app. Step 1 must run on your server. The playback_token it returns is designed to be handed to the browser, which does step 2 by itself.

┌─────────────┐  1. getPlayer(uid)   [sk_ key]     ┌──────────────┐
│ Your server │ ──────────────────────────────────▶│  Videas API  │
│  (sk_ key)  │◀────────────────────────────────── │              │
└─────────────┘   playback_token (valid 4h)         └──────────────┘

      │  2. hand playback_token + asset_id to the page

┌─────────────┐  3. POST /playback/info/  [token]  ┌──────────────┐
│   Browser   │ ──────────────────────────────────▶│  Videas API  │
│  (player)   │◀────────────────────────────────── │              │
└─────────────┘   signed hls_url (short-lived)      └──────────────┘

      │  4. feed hls_url into hls.js / native HLS

   ▶ playback

Your server mints one token and can reuse it for 4 hours; the browser re-resolves the signed URLs whenever they expire (they live much less than the token). When the token itself expires, mint a new one server-side.

What playback/info/ returns

The response is typed by the asset — the type field tells you which shape you got. Every media URL is a { url, expires_at } object.

Video (type: "video")

Field Meaning
hls_url HLS manifest (.m3u8) — the primary stream (adaptive bitrate, subtitles & audio tracks baked in)
source_url Progressive MP4 — a legacy fallback when HLS is unavailable
preview_sprites_url Sprite sheet for seek-bar thumbnails
chapters [{ title, start_time, thumbnail_url }]

Audio (type: "audio"): source, waveform_url, chapters, subtitle_tracks ([{ label, language, is_default, src }]).

Image (type: "image"): variants ([{ name, dimensions, url }]).

Document (type: "document"): url, thumbnails (per-page).

Third-party (type: "third_party", e.g. YouTube/Vimeo): embed_url, provider, provider_id — embed the provider’s iframe rather than an HLS player.

Fields present on every type

  • expires_at — when this playback info (and its signed URLs) expire; re-resolve after this.
  • resume_position_seconds — the viewer’s saved position, if any. Seek here to resume where they left off (null when there’s nothing to resume).
  • blocked — see below.

Handle the blocked state

When the asset owner’s credit wallet is empty, playback is withheld: the response comes back with blocked: true and no media URL. Always check it before wiring the player, and show a neutral “temporarily unavailable” message rather than a broken player:

{ "type": "video", "blocked": true, "hls_url": null, "expires_at": "…" }

Token & URL lifetimes — a quick recap

  • playback_token → valid 4 hours. Mint it server-side; reuse it.
  • Signed media URLs (hls_url, …) → short-lived (see each expires_at). Re-call POST /playback/info/ to refresh them — no new token needed until the 4-hour token itself expires.

Next steps