Service Disruption Callbacks

The Vonage Video platform continuously monitors all running services. When an internal failure affects a service — such as a media server crash, an archiver failure, a broadcast node going down, a SIP gateway issue, or a captions engine fault — the platform's monitoring system detects the failure, updates the service state, and immediately sends a callback notification to your registered endpoint.

This guide explains which callbacks each service emits on disruption, what the payloads look like, and how to recover each service programmatically.

Overview

Service disruptions can affect any of the following Vonage Video services:

Service What is disrupted Primary callback mechanism
Sessions Media Router crashes; participant connections and streams are terminated Server-side session monitoring webhook + client SDK events
Archives (Recordings) Archiver process crashes; the recording is terminated abnormally Archive status webhook ("status": "failed")
Broadcasts Broadcast node fails; the HLS/RTMP stream is terminated Broadcast status webhook ("status": "failed")
SIP Calls SIP gateway fails; the call leg is dropped Session monitoring webhook callDestroyed
Captions Captions engine fails; live transcription stops Captions status webhook ("status": "failed")

In all cases the reason field in the callback payload is normally set to "forceDisconnected". Other reason values may appear depending on the nature of the failure, so your recovery code should treat any unexpected termination (not caused by your own API calls) as a potential disruption.

Note: The platform also sends pre-disruption warnings for server rotations (reason: "serverRotation") via the sessionNotification event. See Server Rotation and Session Migration for details on that specific scenario.

Prerequisites

To receive server-side disruption callbacks you must register a Session Monitoring callback URL for your project. See Session Monitoring for setup instructions.

For archive, broadcast, and captions status callbacks, configure the corresponding status callback URL in your Vonage Video API project dashboard.


Session Disruption

When the Media Router hosting a session crashes, the session is destroyed and the platform sends a sessionDestroyed event to your session monitoring endpoint.

Callback Payloads

{
  "sessionId": "2_MX4xMzExMjU3MX5-MTQ3MDI1NzY3OTkxOH45QXRr",
  "projectId": "123456",
  "event": "sessionDestroyed",
  "reason": "forceDisconnected",
  "timestamp": 1718000050000
}

Client SDK events

In parallel with server-side webhooks, clients receive lifecycle events via the SDK:

session.on('sessionReconnecting', () => {
  // Platform is attempting automatic reconnection via session migration
  showReconnectingBanner();
});

session.on('sessionReconnected', () => {
  // Automatic recovery succeeded — no further action needed
  hideReconnectingBanner();
});

session.on('sessionDisconnected', (event) => {
  // Automatic recovery failed or is not enabled
  if (event.reason === 'networkDisconnected' || event.reason === 'networkTimedout') {
    promptUserToRejoin();
  }
});

publisher.on('streamDestroyed', (event) => {
  if (event.reason === 'networkDisconnected') {
    event.preventDefault(); // Keep the publisher element in the DOM
    retryPublish();
  }
});

For native SDKs use the equivalent delegate methods:

  • iOS: OTSessionDelegate.sessionDidBeginReconnecting(_:) / sessionDidReconnect(_:) / sessionDidDisconnect(_:)
  • Android: Session.SessionListener.onReconnecting() / onReconnected() / onDisconnected()

Recovery

When sessionDisconnected fires with reason: "forceDisconnected" (or after sessionReconnecting times out):

  1. Create a new session via the REST API or a Server SDK.
  2. Issue fresh tokens for all participants.
  3. Notify clients to reconnect — via your own signalling channel, push notification, or in-app message.
// Server-side — handle sessionDestroyed with reason "forceDisconnected"
app.post('/callbacks/vonage/session', (req, res) => {
  const { event, reason, sessionId } = req.body;

  if (event === 'sessionDestroyed' && reason === 'forceDisconnected') {
    createNewSessionAndNotifyClients(sessionId);
  }

  res.sendStatus(200);
});

Tip: If Session Migration is enabled, the platform may recover the session automatically. In that case sessionReconnected fires on the client and no manual rejoin is needed.


Archive (Recording) Disruption

When an archiver process crashes mid-recording, the platform's monitoring system detects the failure, marks the archive as "failed", and sends a status callback to your registered archive status URL.

Callback Payload

{
  "id": "b40ef09b-3811-4726-b508-e41a0f96c68f",
  "event": "archive",
  "status": "failed",
  "reason": "Internal server failure",
  "sessionId": "2_MX4xMzExMjU3MX5-MTQ3MDI1NzY3OTkxOH45QXRr",
  "projectId": "123456",
  "name": "My Recording",
  "createdAt": 1718000000000,
  "duration": 0,
  "outputMode": "composed",
  "hasVideo": true,
  "hasAudio": true
}

Key fields:

Field Value on disruption
status "failed"
reason "Internal server failure"
duration May be 0 if the failure occurred before the first segment was written

Recovery

app.post('/callbacks/vonage/archive', (req, res) => {
  const { status, reason, sessionId } = req.body;

  if (status === 'failed' && reason === 'Internal server failure') {
    // Start a new archive for the same session
    opentok.startArchive(sessionId, { name: 'Resumed recording' }, (err, archive) => {
      if (err) console.error('Failed to restart archive:', err);
      else console.log('New archive started:', archive.id);
    });
  }

  res.sendStatus(200);
});

Note: Always verify that the session still has active participants before restarting the archive. Starting an archive on an empty session will result in an error.


Broadcast Disruption

When a broadcast node fails, the HLS or RTMP stream is terminated and the broadcast is marked as "failed". A status callback is sent to your broadcast status URL.

Callback Payload

{
  "id": "1748b707-0a81-464c-9759-c46ad10d3734",
  "sessionId": "2_MX4xMDBfjE0Mzc2NzY1NDgwMTJ-TjMzfn4",
  "applicationId": 100,
  "createdAt": 1437676551000,
  "updatedAt": 1437676551000,
  "event": "broadcast",
  "group": "status",
  "resolution": "640x480",
  "streamMode" : "auto",
  "streams" : [],
  "broadcastUrls": {
    "hls" : "http://server/fakepath/playlist.m3u8",
    "hlsStatus": "error",
    "rtmp": {
      "foo": {
        "serverUrl": "rtmps://myfooserver:443/myfooapp",
        "streamName": "myfoostream",
        "status": "error"
      },
      "bar": {
        "serverUrl": "rtmp://mybarserver:443/mybarapp",
        "streamName": "mybarstream",
        "status": "error"
      }
    }
  },
  "settings": {
    "hls": {
      "dvr": false,
      "lowLatency": false
    }
  },
  "status": "failed",
  "reason": "Internal server failure"
}

Recovery

  1. Receive the "failed" status callback.
  2. Wait until clients have reconnected to the session (listen for connectionCreated events, or poll session participants).
  3. Start a new broadcast:
app.post('/callbacks/vonage/broadcast', (req, res) => {
  const { status, reason, sessionId } = req.body;

  if (status === 'failed' && reason === 'Internal server failure') {
    waitForParticipants(sessionId).then(() => {
      opentok.startBroadcast(sessionId, broadcastOptions, (err, broadcast) => {
        if (err) console.error('Failed to restart broadcast:', err);
        else notifyViewersOfNewStreamUrl(broadcast.broadcastUrls);
      });
    });
  }

  res.sendStatus(200);
});

Important: The HLS URL changes with each new broadcast. Make sure to update any players or downstream systems consuming the stream with the new URL.


SIP Call Disruption

When a SIP gateway failure drops an active call leg, the SIP connection is terminated and the platform sends callDestroyed to your session monitoring endpoint with reason_message: "Unexpected Clearing". The connection data of a SIP connection typically contains a sip identifier so you can distinguish it from regular participant connections.

Callback Payload

{
  "sessionId": "2_MX4xMzExMjU3MX5-MTQ3MDI1NzY3OTkxOH45QXRr",
  "applicationId": "123456",
  "event": "callDestroyed",
  "reason_code":  "703",
  "reason_message": "Unexpected Clearing",
  "timestamp": 1718000050000,
  "call": {
    "id":  "<conference-id>",
    "connectionId":  "<sip-ot-connection-id>",
    "createdAt":  1470257688143
  }
}

Recovery

Identify SIP connections by inspecting connection.data, then re-dial:

app.post('/callbacks/vonage/session', (req, res) => {
  const { event, reason_message, connection, sessionId } = req.body;

  if (event === 'callDestroyed' && reason_message === 'Unexpected Clearing') {
    let connData = {};
    try { connData = JSON.parse(connection.data); } catch (_) {}

    if (connData.sip) {
      // Re-initiate the SIP call
      const sipUri = getSipUriForConnection(connection.id);
      opentok.dial(sessionId, token, sipUri, sipOptions, (err, call) => {
        if (err) console.error('SIP redial failed:', err);
        else console.log('SIP call re-established:', call.id);
      });
    }
  }

  res.sendStatus(200);
});

Captions Disruption

When the captions engine fails, live transcription stops and the captions session is marked as "failed". A status callback is sent to your captions status URL.

Callback Payload

{
  "captionsId": "<captionsId>",
  "projectId": "<applicationId>",
  "sessionId": "<sessionId>",
  "status": "failed",
  "createdAt": 1651253477,
  "updatedAt": 1651253837,
  "duration": 360,
  "languageCode": "en-US",
  "reason": "Internal server failure",
  "provider": "aws-transcribe",
  "event": "sessionStatus"
}

Recovery

app.post('/callbacks/vonage/captions', (req, res) => {
  const { status, reason, sessionId } = req.body;

  if (status === 'failed' && reason === 'Internal server failure') {
    // Restart captions for the session
    fetch(`https://video.api.vonage.com/v2/project/${PROJECT_ID}/captions`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${jwt}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ sessionId, token: generateToken(sessionId) })
    })
    .then(res => res.json())
    .then(data => console.log('Captions restarted:', data.captionsId))
    .catch(err => console.error('Failed to restart captions:', err));
  }

  res.sendStatus(200);
});

Best Practices

  • Always check the reason field, but do not rely on it exclusively. While disruptions typically carry "Internal server failure", your recovery logic should treat any unexpected "failed" status or <something>Destroyed event as a potential platform disruption.
  • Implement idempotent recovery handlers. Callbacks may be delivered more than once. Use the resource ID (archive.id, captionsId, session.id) to deduplicate.
  • Use pre-rotation warnings. The sessionNotification event (with remainingTime: 3600 or 14400) gives you advance notice of planned rotations. Use it to cleanly stop archives, broadcasts, and captions before the rotation, avoiding a hard "forceDisconnected" termination.
  • Log all disruption events with their full payload for post-incident analysis. The timestamp field is your ground truth for calculating impact duration.
  • For sessions, prefer Session Migration. Enabling session migration dramatically reduces the blast radius of Media Router failures by transparently moving clients to a healthy server. See Server Rotation and Session Migration.
  • Set up alerts on "Internal server failure" callbacks. A spike in Internal server failure events across multiple sessions is an early signal of a wider platform incident.