WebRTC voice call signaling over XMPP IQ stanzas.
This API provides WebRTC call setup and management via XMPP, including:
Voice call signals are transmitted via XMPP IQ stanzas using the custom namespace
urn:xmpp:call:0. This allows call signaling to share the existing XMPP WebSocket
connection used for chat messaging.
Caller Callee
│ │
│ 1. call:initiate │
│ ─────────────────────────────────────────► │
│ │
│ 2. call:accept (or call:reject/call:busy) │
│ ◄───────────────────────────────────────── │
│ │
│ 3. call:offer (SDP) │
│ ─────────────────────────────────────────► │
│ │
│ 4. call:answer (SDP) │
│ ◄───────────────────────────────────────── │
│ │
│ 5. call:ice-candidate (multiple, both ways) │
│ ◄────────────────────────────────────────► │
│ │
│ [WebRTC media connection established] │
│ │
│ 6. call:mute-change (optional, both ways) │
│ ◄────────────────────────────────────────► │
│ │
│ 7. call:hangup │
│ ◄────────────────────────────────────────► │
│ │
| Signal | Direction | Description |
|---|---|---|
call:initiate |
Caller → Callee | Start a new call |
call:accept |
Callee → Caller | Accept incoming call |
call:reject |
Callee → Caller | Decline incoming call |
call:offer |
Caller → Callee | WebRTC SDP offer |
call:answer |
Callee → Caller | WebRTC SDP answer |
call:ice-candidate |
Both ways | ICE candidate for NAT traversal |
call:hangup |
Either party | End the call |
call:busy |
Callee → Caller | Callee is on another call |
call:mute-change |
Both ways | Mute state changed |
When a call ends (via call:hangup), the reason is included:
completed: Normal hangup after connected callrejected: Callee declined the calltimeout: No answer within timeout period (30 seconds)busy: Callee was on another callfailed: ICE/network connection failedcancelled: Caller cancelled before callee answeredLocal development server
Staging server (TLS required)
Production server (TLS required)
Voice call signaling via XMPP IQ stanzas.
Namespace: urn:xmpp:call:0
Authentication: JWT token must be present in auth_token cookie.
Voice call signals are embedded in XMPP IQ stanzas and share the same WebSocket connection as chat messages. The signal payload is JSON-encoded within the IQ stanza.
IQ Stanza Format:
<iq type="set" to="{recipientJID}" id="{messageId}">
<call xmlns="urn:xmpp:call:0">
{JSON signal payload}
</call>
</iq>
Initiate a voice call
Start a new voice call with another user. The callee will receive the initiate signal and can respond with accept, reject, or busy.
Available only on servers:
Accepts the following message:
{
"type": "call:initiate",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recipientId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"timestamp": "2025-12-08T10:30:00Z",
"payload": {
"callerName": "Alice",
"callerAvatar": "https://example.com/avatars/alice.jpg"
}
}
Voice call signaling via XMPP IQ stanzas.
Namespace: urn:xmpp:call:0
Authentication: JWT token must be present in auth_token cookie.
Voice call signals are embedded in XMPP IQ stanzas and share the same WebSocket connection as chat messages. The signal payload is JSON-encoded within the IQ stanza.
IQ Stanza Format:
<iq type="set" to="{recipientJID}" id="{messageId}">
<call xmlns="urn:xmpp:call:0">
{JSON signal payload}
</call>
</iq>
Receive incoming call
Receive notification of an incoming voice call. Respond with accept, reject, or busy within the timeout period (30 seconds).
Available only on servers:
Accepts the following message:
{
"type": "call:initiate",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recipientId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"timestamp": "2025-12-08T10:30:00Z",
"payload": {
"callerName": "Alice",
"callerAvatar": "https://example.com/avatars/alice.jpg"
}
}
Voice call signaling via XMPP IQ stanzas.
Namespace: urn:xmpp:call:0
Authentication: JWT token must be present in auth_token cookie.
Voice call signals are embedded in XMPP IQ stanzas and share the same WebSocket connection as chat messages. The signal payload is JSON-encoded within the IQ stanza.
IQ Stanza Format:
<iq type="set" to="{recipientJID}" id="{messageId}">
<call xmlns="urn:xmpp:call:0">
{JSON signal payload}
</call>
</iq>
Accept incoming call
Accept an incoming call. The caller will then send an SDP offer.
Available only on servers:
Accepts the following message:
{
"type": "call:accept",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"recipientId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2025-12-08T10:30:05Z",
"payload": {}
}
Voice call signaling via XMPP IQ stanzas.
Namespace: urn:xmpp:call:0
Authentication: JWT token must be present in auth_token cookie.
Voice call signals are embedded in XMPP IQ stanzas and share the same WebSocket connection as chat messages. The signal payload is JSON-encoded within the IQ stanza.
IQ Stanza Format:
<iq type="set" to="{recipientJID}" id="{messageId}">
<call xmlns="urn:xmpp:call:0">
{JSON signal payload}
</call>
</iq>
Reject incoming call
Decline an incoming call.
Available only on servers:
Accepts the following message:
{
"type": "call:reject",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"recipientId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2025-12-08T10:30:05Z",
"payload": {}
}
Voice call signaling via XMPP IQ stanzas.
Namespace: urn:xmpp:call:0
Authentication: JWT token must be present in auth_token cookie.
Voice call signals are embedded in XMPP IQ stanzas and share the same WebSocket connection as chat messages. The signal payload is JSON-encoded within the IQ stanza.
IQ Stanza Format:
<iq type="set" to="{recipientJID}" id="{messageId}">
<call xmlns="urn:xmpp:call:0">
{JSON signal payload}
</call>
</iq>
Send busy signal
Indicate that you're already on another call.
Available only on servers:
Accepts the following message:
{
"type": "call:busy",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"recipientId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2025-12-08T10:30:02Z",
"payload": {}
}
Voice call signaling via XMPP IQ stanzas.
Namespace: urn:xmpp:call:0
Authentication: JWT token must be present in auth_token cookie.
Voice call signals are embedded in XMPP IQ stanzas and share the same WebSocket connection as chat messages. The signal payload is JSON-encoded within the IQ stanza.
IQ Stanza Format:
<iq type="set" to="{recipientJID}" id="{messageId}">
<call xmlns="urn:xmpp:call:0">
{JSON signal payload}
</call>
</iq>
Send SDP offer
Send WebRTC SDP offer after call is accepted. This contains the caller's media capabilities.
Available only on servers:
Accepts the following message:
{
"type": "call:offer",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recipientId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"timestamp": "2025-12-08T10:30:06Z",
"payload": {
"sdp": "v=0\\r\\no=- 123456 2 IN IP4 127.0.0.1\\r\\n...",
"type": "offer"
}
}
Voice call signaling via XMPP IQ stanzas.
Namespace: urn:xmpp:call:0
Authentication: JWT token must be present in auth_token cookie.
Voice call signals are embedded in XMPP IQ stanzas and share the same WebSocket connection as chat messages. The signal payload is JSON-encoded within the IQ stanza.
IQ Stanza Format:
<iq type="set" to="{recipientJID}" id="{messageId}">
<call xmlns="urn:xmpp:call:0">
{JSON signal payload}
</call>
</iq>
Receive SDP offer
Receive the caller's SDP offer.
Available only on servers:
Accepts the following message:
{
"type": "call:offer",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recipientId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"timestamp": "2025-12-08T10:30:06Z",
"payload": {
"sdp": "v=0\\r\\no=- 123456 2 IN IP4 127.0.0.1\\r\\n...",
"type": "offer"
}
}
Voice call signaling via XMPP IQ stanzas.
Namespace: urn:xmpp:call:0
Authentication: JWT token must be present in auth_token cookie.
Voice call signals are embedded in XMPP IQ stanzas and share the same WebSocket connection as chat messages. The signal payload is JSON-encoded within the IQ stanza.
IQ Stanza Format:
<iq type="set" to="{recipientJID}" id="{messageId}">
<call xmlns="urn:xmpp:call:0">
{JSON signal payload}
</call>
</iq>
Send SDP answer
Send WebRTC SDP answer in response to offer. This contains the callee's media capabilities.
Available only on servers:
Accepts the following message:
{
"type": "call:answer",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"recipientId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2025-12-08T10:30:07Z",
"payload": {
"sdp": "v=0\\r\\no=- 654321 2 IN IP4 127.0.0.1\\r\\n...",
"type": "answer"
}
}
Voice call signaling via XMPP IQ stanzas.
Namespace: urn:xmpp:call:0
Authentication: JWT token must be present in auth_token cookie.
Voice call signals are embedded in XMPP IQ stanzas and share the same WebSocket connection as chat messages. The signal payload is JSON-encoded within the IQ stanza.
IQ Stanza Format:
<iq type="set" to="{recipientJID}" id="{messageId}">
<call xmlns="urn:xmpp:call:0">
{JSON signal payload}
</call>
</iq>
Receive SDP answer
Receive the callee's SDP answer.
Available only on servers:
Accepts the following message:
{
"type": "call:answer",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"recipientId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2025-12-08T10:30:07Z",
"payload": {
"sdp": "v=0\\r\\no=- 654321 2 IN IP4 127.0.0.1\\r\\n...",
"type": "answer"
}
}
Voice call signaling via XMPP IQ stanzas.
Namespace: urn:xmpp:call:0
Authentication: JWT token must be present in auth_token cookie.
Voice call signals are embedded in XMPP IQ stanzas and share the same WebSocket connection as chat messages. The signal payload is JSON-encoded within the IQ stanza.
IQ Stanza Format:
<iq type="set" to="{recipientJID}" id="{messageId}">
<call xmlns="urn:xmpp:call:0">
{JSON signal payload}
</call>
</iq>
Send ICE candidate
Send an ICE candidate for NAT traversal. Multiple candidates may be sent in both directions during connection establishment.
Available only on servers:
Accepts the following message:
ICE candidate for NAT traversal
{
"type": "call:ice-candidate",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recipientId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"timestamp": "2025-12-08T10:30:08Z",
"payload": {
"candidate": "candidate:1 1 UDP 2122194687 192.168.1.100 54321 typ host",
"sdpMid": "0",
"sdpMLineIndex": 0
}
}
Voice call signaling via XMPP IQ stanzas.
Namespace: urn:xmpp:call:0
Authentication: JWT token must be present in auth_token cookie.
Voice call signals are embedded in XMPP IQ stanzas and share the same WebSocket connection as chat messages. The signal payload is JSON-encoded within the IQ stanza.
IQ Stanza Format:
<iq type="set" to="{recipientJID}" id="{messageId}">
<call xmlns="urn:xmpp:call:0">
{JSON signal payload}
</call>
</iq>
Receive ICE candidate
Receive an ICE candidate from the other party.
Available only on servers:
Accepts the following message:
ICE candidate for NAT traversal
{
"type": "call:ice-candidate",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recipientId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"timestamp": "2025-12-08T10:30:08Z",
"payload": {
"candidate": "candidate:1 1 UDP 2122194687 192.168.1.100 54321 typ host",
"sdpMid": "0",
"sdpMLineIndex": 0
}
}
Voice call signaling via XMPP IQ stanzas.
Namespace: urn:xmpp:call:0
Authentication: JWT token must be present in auth_token cookie.
Voice call signals are embedded in XMPP IQ stanzas and share the same WebSocket connection as chat messages. The signal payload is JSON-encoded within the IQ stanza.
IQ Stanza Format:
<iq type="set" to="{recipientJID}" id="{messageId}">
<call xmlns="urn:xmpp:call:0">
{JSON signal payload}
</call>
</iq>
End the call
Terminate the call. Include the reason and optional duration if the call was connected.
Available only on servers:
Accepts the following message:
{
"type": "call:hangup",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recipientId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"timestamp": "2025-12-08T10:35:00Z",
"payload": {
"reason": "completed",
"duration": 294
}
}
{
"type": "call:hangup",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recipientId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"timestamp": "2025-12-08T10:30:10Z",
"payload": {
"reason": "cancelled"
}
}
Voice call signaling via XMPP IQ stanzas.
Namespace: urn:xmpp:call:0
Authentication: JWT token must be present in auth_token cookie.
Voice call signals are embedded in XMPP IQ stanzas and share the same WebSocket connection as chat messages. The signal payload is JSON-encoded within the IQ stanza.
IQ Stanza Format:
<iq type="set" to="{recipientJID}" id="{messageId}">
<call xmlns="urn:xmpp:call:0">
{JSON signal payload}
</call>
</iq>
Receive hangup
Receive notification that the other party ended the call.
Available only on servers:
Accepts the following message:
{
"type": "call:hangup",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recipientId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"timestamp": "2025-12-08T10:35:00Z",
"payload": {
"reason": "completed",
"duration": 294
}
}
{
"type": "call:hangup",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recipientId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"timestamp": "2025-12-08T10:30:10Z",
"payload": {
"reason": "cancelled"
}
}
Voice call signaling via XMPP IQ stanzas.
Namespace: urn:xmpp:call:0
Authentication: JWT token must be present in auth_token cookie.
Voice call signals are embedded in XMPP IQ stanzas and share the same WebSocket connection as chat messages. The signal payload is JSON-encoded within the IQ stanza.
IQ Stanza Format:
<iq type="set" to="{recipientJID}" id="{messageId}">
<call xmlns="urn:xmpp:call:0">
{JSON signal payload}
</call>
</iq>
Send mute state change
Notify the other party of your mute state change.
Available only on servers:
Accepts the following message:
{
"type": "call:mute-change",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recipientId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"timestamp": "2025-12-08T10:32:00Z",
"payload": {
"muted": true
}
}
{
"type": "call:mute-change",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recipientId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"timestamp": "2025-12-08T10:33:00Z",
"payload": {
"muted": false
}
}
Voice call signaling via XMPP IQ stanzas.
Namespace: urn:xmpp:call:0
Authentication: JWT token must be present in auth_token cookie.
Voice call signals are embedded in XMPP IQ stanzas and share the same WebSocket connection as chat messages. The signal payload is JSON-encoded within the IQ stanza.
IQ Stanza Format:
<iq type="set" to="{recipientJID}" id="{messageId}">
<call xmlns="urn:xmpp:call:0">
{JSON signal payload}
</call>
</iq>
Receive mute state change
Receive notification of the other party's mute state.
Available only on servers:
Accepts the following message:
{
"type": "call:mute-change",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recipientId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"timestamp": "2025-12-08T10:32:00Z",
"payload": {
"muted": true
}
}
{
"type": "call:mute-change",
"callId": "550e8400-e29b-41d4-a716-446655440000",
"senderId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"recipientId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"timestamp": "2025-12-08T10:33:00Z",
"payload": {
"muted": false
}
}
ICE candidate for NAT traversal
Type of call signal
Reason for call ending
Base schema for all call signals
Payload for call:initiate signal
Payload for SDP offer/answer signals
Payload for ICE candidate signal
Payload for call:hangup signal
Payload for call:mute-change signal
Empty payload for signals that don't need additional data