Complete guide to integrating Pubfuse's live streaming platform into your applications
The Pubfuse SDK allows you to integrate live streaming capabilities into your applications. With our API-first approach, you can build custom streaming experiences while leveraging Pubfuse's robust infrastructure.
Each SDK Client operates in isolation with their own users, sessions, and data. Perfect for white-label solutions.
Industry-standard API key authentication with optional HMAC signature verification for enhanced security.
First, you need to register your application to get API credentials.
/api/admin/sdk-clients
{
"name": "My Streaming App",
"website": "https://myapp.com",
"description": "My awesome streaming application",
"contactName": "John Developer",
"contactTitle": "Lead Developer",
"contactEmail": "[email protected]",
"contactPhone": "+1234567890",
"expectedApps": "1-5",
"useCase": "Live streaming for events",
"expectedUsers": "100-1000",
"agreeTerms": true,
"agreeMarketing": false,
"agreeDataProcessing": true
}
{
"success": true,
"apiKey": "pk_5951C5196A6C47EDA12D41B9A050AC5C",
"secretKey": "sk_8510CDE5A7ED4E749D15E1008FBD7B7E",
"clientId": "550e8400-e29b-41d4-a716-446655440000",
"message": "SDK Client registered successfully"
}
All API requests require authentication using your API key. Include it in the X-API-Key
header.
curl -X GET https://api.pubfuse.com/api/v1/sessions \
-H "X-API-Key: pk_5951C5196A6C47EDA12D41B9A050AC5C" \
-H "Content-Type: application/json"
For enhanced security, use HMAC signature authentication with your secret key.
const crypto = require('crypto');
function generateSignature(method, path, body, timestamp, secretKey) {
const payload = `${method}${path}${body}${timestamp}`;
return crypto.createHmac('sha256', secretKey)
.update(payload)
.digest('hex');
}
const method = 'POST';
const path = '/api/v1/sessions';
const body = JSON.stringify({ title: 'My Stream' });
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = generateSignature(method, path, body, timestamp, secretKey);
fetch('https://api.pubfuse.com/api/v1/sessions', {
method: 'POST',
headers: {
'X-API-Key': apiKey,
'X-Signature': signature,
'X-Timestamp': timestamp,
'Content-Type': 'application/json'
},
body: body
});
/api/v1/sessions
Create a new streaming session
/api/v1/sessions
List all sessions for your SDK client
/api/v1/sessions/{id}
Update session status and metadata
/api/users/signup
Register a new user
✅ WORKING/api/users/login
Authenticate a user
✅ WORKING/api/v1/users
List users for your SDK client
/ws/streams/{id}/chat
Real-time chat connection
/api/v1/sessions/{id}/reactions
Send reactions to a stream
Pubfuse now supports LiveKit SFU streaming for better scalability and performance. LiveKit supports 100+ concurrent participants with built-in recording capabilities.
See our LiveKit Setup Guide for complete integration instructions.
Quick sanity-check tool: /livekit-test (connect → publish → remote subscribe)
// 1. Get streaming provider configuration
const providers = await fetch('/api/streaming/providers').then(r => r.json());
const activeProvider = providers.find(p => p.isConfigured);
// 2. Create streaming session
const session = await fetch('/api/streaming/sessions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: 'My Live Stream',
visibility: 'public',
maxParticipants: 1000
})
}).then(r => r.json());
// 3. Generate LiveKit JWT access token ✅ **UPDATED**
const token = await fetch(`/api/streaming/sessions/${session.id}/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'your_pubfuse_api_key' // ✅ Required
},
body: JSON.stringify({
userId: 'user-123',
role: 'publisher'
})
}).then(r => r.json());
// 4. Connect to LiveKit (if provider is LiveKit)
if (activeProvider.provider.rawValue === 'livekit') {
// Load LiveKit SDK
const script = document.createElement('script');
script.src = 'https://unpkg.com/livekit-client@latest/dist/livekit-client.umd.js';
document.head.appendChild(script);
// Connect to room
const room = new LiveKit.Room();
await room.connect(token.serverUrl, token.token);
}
import Foundation
import LiveKit
class PubfuseStreamingSDK {
private var room: Room?
func connect(to streamId: String) async throws {
// Get LiveKit access token
let tokenResponse = try await getLiveKitToken(streamId: streamId)
// Create LiveKit room
let room = Room()
room.delegate = self
// Connect to LiveKit room
try await room.connect(
url: tokenResponse.serverUrl,
token: tokenResponse.token,
connectOptions: ConnectOptions(
autoManageVideo: true,
autoManageAudio: true
)
)
self.room = room
}
private func getLiveKitToken(streamId: String) async throws -> LiveKitTokenResponse {
guard let url = URL(string: "\(baseURL)/api/streaming/sessions/\(streamId)/token") else {
throw PubfuseError.invalidURL
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("your_pubfuse_api_key", forHTTPHeaderField: "X-API-Key") // ✅ Required
let tokenRequest = LiveKitTokenRequest(
userId: UUID().uuidString,
role: "subscriber"
)
request.httpBody = try JSONEncoder().encode(tokenRequest)
let (data, _) = try await URLSession.shared.data(for: request)
return try JSONDecoder().decode(LiveKitTokenResponse.self, from: data)
}
}
extension PubfuseStreamingSDK: RoomDelegate {
func room(_ room: Room, didConnect isReconnect: Bool) {
print("✅ Connected to LiveKit room")
}
func room(_ room: Room, participant: RemoteParticipant, didSubscribeTo publication: RemoteTrackPublication) {
if let videoTrack = publication.track as? VideoTrack {
videoTrack.addRenderer(videoView)
}
}
}
GET /api/streaming/providers
- Get available streaming providersPOST /api/streaming/sessions
- Create streaming sessionPOST /api/streaming/sessions/{id}/token
- Generate LiveKit JWT access tokenGET /api/streaming/ice-config
- Get ICE server configurationGET /api/livekit/health
- LiveKit server health checkTokens are LiveKit-compatible and include:
nbf
, iat
, exp
as integersroomJoin
, room
, canSubscribe
, optional publish/data grantsX-API-Key
Server returns token prefixed with livekit_
; pass it unchanged to the SDK.
Short IDs (e.g., r5
) are accepted and resolved to UUIDs.
class PubfuseSDK {
constructor(apiKey, secretKey, baseUrl = 'https://api.pubfuse.com') {
this.apiKey = apiKey;
this.secretKey = secretKey;
this.baseUrl = baseUrl;
}
async makeRequest(method, path, data = null) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const body = data ? JSON.stringify(data) : '';
const signature = this.generateSignature(method, path, body, timestamp);
const response = await fetch(`${this.baseUrl}${path}`, {
method,
headers: {
'X-API-Key': this.apiKey,
'X-Signature': signature,
'X-Timestamp': timestamp,
'Content-Type': 'application/json'
},
body: data ? body : undefined
});
return response.json();
}
generateSignature(method, path, body, timestamp) {
const crypto = require('crypto');
const payload = `${method}${path}${body}${timestamp}`;
return crypto.createHmac('sha256', this.secretKey)
.update(payload)
.digest('hex');
}
// Stream Management
async createSession(title, description = '') {
return this.makeRequest('POST', '/api/v1/sessions', {
title,
description
});
}
async getSessions() {
return this.makeRequest('GET', '/api/v1/sessions');
}
// User Management
async registerUser(userData) {
return this.makeRequest('POST', '/api/users/signup', userData);
}
async loginUser(email, password) {
return this.makeRequest('POST', '/api/users/login', {
email,
password
});
}
}
// Usage
const sdk = new PubfuseSDK('pk_your_api_key', 'sk_your_secret_key');
// Create a session
sdk.createSession('My Live Stream', 'Welcome to my stream!')
.then(session => console.log('Session created:', session))
.catch(error => console.error('Error:', error));
import requests
import hmac
import hashlib
import time
from typing import Dict, Any, Optional
class PubfuseSDK:
def __init__(self, api_key: str, secret_key: str, base_url: str = 'https://api.pubfuse.com'):
self.api_key = api_key
self.secret_key = secret_key
self.base_url = base_url
def _generate_signature(self, method: str, path: str, body: str, timestamp: str) -> str:
payload = f"{method}{path}{body}{timestamp}"
return hmac.new(
self.secret_key.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
def _make_request(self, method: str, path: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
timestamp = str(int(time.time()))
body = json.dumps(data) if data else ''
signature = self._generate_signature(method, path, body, timestamp)
headers = {
'X-API-Key': self.api_key,
'X-Signature': signature,
'X-Timestamp': timestamp,
'Content-Type': 'application/json'
}
response = requests.request(
method,
f"{self.base_url}{path}",
headers=headers,
json=data
)
return response.json()
def create_session(self, title: str, description: str = '') -> Dict[str, Any]:
return self._make_request('POST', '/api/v1/sessions', {
'title': title,
'description': description
})
def get_sessions(self) -> Dict[str, Any]:
return self._make_request('GET', '/api/v1/sessions')
def register_user(self, user_data: Dict[str, Any]) -> Dict[str, Any]:
return self._make_request('POST', '/api/users/signup', user_data)
def login_user(self, email: str, password: str) -> Dict[str, Any]:
return self._make_request('POST', '/api/users/login', {
'email': email,
'password': password
})
# Usage
sdk = PubfuseSDK('pk_your_api_key', 'sk_your_secret_key')
# Create a session
try:
session = sdk.create_session('My Live Stream', 'Welcome to my stream!')
print('Session created:', session)
except Exception as e:
print('Error:', e)
Generate a LiveKit token from your server, then pass it unchanged (including the livekit_
prefix) to the client SDK.
# Get LiveKit token for a session (UUID or short ID like r5)
curl -s -X POST "http://127.0.0.1:8080/api/streaming/sessions/SESSION_ID/token" \
-H "Content-Type: application/json" \
-H "X-API-Key: <your_api_key>" \
-d '{"userId":"550e8400-e29b-41d4-a716-446655440000","role":"subscriber"}' | jq .
Minimal LiveKit Web client usage:
// tokenResponse: { token, room, serverUrl }
const room = new LiveKit.Room();
await room.connect(tokenResponse.serverUrl, tokenResponse.token, {
autoManageVideo: true,
autoManageAudio: true
});
room.on(LiveKit.RoomEvent.TrackSubscribed, (track, pub, participant) => {
if (track.kind === 'video') {
const el = document.getElementById('remoteVideo');
track.attach(el);
}
});
This is what the web watch page does at runtime. Mirror this sequence for mobile clients.
r5
).window.LiveKit
is available.GET /api/streaming/providers
and select id === "livekit"
.POST /api/streaming/sessions/{id}/token
with X-API-Key
, role subscriber
, and a UUID userId.livekit_
prefix) to the SDK Room.connect()
.TrackSubscribed
, attach remote video/audio to the player.// Pseudocode that mirrors watch.leaf
const sessionId = getSessionIdFromUrl();
// 1) Provider
const providers = await fetch('/api/streaming/providers').then(r => r.json());
const livekit = providers.find(p => p.id === 'livekit' && p.isConfigured);
// 2) Token (server returns: { token, room, serverUrl, expiresAt })
const tRes = await fetch(`/api/streaming/sessions/${sessionId}/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY },
body: JSON.stringify({ userId: USER_UUID, role: 'subscriber' })
});
const t = await tRes.json();
// 3) Connect
const room = new LiveKit.Room();
await room.connect(t.serverUrl || livekit.configuration.url, t.token);
// 4) Track handling
room.on(LiveKit.RoomEvent.TrackSubscribed, (track, pub, participant) => {
if (track.kind === 'video') track.attach(document.getElementById('remoteVideo'));
});
// 5) Reactions/chat (data)
function sendReaction(kind) {
// send via your server or LiveKit data channel depending on your design
}
livekit_
prefix; integer nbf/iat/exp
; grants
include roomJoin
and room
.Use your backend to mint the token, then connect the SDK on device.
import LiveKit
let room = Room()
// tokenResponse.serverUrl (wss://...), tokenResponse.token (starts with livekit_)
try await room.connect(tokenResponse.serverUrl, tokenResponse.token)
room.on(.trackSubscribed) { track, pub, participant in
if let video = track as? RemoteVideoTrack {
// Attach to your LKVideoView / UIView
}
}
import io.livekit.android.Room
val room = Room.getInstance(applicationContext)
// tokenResponse.serverUrl (wss://...), tokenResponse.token (starts with livekit_)
room.connect(tokenResponse.serverUrl, tokenResponse.token)
room.onTrackSubscribed = { track, publication, participant ->
// Attach video track to SurfaceViewRenderer
}
livekit_
prefix from the token. The server includes integer nbf/iat/exp
and grants
(e.g., roomJoin
, room
, canSubscribe
).
import Foundation
import CryptoKit
class PubfuseSDK {
private let apiKey: String
private let secretKey: String
private let baseURL: String
init(apiKey: String, secretKey: String, baseURL: String = "https://api.pubfuse.com") {
self.apiKey = apiKey
self.secretKey = secretKey
self.baseURL = baseURL
}
private func generateSignature(method: String, path: String, body: String, timestamp: String) -> String {
let payload = "\(method)\(path)\(body)\(timestamp)"
let key = SymmetricKey(data: secretKey.data(using: .utf8)!)
let signature = HMAC.authenticationCode(for: payload.data(using: .utf8)!, using: key)
return Data(signature).map { String(format: "%02hhx", $0) }.joined()
}
func makeRequest(method: String, path: String, data: T? = nil) async throws -> Data {
let timestamp = String(Int(Date().timeIntervalSince1970))
let body = data != nil ? try JSONEncoder().encode(data) : Data()
let bodyString = String(data: body, encoding: .utf8) ?? ""
let signature = generateSignature(method: method, path: path, body: bodyString, timestamp: timestamp)
var request = URLRequest(url: URL(string: "\(baseURL)\(path)")!)
request.httpMethod = method
request.setValue(apiKey, forHTTPHeaderField: "X-API-Key")
request.setValue(signature, forHTTPHeaderField: "X-Signature")
request.setValue(timestamp, forHTTPHeaderField: "X-Timestamp")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
if data != nil {
request.httpBody = body
}
let (data, _) = try await URLSession.shared.data(for: request)
return data
}
// Stream Management
func createSession(title: String, description: String = "") async throws -> SessionResponse {
let request = CreateSessionRequest(title: title, description: description)
let data = try await makeRequest(method: "POST", path: "/api/v1/sessions", data: request)
return try JSONDecoder().decode(SessionResponse.self, from: data)
}
func getSessions() async throws -> [SessionResponse] {
let data = try await makeRequest(method: "GET", path: "/api/v1/sessions")
return try JSONDecoder().decode([SessionResponse].self, from: data)
}
}
// Usage
let sdk = PubfuseSDK(apiKey: "pk_your_api_key", secretKey: "sk_your_secret_key")
Task {
do {
let session = try await sdk.createSession(title: "My Live Stream", description: "Welcome!")
print("Session created: \(session)")
} catch {
print("Error: \(error)")
}
}
Each SDK Client has a default rate limit of 1000 requests per hour. Monitor your usage and contact support if you need higher limits.
// Implement exponential backoff for rate limiting
async function makeRequestWithRetry(requestFn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await requestFn();
} catch (error) {
if (error.status === 429 && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // Exponential backoff
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
Error: 401 Unauthorized - Invalid API key
Solution:
X-API-Key
headerError: 429 Too Many Requests
Solution:
Error: 401 Unauthorized - Invalid signature
Solution:
Enable debug logging to troubleshoot API issues:
// Add debug logging to your SDK
class PubfuseSDK {
constructor(apiKey, secretKey, debug = false) {
this.apiKey = apiKey;
this.secretKey = secretKey;
this.debug = debug;
}
log(message, data = null) {
if (this.debug) {
console.log(`[PubfuseSDK] ${message}`, data);
}
}
async makeRequest(method, path, data = null) {
this.log(`Making request: ${method} ${path}`, data);
// ... rest of implementation
this.log('Response received:', response);
return response;
}
}
If you need assistance integrating the Pubfuse SDK, we're here to help!