Call Control

Make a Call

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

  • /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.

InviteFlow

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:

[ua]
addr = "0.0.0.0"
udp_port = 13050  # Avoid using 5060
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, Active Call will send a request to the Webhook address
  • WebHook service connects to Active Call based on the dialogId in the request
  • Send Accept command to answer, or Reject command to reject
AcceptFlow

Configure Webhook

Configure the Webhook address in config.toml:

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

Receive Notification

When there is an incoming call, Active Call 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"
}
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 Active Call

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 Active Call Go SDK, which can be referenced:


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)
}
See [Active Call Go SDK](/static/docs/active-call/sdk/go) for more information.
## Transfer {#refer}

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
  • Active Call calls the transfer target
  • After the target answers, Active Call forwards audio between both ends
ReferFlow

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:

CallEvent

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.

Request failure response code list: [Request Failure 4xx](https://datatracker.ietf.org/doc/html/rfc3261#section-21.4)

Hangup

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

TrackEnd

RTP/WebRTC Track ended.

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.