Mobile

Play vdoX videos in any mobile app, React Native, Flutter, Swift, or Kotlin, in a few lines, no per-platform SDK binary to install.

How it works

Both options below start the same way: your own backend, never the app itself, holds your vdoX API key.

  1. Your backend calls POST /api/v1/videos/:id/playback-token with your API key. Your API key never reaches the app.
  2. vdoX responds with { token, expiresAt, manifestUrl }, an absolute, already-tokenized HLS manifest URL.
  3. Your backend hands token and manifestUrl to the app, over whatever channel it already uses to talk to your backend.
  4. The app either loads a hosted embed URL in a WebView (Option A) or hands manifestUrl straight to a native HLS player (Option B).

See Authentication and API reference for the full request and response shapes.

Option A: WebView embed (fastest)

Load this URL in a plain WebView, no player library to add, no hls.js to wire up yourself:

shellURL pattern
https://YOUR_VDOX_APP/embed/{videoId}?token={token}

Fill in {videoId} and {token} from your backend's playback-token response, and replace YOUR_VDOX_APP with your vdoX app's real host. The page is a full-viewport player and nothing else, it is built to fill a WebView. Every platform below needs one setting enabled: inline (not fullscreen-only) playback without a user gesture first.

React Native (react-native-webview)

codeVdoxPlayer.tsx
import { WebView } from "react-native-webview";

export function VdoxPlayer({ videoId, token }: { videoId: string; token: string }) {
  const uri = `https://YOUR_VDOX_APP/embed/${videoId}?token=${token}`;
  return (
    <WebView
      source={{ uri }}
      allowsInlineMediaPlayback
      mediaPlaybackRequiresUserAction={false}
      style={{ flex: 1 }}
    />
  );
}

Flutter (webview_flutter)

codevdox_player.dart
import 'package:webview_flutter/webview_flutter.dart';

final controller = WebViewController()
  ..setJavaScriptMode(JavaScriptMode.unrestricted)
  ..loadRequest(Uri.parse(
    'https://YOUR_VDOX_APP/embed/$videoId?token=$token',
  ));

// In build(): WebViewWidget(controller: controller)

SwiftUI (WKWebView)

codeVdoxPlayerView.swift
import SwiftUI
import WebKit

struct VdoxPlayerView: UIViewRepresentable {
  let videoId: String
  let token: String
  func makeUIView(context: Context) -> WKWebView {
    let config = WKWebViewConfiguration()
    config.allowsInlineMediaPlayback = true
    config.mediaTypesRequiringUserActionForPlayback = []
    return WKWebView(frame: .zero, configuration: config)
  }
  func updateUIView(_ webView: WKWebView, context: Context) {
    webView.load(URLRequest(url: URL(string: "https://YOUR_VDOX_APP/embed/\(videoId)?token=\(token)")!))
  }
}

Android (WebView)

codeVdoxPlayer.kt
import android.webkit.WebView

val webView = WebView(context).apply {
  settings.javaScriptEnabled = true
  settings.mediaPlaybackRequiresUserGesture = false
  loadUrl("https://YOUR_VDOX_APP/embed/$videoId?token=$token")
}
setContentView(webView)

Option B: native HLS playback (full control)

Prefer your own player UI instead of a WebView? Every native HLS player on every platform can consume manifestUrl directly, it's a standard HLS master playlist. AES-128 is standard HLS too: the player fetches the decryption key automatically, the key URL inside the manifest already carries your token, there is no custom key-delivery code to write.

Swift (AVPlayer)

codePlayerViewController.swift
import AVKit

let url = URL(string: manifestUrl)!
let player = AVPlayer(url: url)
let controller = AVPlayerViewController()
controller.player = player
present(controller, animated: true) {
  player.play()
}

Kotlin (Media3 ExoPlayer)

codeVdoxPlayer.kt
// build.gradle: HLS support is a separate artifact.
// implementation("androidx.media3:media3-exoplayer:1.4.1")
// implementation("androidx.media3:media3-exoplayer-hls:1.4.1")
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.common.MediaItem

val player = ExoPlayer.Builder(context).build()
player.setMediaItem(MediaItem.fromUri(manifestUrl))
player.prepare()
player.play()
playerView.player = player

React Native (react-native-video)

codeVdoxPlayer.tsx
import Video from "react-native-video";

export function VdoxPlayer({ manifestUrl }: { manifestUrl: string }) {
  return (
    <Video
      source={{ uri: manifestUrl }}
      controls
      resizeMode="contain"
      style={{ flex: 1 }}
    />
  );
}

Flutter (video_player)

codevdox_player.dart
import 'package:video_player/video_player.dart';

final controller = VideoPlayerController.networkUrl(
  Uri.parse(manifestUrl),
);
await controller.initialize();
controller.play();

// In build(): VideoPlayer(controller)

Token lifetime

Playback tokens are short-lived (5 minutes by default), the same TTL used everywhere else in the API. Mint one per view, right before the app needs to play: don't mint ahead of time or share one token across viewers. For a session longer than the TTL, mint again and swap the player's source (a new WebView URL, or a new manifestUrl) instead of trying to extend the old one.

Hardening your app

The integration above gets a video playing. Locking down a native app against screen recording, credential theft, and casual sharing is a separate, app-side concern: see Mobile security for a full checklist (secure token storage, certificate pinning, root/jailbreak and capture detection, download protection, and how to report capture attempts to vdoX), with concrete Android/Kotlin and iOS/Swift API names.