Skip to main content

Call Control

Make a Call

RustPBX supports two call methods: SIP and WebRTC, distinguished by path when connecting to RustPBX:

  • /call/sip: Establish calls using SIP protocol, RTP transmits audio.
  • /call/webrtc: Establish calls directly using SDP Offer.

Use the Invite command to initiate a call. When the call is successfully established, you will receive an Answer event, otherwise you will receive a Reject event.

1: Invite
1: Invite
Client
Client
2: INVITE
2: INVITE
RustPBX
RustPBX
Phone
Phone
3: 200 OK
3: 200 OK
4. Answer
4. Answer
5. ACK
5. ACK
Text is not SVG - cannot display

Both calling and answering use CallOption, except for setting the callee address, other parameters are the same, mainly used to configure the following functions:

  • TTS: Text-to-speech
  • ASR: Speech recognition
  • VAD: Voice activity detection
  • Recording: Recording
  • denoise: Noise reduction

SIP Call

SIP calls need to set the caller (caller) and callee (callee) SIP addresses in CallOption, example:

{
"command": "invite",
"option": {
"caller": "sip:6001@192.168.1.100",
"callee": "sip:6002@192.168.1.100"
}
}

The caller address can be set to the UserAgent's SIP address, default is sip:{localIP}:13050.

The UserAgent's SIP address can be configured in config.toml:

config.toml
[ua]
addr = "0.0.0.0"
udp_port = 13050 # Avoid using 5060
info

UserAgent is equivalent to a phone, so it has its own SIP address.

WebRTC Call

WebRTC calls need to set the SDP Offer of the call target in the option.offer field, example:

{
"command": "invite",
"option": {
"offer": "v=0\r\no=- 123456789 2 IN IP4 127.0.0.1\r\n..."
}
}

Answer/Reject

Answering is slightly more complex than calling, requiring Webhook configuration to receive incoming call notifications. There are the following steps:

  • Configure Webhook
  • When there is an incoming call, RustPBX will send a request to the Webhook address
  • WebHook service connects to RustPBX based on the dialogId in the request
  • Send Accept command to answer, or Reject command to reject
2: WebHook
http://localhost:8090/webhook?dialogId: 666
2: WebHook...
Client
Client
1: INVITE
1: INVITE
RustPBX
RustPBX
Phone
Phone
5: 200 OK/603 Decline
5: 200 OK/603 Decline
3. Connect
ws://localhost:8080/call/sip?id=666
3. Connect...
5: ACK
5: ACK
4. Accept/Reject
4. Accept/Reject
Text is not SVG - cannot display

Configure Webhook

Configure the Webhook address in config.toml:

config.toml
[ua.handler]
type = "webhook"
url = "http://localhost:8090/webhook"
method = "POST"

Receive Notification

When there is an incoming call, RustPBX will send a POST request to the Webhook address, example:

{
"event": "invite",
"dialogId": "9zUPiixVNO-xRTf4NqCV-dv9qE2iB",
"caller": "sip:6001@192.168.139.103",
"callee": "sip:192.168.3.197",
"createdAt": "2025-10-23T06:33:26.623706+00:00"
}
info

The dialogId format is: {call_id}-{tag1}-{tag2}, where tag1 is the lexicographically larger one of from_tag and to_tag, and tag2 is the smaller one.

Connect to RustPBX

Use dialogId as the value of the id parameter to establish a WebSocket connection

'ws://localhost:8080/call/sip?id=9zUPiixVNO-xRTf4NqCV-dv9qE2iB'

Answer/Reject

Then send the Accept (answer) / Reject (reject) command:

Example Code:

There is webhook example code in RustPBX Go SDK, which can be referenced:

cmd/webhook.go

func serveWebhook(parent context.Context, option CreateClientOption, addr, prefix string) {
server := gin.Default()
server.POST(prefix, func(c *gin.Context) {
var form IncomingCall
if err := c.ShouldBindJSON(&form); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}

client := createClient(parent, option, form.DialogID)

go func() {
ctx, cancel := context.WithCancel(parent)
defer cancel()
err := client.Connect("sip")
if err != nil {
option.Logger.Errorf("Failed to connect to server: %v", err)
}
defer client.Shutdown()

client.Accept(option.CallOption)
time.Sleep(300 * time.Millisecond)
client.TTS(option.Greeting, "", "", true, false, nil, nil, false)
<-ctx.Done()
}()

c.JSON(200, gin.H{"message": "OK"})
})
server.Run(addr)
}
info

See RustPBX Go SDK for more information.

Transfer

Use the Refer command during a call to transfer the call to another number, used for "transfer to human" scenarios.

Transfer Flow:

  • Call is already established, send Refer command
  • RustPBX calls the transfer target
  • After the target answers, RustPBX forwards audio between both ends
2. Refer
2. Refer
Client
Client
1. 通话
1. 通话
RustPBX
RustPBX
Phone
Phone
4. 200 OK
4. 200 OK
Call center agent
Call center agent
3. Invite
3. Invite
转发
转发
5. 通话
5. 通话
Text is not SVG - cannot display

Hangup

After the call is established, use the Hangup command to hang up the call. Example:

{
"command": "hangup",
"reason": "Normal clearing"
}

Events

Throughout the process from call establishment to end, multiple events will be triggered, as shown:

Phone
Phone
UserAgent
UserAgent
INVITE
INVITE
BYE
BYE
4xx
4xx
200 OK
200 OK
200 OK
200 OK
Events
Events
Track Start Ringing Reject Answer Track End Hangup
180 Ringing
180 Ringing
Text is not SVG - cannot display

TrackStart

Create RTP/WebRTC Track

Ringing

Ringing event, the other party has received the call but hasn't answered yet.

Answer

Call established, the other party has answered.

Reject

The other party rejected, or other 4xx errors.

info

Request failure response code list: Request Failure 4xx

Hangup

After the call is established, the other party hangs up.

TrackEnd

RTP/WebRTC Track ended.

info

TTS/Play commands also create corresponding Tracks, and will also trigger TrackStart and TrackEnd events. Can be distinguished by TrackId.

See: TTS/Play.

Other Events

ASR and VAD functions will trigger corresponding events, including:

  • AsrDelta: Intermediate recognition result, content may change.
  • AsrFinal: Final recognition result, content is stable.
  • Speaking: Voice activity detection, voice input detected.
  • Silence: Voice activity detection, no voice input for a period of time.

For details, see: ASR and VAD.