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.
- Your backend calls
POST /api/v1/videos/:id/playback-tokenwith your API key. Your API key never reaches the app. - vdoX responds with
{ token, expiresAt, manifestUrl }, an absolute, already-tokenized HLS manifest URL. - Your backend hands
tokenandmanifestUrlto the app, over whatever channel it already uses to talk to your backend. - The app either loads a hosted embed URL in a WebView (Option A) or hands
manifestUrlstraight 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:
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)
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)
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)
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)
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)
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)
// 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 = playerReact Native (react-native-video)
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)
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.