Quickstart: Stream in under 5 minutes
Learn how to generate a secure stream key, ingest video from OBS or SRT, embed the sub-second WebRTC viewer, and connect to the real-time moderated chat system.
Ultra-low latency SFU delivery for interactive audiences, with automated AAC-to-Opus audio transcoding.
JWT validation via JWKS, Redis token-binding against IP and browser fingerprints, plus stream-wide bans.
Lossless FFmpeg stream capture auto-uploaded to Amazon S3 or Google Drive with tenant isolation.
1Provision a Stream & Stream Key
Create a new live stream instance using your tenant admin API key. The response returns your stream ID, ingest URLs, and private Stream Key.
Provision Live StreamAuth
Provision a live stream channel with transcoding and recording options.
Payload Example
{
"title": "Townhall Q3 2026",
"auto_record": true,
"geo_block_regions": ["KP", "IR"],
"chat_enabled": true
}Status Codes
- Name
201- Type
- HTTP
- Description
- Stream channel provisioned successfully
- Name
401- Type
- HTTP
- Description
- Unauthorized
2Configure OBS or Ingest Encoders
Open OBS Studio (or your hardware encoder like ATEM Mini, vMix, or Teradek) and configure the streaming destination:
Recommended Encoder Output Settings:
- Video Codec: H.264 (AVC) — Baseline or Main profile
- Keyframe Interval: 2 seconds (strictly enforced for sub-second WebRTC & HLS chunking)
- Audio Codec: AAC (Transcoded in real-time to Opus for WebRTC viewers)
- Rate Control: CBR (Constant Bitrate) with 2500–6000 kbps
3Embed Low-Latency WebRTC Viewer
Viewers authenticate using a Zero-Trust JWT signed by your tenant IDP (https://id.vyntech.com.au). The client exchanges an SDP Offer with the Vyntech WebRTC SFU to begin playback in under 350ms:
import { useEffect, useRef } from "react";
export function StreamPlayer({ streamId, viewerJwt, fingerprint }) {
const videoRef = useRef(null);
useEffect(() => {
async function initWebRTC() {
const pc = new RTCPeerConnection({
iceServers: [{ urls: "stun:stun.l.google.com:19302" }]
});
pc.ontrack = (event) => {
if (videoRef.current) {
videoRef.current.srcObject = event.streams[0];
}
};
pc.addTransceiver("video", { direction: "recvonly" });
pc.addTransceiver("audio", { direction: "recvonly" });
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
const response = await fetch(`https://stream.apis.vyntech.com.au/api/webrtc/${streamId}/play`, {
method: "POST",
headers: {
"Content-Type": "application/sdp",
"Authorization": `Bearer ${viewerJwt}`,
"X-Device-Fingerprint": fingerprint,
},
body: offer.sdp,
});
const answerSdp = await response.text();
await pc.setRemoteDescription({ type: "answer", sdp: answerSdp });
}
initWebRTC();
}, [streamId, viewerJwt, fingerprint]);
return <video ref={videoRef} autoPlay playsInline controls className="w-full rounded-xl bg-black" />;
}4Connect to Real-Time Moderated Chat
Connect to the distributed chat gateway over secure WebSockets (wss://). The server enforces token binding, rate limits, duplicate filters, and AI toxicity screening:
const socket = new WebSocket(
`wss://stream.apis.vyntech.com.au/ws/chat?stream_id=${streamId}&token=${viewerJwt}&fingerprint=${fingerprint}`
);
socket.onmessage = (event) => {
const message = JSON.parse(event.data);
switch (message.type) {
case "chat_message":
console.log(`[${message.author}]: ${message.text}`);
break;
case "message_deleted":
console.log(`Message ${message.message_id} removed by moderator`);
break;
case "stream_banned":
alert("You have been banned from this live stream.");
break;
}
};
// Send message
function sendMessage(text) {
socket.send(JSON.stringify({
action: "send_message",
text: text
}));
}