Pubfuse SDK Documentation

Complete guide to integrating Pubfuse's live streaming platform into your applications

Getting Started

What is the Pubfuse SDK?

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.

Key Features:
  • Live Streaming: RTMP ingest with HLS playback
  • Real-time Chat: WebSocket-based messaging
  • User Management: Complete authentication system
  • Monetization: Tokens, diamonds, and subscriptions
  • Analytics: Real-time metrics and insights
Multi-tenant Architecture

Each SDK Client operates in isolation with their own users, sessions, and data. Perfect for white-label solutions.

Secure API Keys

Industry-standard API key authentication with optional HMAC signature verification for enhanced security.

SDK Registration

Step 1: Register Your SDK Client

First, you need to register your application to get API credentials.

POST /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
}
Response:
{
  "success": true,
  "apiKey": "pk_5951C5196A6C47EDA12D41B9A050AC5C",
  "secretKey": "sk_8510CDE5A7ED4E749D15E1008FBD7B7E",
  "clientId": "550e8400-e29b-41d4-a716-446655440000",
  "message": "SDK Client registered successfully"
}

Authentication

API Key Authentication

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"

HMAC Signature Authentication (Recommended)

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 Endpoints

Stream Management

POST /api/v1/sessions

Create a new streaming session

GET /api/v1/sessions

List all sessions for your SDK client

PATCH /api/v1/sessions/{id}

Update session status and metadata

User Management

POST /api/users/signup

Register a new user

✅ WORKING
POST /api/users/login

Authenticate a user

✅ WORKING
GET /api/v1/users

List users for your SDK client

Real-time Features

WebSocket /ws/streams/{id}/chat

Real-time chat connection

POST /api/v1/sessions/{id}/reactions

Send reactions to a stream

LiveKit SFU Integration

LiveKit Integration Benefits

  • Scalability: Support for 100+ concurrent participants
  • Recording: Built-in stream recording capabilities
  • Adaptive Streaming: Automatic quality adjustment
  • Professional Grade: Enterprise-ready infrastructure
  • Fallback Support: Automatic fallback to WebRTC if needed

Quick sanity-check tool: /livekit-test (connect → publish → remote subscribe)

Quick Start with LiveKit

// 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);
}

iOS LiveKit Integration

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)
        }
    }
}

API Endpoints for LiveKit ✅ UPDATED

  • GET /api/streaming/providers - Get available streaming providers
  • POST /api/streaming/sessions - Create streaming session
  • POST /api/streaming/sessions/{id}/token - Generate LiveKit JWT access token
  • GET /api/streaming/ice-config - Get ICE server configuration
  • GET /api/livekit/health - LiveKit server health check
🔑 JWT Token Generation

Tokens are LiveKit-compatible and include:

  • Epoch timestamps: nbf, iat, exp as integers
  • Grants: roomJoin, room, canSubscribe, optional publish/data grants
  • Room-scoped: Tied to a specific session/room
  • API key required: Provide X-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.

Code Examples

JavaScript/Node.js

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));

Python

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)

API Integration (Backend)

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);
  }
});

Watch View Flow (LiveKit)

This is what the web watch page does at runtime. Mirror this sequence for mobile clients.

  1. Extract session/stream ID: from the URL (UUID or short ID like r5).
  2. Load LiveKit SDK: wait until window.LiveKit is available.
  3. Choose provider: call GET /api/streaming/providers and select id === "livekit".
  4. Request token: POST /api/streaming/sessions/{id}/token with X-API-Key, role subscriber, and a UUID userId.
  5. Connect Room: pass serverUrl and the token (keep the livekit_ prefix) to the SDK Room.connect().
  6. Render tracks: on TrackSubscribed, attach remote video/audio to the player.
  7. Realtime events: reactions/chat are emitted over the data path; self-echo is ignored and duplicates are deduped on client.
// 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
}
  • Token: keep the livekit_ prefix; integer nbf/iat/exp; grants include roomJoin and room.
  • Provider: WebRTC fallback is disabled on watch; LiveKit is the primary.
  • Stability: client dedupes reactions and ignores self-echo; renegotiation is handled safely.

Mobile Implementation (iOS & Android)

Use your backend to mint the token, then connect the SDK on device.

iOS (Swift)
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
    }
}
Android (Kotlin)
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
}
Note: Do not strip the livekit_ prefix from the token. The server includes integer nbf/iat/exp and grants (e.g., roomJoin, room, canSubscribe).

Swift (iOS)

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)")
    }
}

Best Practices

Security
  • Never expose secret keys in client-side code
  • Use HTTPS for all API communications
  • Implement HMAC signature authentication
  • Rotate API keys periodically
  • Validate all input data
Performance
  • Implement proper error handling
  • Use connection pooling
  • Cache frequently accessed data
  • Implement retry logic with backoff
  • Monitor API usage and limits
Rate Limiting

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;
        }
    }
}

Troubleshooting

Common Issues

Error: 401 Unauthorized - Invalid API key

Solution:

  • Verify your API key is correct
  • Check that the key is properly included in the X-API-Key header
  • Ensure your SDK Client is active in the admin dashboard

Error: 429 Too Many Requests

Solution:

  • Implement exponential backoff retry logic
  • Cache responses when possible
  • Contact support for higher rate limits if needed

Error: 401 Unauthorized - Invalid signature

Solution:

  • Verify the signature generation algorithm
  • Check that the timestamp is within 5 minutes
  • Ensure the payload string matches exactly
  • Verify your secret key is correct

Debug Mode

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;
    }
}

Need Help?

If you need assistance integrating the Pubfuse SDK, we're here to help!

Email Support

[email protected]

API Documentation

Swagger API Docs

OpenAPI Spec

Download OpenAPI Spec