On this page
To play an episode, your backend asks Wandura for a playback ticket with POST /api/delivery/v1/playback, and the viewer's Player SDK uses that ticket to load the encrypted stream. This page explains how to request a ticket, what it contains, how long it lasts and what to do when it expires.
When to request a ticket
Request one ticket per play start: when the viewer taps an episode, or when your app loads the next episode ahead of time. Do not request tickets in bulk and store them, because each one expires 30 minutes after it is issued.
Each request covers exactly one episode. There is no batch endpoint.
Wandura checks your licence on every request: your organisation must be active, and your streaming licence for the film must be active and inside its window. A catalog entry does not guarantee a ticket; the ticket request is the final check.
Identify the episode
You can point at the episode in either of two ways:
| Option | Fields | Example |
|---|---|---|
| By episode ID | episode_id |
"episode_id": "b2a4d6e8-1c3f-4a5b-8d7e-9f0a1b2c3d4e" |
| By film and number | film_ref (film ID or slug) and episode_no (a JSON number, starting at 1) |
"film_ref": "the-heiress-returns", "episode_no": 3 |
If you send both, episode_id is used. An unknown film, an unknown episode and an episode that is not ready all return the same 404 with error_code not_ready.
Send the device values
Licensed episodes are encrypted, and only the Player SDK can decrypt them. A standard HLS player loads the playlist and then stalls, without an error that explains why. Before each ticket request, the app on the viewer's device asks the Player SDK for two values and passes them to your backend:
| Field | What it is |
|---|---|
union_info |
The device ID generated by the Player SDK. |
drm_kek |
The device public key generated by the Player SDK. The matching private key never leaves the device. |
The Player SDK guide on partner.wandura.ai → Developers → Player SDK names the exact SDK calls; it is available after onboarding to members with the Developer or Admin role. Wandura registers your app IDs and issues the Player SDK licence; your organisation pays for it.
Rules for the device values:
- Send both or neither. Sending only one returns
400invalid_request. - Send the real SDK output. A value is rejected with
400invalid_device_infowhen, after trimming spaces, it is shorter than 8 characters or is one of(null),null,nil,undefined,nan,<null>ornone(in any letter case). The response lists the bad fields infields. This usually means an SDK call returned nothing and your code turned it into a string. - Sending neither for an encrypted episode returns
409sdk_required.
What the ticket contains
| Field | Description |
|---|---|
vid |
The video ID to load in the Player SDK. |
play_auth_token |
The play token to hand to the Player SDK. Treat it as a secret. |
expires_at |
When the ticket expires, as an ISO 8601 UTC timestamp. |
protection |
The protection level of what you received. private-encrypted for licensed episodes. |
subtitles |
Every subtitle attached to the episode. Can be an empty array. |
On the device, set the video ID and the play token on the Player SDK and start playback. The SDK fetches the playlist and the keys referenced in it by itself; your code does not handle keys.
Check protection
Read protection on every response. Only offer paid episodes when it is private-encrypted. If you ever receive a different value, do not sell the episode and contact Wandura.
Expiry
A ticket is valid for 30 minutes from the moment it is issued. expires_at gives the exact deadline.
When the Player SDK reports an error during playback, including an expired ticket:
- Request one fresh ticket from your backend and reload the player at the current position.
- If that request fails, read
error_codefrom the API response to decide what to tell the viewer. See Errors.
Do not branch on the Player SDK's own error codes to detect expiry; they vary between SDK versions. Retry once, not in a loop.
Plays, prefetch and intent
Each successful ticket request is recorded on Wandura's side and counts as a play, unless it is marked as a prefetch. If your app loads episodes before the viewer taps them, tell Wandura with the optional intent field:
| Value | Meaning |
|---|---|
play |
The viewer just tapped the episode. |
prefetch |
Your app is loading the episode ahead of a tap. Not counted as a play. |
intent never changes the response. A missing or misspelled value is recorded as unknown, not rejected, and the request counts as a play: only prefetch is left out of the play count. Prefetch requests still count toward your rate limit.
viewer_ref
viewer_ref is an optional, opaque string that Wandura stores with the ticket for support lookups. Use your own session or device identifier, such as <user-id>:<device-id>. Do not put personal data, such as email addresses or names, in it.
Subtitles
subtitles has one entry per subtitle attached to the episode:
| Field | Description |
|---|---|
lang |
Language code as delivered by the studio, for example en, vi or pt-BR. |
url |
Where to download the subtitle file. |
format |
Always webvtt. |
- An empty array means no subtitles are attached yet. It is not an error.
- Subtitle URLs are not tied to the ticket's expiry, so you can keep using them after the ticket expires. When Wandura replaces a subtitle file, it gets a new URL and the old one stops working: if a stored URL fails, take the current one from a fresh ticket.
- Native players can take the URL directly. In a web player, do not add the
crossoriginattribute to the video element to load subtitles as a<track>: that changes how the video itself is requested and can stop it from playing. Download the WebVTT file withfetchand add the cues withvideo.addTextTrack()instead.
Related
Updated 1 Oct 2026 · Change history (3)