Skip to content

Instantly share code, notes, and snippets.

@cyrenity
Created January 7, 2026 09:44
Show Gist options
  • Select an option

  • Save cyrenity/9d49b190052d1464a8d40008b963334a to your computer and use it in GitHub Desktop.

Select an option

Save cyrenity/9d49b190052d1464a8d40008b963334a to your computer and use it in GitHub Desktop.
FreeSWITCH Verto (mod_verto) Protocol

FreeSWITCH Verto Protocol Documentation

This document describes the Verto protocol, a JSON-RPC 2.0 protocol used by FreeSWITCH's [mod_verto] to provide WebRTC signaling and event handling over Secure WebSockets (WSS).

Overview

  • Protocol: JSON-RPC 2.0
  • Transport: Secure WebSockets (WSS)
  • Endpoint: mod_verto acts as the server endpoint.

Connection and Session

Authentication (login)

Request:

{
  "jsonrpc": "2.0",
  "method": "login",
  "params": {
    "login": "1000@example.com",
    "passwd": "password123",
    "sessid": "optional-uuid-for-session-recovery"
  },
  "id": 1
}

Response (Success):

{
  "jsonrpc": "2.0",
  "result": {
    "message": "logged in",
    "sessid": "abc12345-uuid"
  },
  "id": 1
}

Session Recovery

  • verto.attach (Server → Client): Re-establishes call state for active calls
  • verto.clientReady (Server → Client): Session re-attachment complete
  • verto.punt (Server → old Client): Notifies the old connection that a new one claimed the session

Call Control Methods

verto.invite - Initiate Outbound Call

Request:

{
  "jsonrpc": "2.0",
  "method": "verto.invite",
  "params": {
    "dialogParams": {
      "callID": "uuid-generated-by-client",
      "destination_number": "9999",
      "caller_id_name": "John Doe",
      "caller_id_number": "1000",
      "remote_caller_id_name": "Conference",
      "remote_caller_id_number": "9999"
    },
    "sdp": "v=0\r\no=- 12345 IN IP4 ..."
  },
  "id": 2
}

verto.answer - Answer Incoming Call

Request:

{
  "jsonrpc": "2.0",
  "method": "verto.answer",
  "params": {
    "dialogParams": {
      "callID": "call-uuid-from-invite"
    },
    "sdp": "v=0\r\no=- 12345 IN IP4 ..."
  },
  "id": 3
}

verto.bye - Terminate Call

Request:

{
  "jsonrpc": "2.0",
  "method": "verto.bye",
  "params": {
    "dialogParams": {
      "callID": "call-uuid"
    },
    "cause": "NORMAL_CLEARING"
  },
  "id": 4
}

verto.modify - Modify Active Call

All verto.modify requests require dialogParams.callID and action in params.

Response Structure (all actions):

{
  "jsonrpc": "2.0",
  "result": {
    "callID": "call-uuid",
    "action": "hold",
    "holdState": "held"
  },
  "id": 5
}
Response Field Description
callID Echo of the call UUID
action Echo of the action performed
holdState Current hold state: "held" or "active"
sdp (updateMedia only) The new local SDP
message Error message if action failed

Hold:

{
  "jsonrpc": "2.0",
  "method": "verto.modify",
  "params": {
    "dialogParams": { "callID": "call-uuid" },
    "action": "hold"
  },
  "id": 5
}

Unhold:

{
  "jsonrpc": "2.0",
  "method": "verto.modify",
  "params": {
    "dialogParams": { "callID": "call-uuid" },
    "action": "unhold"
  },
  "id": 6
}

Toggle Hold:

{
  "jsonrpc": "2.0",
  "method": "verto.modify",
  "params": {
    "dialogParams": { "callID": "call-uuid" },
    "action": "toggleHold"
  },
  "id": 7
}

Transfer:

{
  "jsonrpc": "2.0",
  "method": "verto.modify",
  "params": {
    "dialogParams": { "callID": "call-uuid" },
    "action": "transfer",
    "destination": "1001"
  },
  "id": 8
}

Replace/Bridge (Attended Transfer):

{
  "jsonrpc": "2.0",
  "method": "verto.modify",
  "params": {
    "dialogParams": { "callID": "call-uuid" },
    "action": "replace",
    "replaceCallID": "other-call-uuid"
  },
  "id": 9
}

Video Refresh (keyframe request):

{
  "jsonrpc": "2.0",
  "method": "verto.modify",
  "params": {
    "dialogParams": { "callID": "call-uuid" },
    "action": "videoRefresh"
  },
  "id": 10
}

Update Media (re-INVITE with new SDP):

{
  "jsonrpc": "2.0",
  "method": "verto.modify",
  "params": {
    "dialogParams": { "callID": "call-uuid" },
    "action": "updateMedia",
    "sdp": "v=0\r\no=- 12345 IN IP4 ..."
  },
  "id": 11
}

Response includes new local SDP:

{
  "jsonrpc": "2.0",
  "result": {
    "callID": "call-uuid",
    "action": "updateMedia",
    "sdp": "v=0\r\no=- 67890 IN IP4 ...",
    "holdState": "active"
  },
  "id": 11
}

Note

updateMedia can only be called on an answered call. Calling it before the call is answered will result in the call being hung up.

verto.info - Send Information

DTMF:

{
  "jsonrpc": "2.0",
  "method": "verto.info",
  "params": {
    "dialogParams": { "callID": "call-uuid" },
    "dtmf": "1234#"
  },
  "id": 11
}

Chat Message:

{
  "jsonrpc": "2.0",
  "method": "verto.info",
  "params": {
    "msg": {
      "to": "1001@example.com",
      "from": "1000@example.com",
      "body": "Hello!"
    }
  },
  "id": 12
}

Real-Time Text (RTT):

{
  "jsonrpc": "2.0",
  "method": "verto.info",
  "params": {
    "dialogParams": { "callID": "call-uuid" },
    "txt": {
      "code": 65,
      "chars": "A"
    },
    "noDialogParams": true
  },
  "id": 13
}

Event System

verto.subscribe - Subscribe to Events

Request:

{
  "jsonrpc": "2.0",
  "method": "verto.subscribe",
  "params": {
    "eventChannel": ["conference.9999@example.com", "presence"]
  },
  "id": 14
}

Response:

{
  "jsonrpc": "2.0",
  "result": {
    "subscribedChannels": ["conference.9999@example.com"],
    "unauthorizedChannels": ["presence"]
  },
  "id": 14
}

verto.unsubscribe - Unsubscribe from Events

Request:

{
  "jsonrpc": "2.0",
  "method": "verto.unsubscribe",
  "params": {
    "eventChannel": "conference.9999@example.com"
  },
  "id": 15
}

verto.broadcast - Broadcast to Channel

Request:

{
  "jsonrpc": "2.0",
  "method": "verto.broadcast",
  "params": {
    "eventChannel": "conference.9999@example.com",
    "data": {
      "action": "chat",
      "message": "Hello everyone!"
    }
  },
  "id": 16
}

verto.event (Server → Client)

Server pushes events to subscribed clients:

{
  "jsonrpc": "2.0",
  "method": "verto.event",
  "params": {
    "eventChannel": "conference.9999@example.com",
    "pvtData": { "action": "conference-liveArray-join" },
    "data": { ... }
  }
}

Conference LiveArray Events

Conference participant management uses a "liveArray" mechanism. When joining a conference, the server sends private data containing channel information, then pushes real-time participant updates.

Conference Detection (pvtData)

When a call joins a conference, the server sends verto.event with pvtData:

{
  "jsonrpc": "2.0",
  "method": "verto.event",
  "params": {
    "eventType": "channelPvtData",
    "pvtData": {
      "laChannel": "conference-liveArray.3500@domain.com",
      "laName": "3500",
      "role": "moderator",
      "chatChannel": "conference-chat.3500@domain.com",
      "infoChannel": "conference-info.3500@domain.com"
    }
  }
}
Field Description
laChannel LiveArray channel to subscribe to for participant updates
laName Conference name/number
role User role: "moderator" or "participant"
chatChannel Channel for chat messages
infoChannel Channel for conference info updates

Subscribing to LiveArray

After receiving pvtData, subscribe to the liveArray channel:

{
  "jsonrpc": "2.0",
  "method": "verto.subscribe",
  "params": {
    "eventChannel": "conference-liveArray.3500@domain.com",
    "subParams": {}
  },
  "id": 20
}

LiveArray Actions

bootObj - Initial Participant List

Sent immediately after subscription with all current participants:

{
  "jsonrpc": "2.0",
  "method": "verto.event",
  "params": {
    "eventChannel": "conference-liveArray.3500@domain.com",
    "data": {
      "action": "bootObj",
      "data": [
        ["1001", 0, [{"participantId": "1001", "participantName": "Alice", "audio": {"muted": false, "talking": false}, "video": {"muted": false}}]],
        ["1002", 1, [{"participantId": "1002", "participantName": "Bob", "audio": {"muted": true, "talking": false}, "video": {"muted": true}}]]
      ]
    }
  }
}

add - Participant Joined

{
  "jsonrpc": "2.0",
  "method": "verto.event",
  "params": {
    "eventChannel": "conference-liveArray.3500@domain.com",
    "data": {
      "action": "add",
      "hashKey": "1003",
      "arrIndex": 2,
      "data": [{
        "participantId": "1003",
        "participantName": "Charlie",
        "audio": { "muted": false, "talking": false },
        "video": { "muted": false }
      }]
    }
  }
}

modify - Participant State Changed

{
  "jsonrpc": "2.0",
  "method": "verto.event",
  "params": {
    "eventChannel": "conference-liveArray.3500@domain.com",
    "data": {
      "action": "modify",
      "hashKey": "1001",
      "arrIndex": 0,
      "data": [{
        "participantId": "1001",
        "participantName": "Alice",
        "audio": { "muted": false, "talking": true },
        "video": { "muted": false }
      }]
    }
  }
}

del - Participant Left

{
  "jsonrpc": "2.0",
  "method": "verto.event",
  "params": {
    "eventChannel": "conference-liveArray.3500@domain.com",
    "data": {
      "action": "del",
      "hashKey": "1002",
      "arrIndex": 1
    }
  }
}

Participant Data Structure

Field Type Description
participantId string Unique member ID
participantName string Display name
audio.muted boolean Microphone muted
audio.talking boolean Currently speaking
video.muted boolean Camera disabled

Conference Control Commands

Moderators can control the conference via verto.broadcast:

Mute a Member:

{
  "jsonrpc": "2.0",
  "method": "verto.broadcast",
  "params": {
    "eventChannel": "conference-liveArray.3500@domain.com",
    "data": {
      "action": "mute",
      "memberId": "1002"
    }
  },
  "id": 21
}

Kick a Member:

{
  "jsonrpc": "2.0",
  "method": "verto.broadcast",
  "params": {
    "eventChannel": "conference-liveArray.3500@domain.com",
    "data": {
      "action": "kick",
      "memberId": "1002"
    }
  },
  "id": 22
}

Send Chat Message:

{
  "jsonrpc": "2.0",
  "method": "verto.broadcast",
  "params": {
    "eventChannel": "conference-chat.3500@domain.com",
    "data": {
      "action": "chat",
      "from": "Alice",
      "message": "Hello everyone!"
    }
  },
  "id": 23
}

System Methods

verto.ping - Keep-Alive

Request:

{
  "jsonrpc": "2.0",
  "method": "verto.ping",
  "id": 99
}

Response:

{
  "jsonrpc": "2.0",
  "result": { "message": "PONG" },
  "id": 99
}

dialogParams Structure

All call-related methods include dialogParams:

Field Description
callID Unique UUID for the call (client-generated for outbound)
destination_number Number to dial
caller_id_name Caller's display name
caller_id_number Caller's number
callee_id_name Callee's display name (for answered calls)
callee_id_number Callee's number
remote_caller_id_name Remote party name
remote_caller_id_number Remote party number
login User login string

Error Codes

Code Description
-32600 Invalid Request
-32601 Invalid Method
-32602 Permission Denied
-32000 Auth Required - must login first
-32001 Auth Failed - invalid credentials
-32002 Session Error

Session Recovery

Verto supports session recovery to handle network disconnections or page reloads without dropping active calls.

Recovery Flow

  1. Disconnection: WebSocket closes. Server marks session as "detached" and starts a timeout timer.
  2. Reconnection: Client reconnects and sends login with the original sessid.
  3. Server Initiation: Server detects the reattached session has detached calls and sends verto.attach REQUEST to the client.
  4. Client Response: Client handles the request, creates a new PeerConnection, and sends a verto.attach METHOD back to server with the Answer SDP.
  5. Restoration: Server processes the answer, clears ICE, and call media resumes.

1. Server Sends verto.attach (Request)

The server invites the client to reattach to a specific call.

{
  "jsonrpc": "2.0",
  "method": "verto.attach",
  "id": 1234,
  "params": {
    "callID": "e1f19988-4fbd-4b81-842d-24239a854587",
    "sdp": "v=0\r\no=FreeSWITCH ... (Server's Offer)",
    "display_name": "Alice",
    "display_number": "1001",
    "callee_id_name": "Bob",
    "callee_id_number": "1002"
  }
}

2. Client Sends verto.attach (Method)

The client accepts the offer, generates an answer, and sends it back.

{
  "jsonrpc": "2.0",
  "method": "verto.attach",
  "id": 5678,
  "params": {
    "dialogParams": {
      "callID": "e1f19988-4fbd-4b81-842d-24239a854587",
      "destination_number": "1002",
      "caller_id_name": "Alice",
      "caller_id_number": "1001"
    },
    "sdp": "v=0\r\no=- ... (Client's New Answer)"
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment