Skip to content

Webhooks & Stream Events

Learn how to monitor stream status and handle stream events in your application.

Stream Status Flow

Stream States

StateDescriptionWhen it happens
READY_FOR_USEStream created, waiting for broadcasterAfter calling Create Livestream API
STARTEDBroadcaster connected, stream is liveWhen broadcaster starts pushing RTMP
ENDEDBroadcaster disconnectedWhen broadcaster stops streaming
TOKEN_EXPIREDPush token expiredWhen token lifetime exceeded

Monitoring Stream Status

Polling Method

Poll the API to check stream status changes:

javascript
async function monitorStreamStatus(streamKey, onStatusChange) {
  let lastStatus = null;

  const checkStatus = async () => {
    try {
      const response = await fetch(
        `https://api.xxxxxx.xxx/api/v1/livestreams/stream-info/${streamKey}`
      );
      const { data } = await response.json();

      if (data.status !== lastStatus) {
        onStatusChange(data.status, lastStatus);
        lastStatus = data.status;
      }
    } catch (error) {
      console.error('Failed to check stream status:', error);
    }
  };

  // Poll every 5 seconds
  setInterval(checkStatus, 5000);

  // Initial check
  checkStatus();
}

// Usage
monitorStreamStatus('ee04affe2a669854052102fe762bd715', (newStatus, oldStatus) => {
  console.log(`Stream status changed: ${oldStatus} -> ${newStatus}`);

  switch (newStatus) {
    case 'STARTED':
      notifyViewers('Stream is now live!');
      break;
    case 'ENDED':
      notifyViewers('Stream has ended');
      break;
    case 'TOKEN_EXPIRED':
      refreshPushToken();
      break;
  }
});

Get Stream Info API

bash
curl -X GET "https://api.xxxxxx.xxx/api/v1/livestreams/stream-info/YOUR_STREAM_KEY"

Response:

json
{
  "data": {
    "streamKey": "ee04affe2a669854052102fe762bd715",
    "status": "STARTED",
    "hlsSources": [
      {
        "name": "origin",
        "url": "https://stream.lunarstream.kozow.com/hls/ee04affe2a669854052102fe762bd715/master.m3u8"
      }
    ]
  }
}

Handling Status Changes

React Example

javascript
import { useState, useEffect, useCallback } from 'react';

function useStreamStatus(streamKey) {
  const [status, setStatus] = useState(null);
  const [loading, setLoading] = useState(true);

  const checkStatus = useCallback(async () => {
    try {
      const response = await fetch(
        `https://api.xxxxxx.xxx/api/v1/livestreams/stream-info/${streamKey}`
      );
      const { data } = await response.json();
      setStatus(data.status);
    } catch (error) {
      console.error('Status check failed:', error);
    } finally {
      setLoading(false);
    }
  }, [streamKey]);

  useEffect(() => {
    checkStatus();
    const interval = setInterval(checkStatus, 5000);
    return () => clearInterval(interval);
  }, [checkStatus]);

  return { status, loading };
}

// Usage in component
const StreamViewer = ({ streamKey }) => {
  const { status, loading } = useStreamStatus(streamKey);

  if (loading) return <div>Loading...</div>;

  switch (status) {
    case 'STARTED':
      return <VideoPlayer streamKey={streamKey} />;
    case 'READY_FOR_USE':
      return <div>Waiting for stream to start...</div>;
    case 'ENDED':
      return <div>Stream has ended</div>;
    default:
      return <div>Stream unavailable</div>;
  }
};

Best Practices

1. Polling Frequency

  • Recommended: Poll every 5-10 seconds
  • Minimum: Don't poll more than once per second
  • Stop polling: When stream status is ENDED

2. Error Handling

javascript
async function safeStatusCheck(streamKey) {
  const maxRetries = 3;
  let retries = 0;

  while (retries < maxRetries) {
    try {
      const response = await fetch(
        `https://api.xxxxxx.xxx/api/v1/livestreams/stream-info/${streamKey}`
      );

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }

      return await response.json();
    } catch (error) {
      retries++;
      if (retries >= maxRetries) {
        throw error;
      }
      // Wait before retry
      await new Promise(resolve => setTimeout(resolve, 1000 * retries));
    }
  }
}

3. Caching

Cache stream status to reduce API calls:

javascript
const statusCache = new Map();
const CACHE_TTL = 3000; // 3 seconds

async function getCachedStatus(streamKey) {
  const cached = statusCache.get(streamKey);

  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.status;
  }

  const { data } = await fetchStreamStatus(streamKey);
  statusCache.set(streamKey, {
    status: data.status,
    timestamp: Date.now()
  });

  return data.status;
}

Troubleshooting

IssuePossible CauseSolution
Status not updatingPolling too slowReduce polling interval
Rate limit errorsPolling too fastIncrease polling interval
Stream stuck in READYBroadcaster not connectedCheck RTMP credentials
Unexpected ENDEDNetwork issuesCheck broadcaster connection

Next Steps

Released under the MIT License.