# Embed the player

> Build a Wolvy embed URL, the iframe attributes DRM needs, responsive sizing, how the domain allowlist reads the Referer, and App-Only playback on mobile.

Source: https://docs.wolvy.net/embedding/ · Wolvy Developer API v1 · updated 2026-11-12

_Guide · the iframe_

One iframe puts the Wolvy player — with DRM, your theme and the watermark — on any page. This guide covers the URL, the attributes DRM needs, how domain rules read the Referer, and what to do when a player stays blank.

Host `embed.wolvy.stream` · Endpoints `GET /v1/videos/{id}/embed` · `POST …/playback-tokens`

## The embed URL

`https://embed.wolvy.stream/`player host `a1b2c3d4e5f60718293a`video id `/` `NDI=`base64(account id) `?vt=eyJ2aWQiOi…`signed viewer token

- **Video id** — the `id` from the video object.
- **Account id** — the number from [`GET /v1/account`](https://docs.wolvy.net/api/#get-account), base64-encoded as text: `42` → `NDI=`.
- **`vt`** — optional unless enforcement is on, and always per viewer. [How to sign one](https://docs.wolvy.net/viewer-tokens/).

You rarely build it by hand: every video object carries `embed_url`, [`GET …/embed`](https://docs.wolvy.net/api/#get-embed) returns it with an iframe snippet, and [`POST …/playback-tokens`](https://docs.wolvy.net/api/#create-playback-token) returns it already signed.

> **Note — There are no player parameters**
> Autoplay, controls, colours and quality are account settings, applied to every embed — not URL parameters. See [player settings](https://docs.wolvy.net/embedding/#player-settings).

## Embed builder

Fill in your ids to get a URL and markup you can paste. Signed URLs belong to one viewer, so in production generate them on your server at render time.

_[Interactive: Embed Builder — runs on https://docs.wolvy.net/embedding/]_

## The iframe

_Recommended markup_

```html
<div class="wolvy-player">
  <iframe
    src="https://embed.wolvy.stream/a1b2c3d4e5f60718293a/NDI=?vt=…"
    title="Onboarding — week one"
    allow="autoplay; fullscreen; encrypted-media; picture-in-picture"
    allowfullscreen
    referrerpolicy="strict-origin-when-cross-origin"
    loading="lazy"
  ></iframe>
</div>
```

| Attribute | Why it is there |
| --- | --- |
| `allow="… encrypted-media …"` | Lets the browser’s DRM module start inside a cross-origin frame. Without it, protected video cannot play. |
| `allow="autoplay; fullscreen; picture-in-picture"` + `allowfullscreen` | Grants the player the features its controls offer. |
| `referrerpolicy="strict-origin-when-cross-origin"` | Guarantees the Referer that [domain rules](https://docs.wolvy.net/embedding/#domains) check, even if the page — or a security plugin — sets `Referrer-Policy: no-referrer`. |
| `title` | The accessible name screen readers announce for the frame. |
| `loading="lazy"` | Optional. Defers players below the fold — worthwhile when a page has several. |
| **No** `sandbox` | A sandbox without `allow-same-origin` blocks DRM outright, and one loose enough for DRM protects nothing. The iframe is already isolated by origin. |

## Responsive sizing

Give the wrapper the video’s shape and let the iframe fill it. The player letterboxes anything that does not match.

_CSS_

```css
.wolvy-player {
  position: relative;
  aspect-ratio: 16 / 9;      /* 9 / 16 for vertical video */
  max-width: 960px;
  background: #0b0616;
}
.wolvy-player iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
```

## Domain rules & Referer

The embed host checks _which page_ is embedding a video before it serves the player. The rules live in Settings → Protection → Allowed domains and apply to every video on the account.

1. **A Referer must arrive**No Referer (or Origin) header — for example the URL opened directly, or a page with `Referrer-Policy: no-referrer` — is refused with a 403 page.
2. **Blocked domains are checked first**A match is refused, whatever the allowed list says.
3. **An empty allowed list lets every site embed**Once you add one domain, only listed domains work — plus `wolvy.net`, and `play.wolvy.stream` when Direct Play is on.
4. **Matching is by hostname**`academy.example.com` matches only itself. `*.example.com` matches every subdomain but **not** `example.com` — list both if you use both.

> **Careful — Rules are hostnames, not URLs**
> Enter `academy.example.com`, not `https://academy.example.com/`. A scheme, path or port means the rule never matches. Changes apply from the next playback.

## Domain tester

This replays the embed host’s check with your rules. The defaults show the most common surprise: a wildcard that does not cover the bare domain.

_[Interactive: Domain Tester — runs on https://docs.wolvy.net/embedding/]_

## Blank player or 403?

Embeds fail quietly — a redirect to an error page inside the frame. It is almost always one of these:

| Symptom | Likely cause | Fix |
| --- | --- | --- |
| 403 when you open the embed URL in the address bar | No Referer — the check working as designed | Test inside an iframe on an allowed page |
| 403 on your site, fine on another | Your hostname is not on the allowed list, or a wildcard without the bare domain | Add the exact hostname; try the [domain tester](https://docs.wolvy.net/embedding/#domain-tester) |
| 403 on every embed after installing a security plugin | `Referrer-Policy: no-referrer` on the page | Add `referrerpolicy="strict-origin-when-cross-origin"` to the iframe |
| 403 only for some pages or viewers | Enforcement on, and the token is missing, expired or signed with an old secret | Sign every embed; [check the stored secret](https://docs.wolvy.net/viewer-tokens/#test-vectors) |
| Player shows “No supported content protection system” | The iframe is sandboxed or lacks `encrypted-media` | Use the [recommended markup](https://docs.wolvy.net/embedding/#iframe) |
| 400 page | Malformed URL — wrong path shape, or the account id not base64 | Rebuild it from `embed_url` |
| A “switch browser” or “open the app” screen | App-Only mode, on a device without hardware DRM | Expected — see [App-Only mode](https://docs.wolvy.net/embedding/#app-only) |

## Player settings

How the player looks and behaves is set once per account in the dashboard — Settings → Player, Subtitles, Watermark and Protection — and applies to every embed. Nothing is configured per iframe, so an embed can never contradict the account.

| Setting | Where | Read it via the API |
| --- | --- | --- |
| Controls, theme, accent colour | Settings → Player | `theme`, `colors` |
| Resolutions generated | Settings → Player | `available_resolutions` |
| Caption font, colours, size | Settings → Subtitles | `subtitles` |
| Watermark and enforcement | Settings → Watermark | `watermark` |
| Domains and App-Only mode | Settings → Protection | `allowed_domains`, `blocked_domains`, `app_only_l3` |

All of it is readable in one call: [`GET /v1/settings/player`](https://docs.wolvy.net/api/#get-player-settings). The API cannot change these — a key that could repaint every embed is a risk it deliberately does not carry.

To drive playback from your own page — play, pause, seek, speed, and a “watched 90%” signal for course completion — switch on the [Player API](https://docs.wolvy.net/player-api/).

## App-Only mode & mobile apps

Some browsers only have _software_ DRM, which is easier to attack. With **App-Only mode** on (Settings → Protection → App-Only Mode, Multi-DRM plans), those viewers are not played in the browser — they are steered to a client with hardware-backed protection:

| Viewer is on | With App-Only mode on |
| --- | --- |
| Android — any browser | Offered to open the video in the [Wolvy Player app](https://wolvy.net/player-app/), or install it from Google Play |
| Android — Wolvy Player app | Plays, with screen recording blocked |
| Windows — Edge with hardware DRM | Plays in the browser |
| Windows — other browsers | Asked to switch to Microsoft Edge |
| macOS — Safari | Plays in the browser |
| macOS — other browsers | Asked to switch to Safari |
| iPhone or iPad — Safari | Plays in the browser |
| iPhone or iPad — other browsers | Asked to open Safari |
| Linux, and anything else without hardware DRM | Shown guidance with a link to the Android app |

### From your own Android app

The playback-token response includes `deep_link` — `wolvy://play/{video_id}/{base64 account id}/{vt}`. Open it and the Wolvy Player app takes over, re-verifying the signed token on Wolvy’s side. Fetch the link from your backend; never mint tokens inside the app.

```kotlin
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri

// deepLink comes from your backend: POST /v1/videos/{id}/playback-tokens → deep_link
fun openInWolvyPlayer(context: Context, deepLink: String) {
    try {
        context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(deepLink)))
    } catch (e: ActivityNotFoundException) {
        // Wolvy Player is not installed — send the viewer to Google Play.
        context.startActivity(
            Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.wolvy.player"))
        )
    }
}
```

_Also on the page in React Native._

## Common questions

### Why is my Wolvy player blank or refusing to load?

Nine times out of ten it is the domain rules: the page’s `Referer` has to match an allowed domain, and the iframe must not strip it. Work through [Blank player or 403?](https://docs.wolvy.net/embedding/#troubleshooting) — it lists what each symptom means.

### Which iframe attributes does DRM playback need?

`allow="encrypted-media"` is the one that matters — without it the browser’s DRM module cannot start inside a cross-origin frame. Add `autoplay; fullscreen; picture-in-picture` plus `allowfullscreen` for the controls, and `referrerpolicy="strict-origin-when-cross-origin"` so the domain check still sees a Referer. Never add `sandbox`.

### Does the domain allowlist cover subdomains?

Only when you add the wildcard. `example.com` matches that host alone; `*.example.com` matches `app.example.com` and any other subdomain but not `example.com` itself, so add both when you need both.

### Can I show Wolvy video inside my own mobile app?

Yes. A playback token response includes `deep_link` — `wolvy://play/{video_id}/{base64 account id}/{vt}` — which hands the viewer to the Wolvy Player app, where screen recording is blocked. Fetch the link from your backend; never mint tokens inside the app.

### Do I need a viewer token to embed a video?

Not to make it play, unless [enforcement](https://docs.wolvy.net/viewer-tokens/#enforcement) is on. But without `?vt=` the watermark has no name on it and the session logs show no viewer, so add one on every page that shows a player.

Something wrong or unclear on this page? Email [contact@wolvy.net](mailto:contact@wolvy.net?subject=Docs%3A%20Embed%20the%20player) — include the page name.
