How to control and steer calls with Call Automation

Important

This feature of Azure Communication Services is currently in preview.

Preview APIs and SDKs are provided without a service-level agreement. We recommend that you don't use them for production workloads. Some features might not be supported, or they might have constrained capabilities.

For more information, review Supplemental Terms of Use for Microsoft Azure Previews.

Call Automation uses a REST API interface to receive requests for actions and provide responses to notify whether the request was successfully submitted or not. Due to the asynchronous nature of calling, most actions have corresponding events that are triggered when the action completes successfully or fails. This guide covers the actions available for steering calls, like CreateCall, Transfer, Redirect, and managing participants. Actions are accompanied with sample code on how to invoke the said action and sequence diagrams describing the events expected after invoking an action. These diagrams will help you visualize how to program your service application with Call Automation.

Call Automation supports various other actions to manage call media and recording that aren't included in this guide.

Note

Call Automation currently doesn't interoperate with Microsoft Teams. Actions like making, redirecting a call to a Teams user or adding them to a call using Call Automation isn't supported.

As a pre-requisite, we recommend you to read the below articles to make the most of this guide:

  1. Call Automation concepts guide that describes the action-event programming model and event callbacks.
  2. Learn about user identifiers like CommunicationUserIdentifier and PhoneNumberIdentifier used in this guide.

For all the code samples, client is CallAutomationClient object that can be created as shown and callConnection is the CallConnection object obtained from Answer or CreateCall response. You can also obtain it from callback events received by your application.

var client = new CallAutomationClient("<resource_connection_string>"); 

Make an outbound call

You can place a 1:1 or group call to a communication user or phone number (public or Communication Services owned number). Below sample makes an outbound call from your service application to a phone number. callerIdentifier is used by Call Automation as your application's identity when making an outbound a call. When calling a PSTN endpoint, you also need to provide a phone number that will be used as the source caller ID and shown in the call notification to the target PSTN endpoint. To place a call to a Communication Services user, you'll need to provide a CommunicationUserIdentifier object instead of PhoneNumberIdentifier.

Uri callBackUri = new Uri("https://<myendpoint>/Events"); //the callback endpoint where you want to receive subsequent events 
var callerIdentifier = new CommunicationUserIdentifier("<user_id>");  
CallSource callSource = new CallSource(callerIdentifier); 
callSource.CallerId = new PhoneNumberIdentifier("+16044561234"); // This is the ACS provisioned phone number for the caller  
var callThisPerson = new PhoneNumberIdentifier("+16041234567"); 
var listOfPersonToBeCalled = new List<CommunicationIdentifier>();  
listOfPersonToBeCalled.Add(callThisPerson); 
var createCallOptions = new CreateCallOptions(callSource, listOfPersonToBeCalled, callBackUri); 
CreateCallResult response = await client.CreateCallAsync(createCallOptions); 

The response provides you with CallConnection object that you can use to take further actions on this call once it's connected. Once the call is answered, two events will be published to the callback endpoint you provided earlier:

  1. CallConnected event notifying that the call has been established with the callee.
  2. ParticipantsUpdated event that contains the latest list of participants in the call. Sequence diagram for placing an outbound call.

Answer an incoming call

Once you've subscribed to receive incoming call notifications to your resource, below is sample code on how to answer that call. When answering a call, it's necessary to provide a callback url. Communication Services will post all subsequent events about this call to that url.

string incomingCallContext = "<IncomingCallContext_From_IncomingCall_Event>"; 
Uri callBackUri = new Uri("https://<myendpoint_where_I_want_to_receive_callback_events"); 

var answerCallOptions = new AnswerCallOptions(incomingCallContext, callBackUri);  
AnswerCallResult answerResponse = await client.AnswerCallAsync(answerCallOptions);
CallConnection callConnection = answerResponse.CallConnection; 

The response provides you with CallConnection object that you can use to take further actions on this call once it's connected. Once the call is answered, two events will be published to the callback endpoint you provided earlier:

  1. CallConnected event notifying that the call has been established with the caller.
  2. ParticipantsUpdated event that contains the latest list of participants in the call.

Sequence diagram for answering an incoming call.

Reject a call

You can choose to reject an incoming call as shown below. You can provide a reject reason: none, busy or forbidden. If nothing is provided, none is chosen by default.

string incomingCallContext = "<IncomingCallContext_From_IncomingCall_Event>"; 
var rejectOption = new RejectCallOptions(incomingCallContext); 
rejectOption.CallRejectReason = CallRejectReason.Forbidden; 
_ = await client.RejectCallAsync(rejectOption); 

No events are published for reject action.

Redirect a call

You can choose to redirect an incoming call to one or more endpoints without answering it. Redirecting a call will remove your application's ability to control the call using Call Automation.

string incomingCallContext = "<IncomingCallContext_From_IncomingCall_Event>"; 
var target = new CommunicationUserIdentifier("<user_id_of_target>"); //user id looks like 8:a1b1c1-... 
var redirectOption = new RedirectCallOptions(incomingCallContext, target); 
_ = await client.RedirectCallAsync(redirectOption); 

To redirect the call to a phone number, set the target to be PhoneNumberIdentifier.

var target = new PhoneNumberIdentifier("+16041234567"); 

No events are published for redirect. If the target is a Communication Services user or a phone number owned by your resource, it will generate a new IncomingCall event with 'to' field set to the target you specified.

Transfer a 1:1 call

When your application answers a call or places an outbound call to an endpoint, that endpoint can be transferred to another destination endpoint. Transferring a 1:1 call will remove your application from the call and hence remove its ability to control the call using Call Automation.

var transferDestination = new CommunicationUserIdentifier("<user_id>"); 
var transferOption = new TransferToParticipantOptions(transferDestination);   
TransferCallToParticipantResult result = await callConnection.TransferCallToParticipantAsync(transferOption);

When transferring to a phone number, it's mandatory to provide a source caller ID. This ID serves as the identity of your application(the source) for the destination endpoint.

var transferDestination = new PhoneNumberIdentifier("+16041234567"); 
var transferOption = new TransferToParticipantOptions(transferDestination); 
transferOption.SourceCallerId = new PhoneNumberIdentifier("+16044561234"); 
TransferCallToParticipantResult result = await callConnection.TransferCallToParticipantAsync(transferOption);

The below sequence diagram shows the expected flow when your application places an outbound 1:1 call and then transfers it to another endpoint.

Sequence diagram for placing a 1:1 call and then transferring it.

Add a participant to a call

You can add one or more participants (Communication Services users or phone numbers) to an existing call. When adding a phone number, it's mandatory to provide source caller ID. This caller ID will be shown on call notification to the participant being added.

var addThisPerson = new PhoneNumberIdentifier("+16041234567"); 
var listOfPersonToBeAdded = new List<CommunicationIdentifier>(); 
listOfPersonToBeAdded.Add(addThisPerson); 
var addParticipantsOption = new AddParticipantsOptions(listOfPersonToBeAdded); 
addParticipantsOption.SourceCallerId = new PhoneNumberIdentifier("+16044561234");
AddParticipantsResult result = await callConnection.AddParticipantsAsync(addParticipantsOption); 

To add a Communication Services user, provide a CommunicationUserIdentifier instead of PhoneNumberIdentifier. Source caller ID isn't mandatory in this case.

AddParticipant will publish a AddParticipantSucceeded or AddParticipantFailed event, along with a ParticipantUpdated providing the latest list of participants in the call.

Sequence diagram for adding a participant to the call.

Remove a participant from a call

var removeThisUser = new CommunicationUserIdentifier("<user_id>"); 
var listOfParticipantsToBeRemoved = new List<CommunicationIdentifier>(); 
listOfParticipantsToBeRemoved.Add(removeThisUser); 
var removeOption = new RemoveParticipantsOptions(listOfParticipantsToBeRemoved); 
RemoveParticipantsResult result = await callConnection.RemoveParticipantsAsync(removeOption);

RemoveParticipant only generates ParticipantUpdated event describing the latest list of participants in the call. The removed participant is excluded if remove operation was successful.
Sequence diagram for removing a participant from the call.

Hang up on a call

Hang Up action can be used to remove your application from the call or to terminate a group call by setting forEveryone parameter to true. For a 1:1 call, hang up will terminate the call with the other participant by default.

_ = await callConnection.HangUpAsync(true); 

CallDisconnected event is published once the hangUp action has completed successfully.

Get information about a call participant

CallParticipant participantInfo = await callConnection.GetParticipantAsync("<user_id>")

Get information about all call participants

List<CallParticipant> participantList = (await callConnection.GetParticipantsAsync()).Value.ToList(); 

Get latest info about a call

CallConnectionProperties thisCallsProperties = callConnection.GetCallConnectionProperties();