Edit

Answer Teams Phone calls from Call Automation

Use Azure Communication Services Call Automation to receive and answer calls for a Teams resource account.

Prerequisites

Associate your Teams resource account with your Communication Services resource

Execute the following command. Make sure you have your Azure Communication Services resource identifier ready. To find it, see Get an immutable resource identifier.

Set-CsOnlineApplicationInstance -Identity <appIdentity> -AcsResourceId <acsResourceId>

For more details, follow this guide: Associate your Azure Communication Services resource with the Teams Resource account

Configure your Communication Services resource to accept calls for the Teams resource account

Send a request to the Microsoft Teams Extension access assignments API to allow receiving calls for the Teams resource account. For more details on how to authenticate the web request, follow this guide: Authentication

The following example shows a request for a Teams Tenant with identifier 87d349ed-44d7-43e1-9a83-5f2406dee5bd and a Teams resource account oid with identifier e5b7f628-ea94-4fdc-b3d9-1af1fe231111.

PUT {endpoint}/access/teamsExtension/tenants/87d349ed-44d7-43e1-9a83-5f2406dee5bd/assignments/e5b7f628-ea94-4fdc-b3d9-1af1fe231111?api-version=2025-06-30

{
    "principalType" : "teamsResourceAccount"
}

The {principalType} needs to be teamsResourceAccount.

Response

The following example shows the response.

HTTP/1.1 201 Created
Content-type: application/json

{
    "objectId": "e5b7f628-ea94-4fdc-b3d9-1af1fe231111",
    "tenantId": "87d349ed-44d7-43e1-9a83-5f2406dee5bd",
    "principalType" : "teamsResourceAccount"
}

Stop accepting calls for the Teams resource account

Send a request to the Microsoft Teams Extension access assignments API to delete the entry for your Teams resource account.

DELETE {endpoint}/access/teamsExtension/assignments/e5b7f628-ea94-4fdc-b3d9-1af1fe231111?api-version=2025-06-30

Response

HTTP/1.1 204 NoContent
Content-type: application/json

{}

To verify that the Teams resource account is no longer linked with the Communication Services resource, you can send a GET request to the Microsoft Teams Extension access assignments API. Verify that its response status code is 404.

GET {endpoint}/access/teamsExtension/assignments/e5b7f628-ea94-4fdc-b3d9-1af1fe231111?api-version=2025-06-30

Receive and answer incoming calls

Set up and host your Azure DevTunnel

DevTunnels create a persistent endpoint URL which allows anonymous access. We use this endpoint to notify your application of calling events from the Azure Communication Services Call Automation service.

devtunnel create --allow-anonymous

devtunnel port create -p 8080

devtunnel host

Handle Incoming Call event and answer the call

app.MapPost("/api/incomingCall", async (
    [FromBody] EventGridEvent[] eventGridEvents,
    ILogger<Program> logger) =>
{
    foreach (var eventGridEvent in eventGridEvents)
    {
        if (eventGridEvent.TryGetSystemEventData(out object systemEvent))
        {
            switch (systemEvent)
            {
                case SubscriptionValidationEventData subscriptionValidated:
                    var responseData = new SubscriptionValidationResponse
                    {
                        ValidationResponse = subscriptionValidated.ValidationCode
                    };
                    return Results.Ok(responseData);

                case AcsIncomingCallEventData incomingCall:
                    var incomingCallContext = incomingCall.IncomingCallContext;
                    var callbackUri = new Uri(new Uri(devTunnelUri), $"/api/callbacks");
                    var options = new AnswerCallOptions(incomingCallContext, callbackUri);

                    AnswerCallResult answerCallResult = await callAutomationClient.AnswerCallAsync(options);
                    logger.LogInformation($"Answered call for connection id: {answerCallResult.CallConnection.CallConnectionId}");

                    //Use EventProcessor to process CallConnected event

                    var answerResult = await answerCallResult.WaitForEventProcessorAsync();
                    if (answerResult.IsSuccess)
                    {
                        logger.LogInformation($"Call connected event received for connection id: {answerResult.SuccessResult.CallConnectionId}");
                        var callConnectionMedia = answerCallResult.CallConnection.GetCallMedia();
                    }
                    return Results.Ok();

                default:
                    logger.LogInformation($"Received unexpected event of type {eventGridEvent.EventType}");
                    return Results.BadRequest();
            }
        }
    }
    return Results.Ok();
});

Sample Incoming Call event with Teams resource account identifier and custom context (VoIP and SIP)

{
  "to": {
    "kind": "unknown",
    "rawId": "28:orgid:cc123456-5678-5678-1234-ccc123456789"
  },
  "from": {
    "kind": "phoneNumber",
    "rawId": "4:+12065551212",
    "phoneNumber": {
      "value": "+12065551212"
    }
  },
  "serverCallId": "aHR0cHM6Ly9hcGkuZmxpZ2h0cHJveHkudGVhbXMubWljcm9zb2Z0LmNvbS9hcGkvdjIvZXAvY29udi11c3dlLTAyLXNkZi1ha3MuY29udi5za3lwZS5jb20vY29udi9fVERMUjZVS3BrT05aTlRMOHlIVnBnP2k9MTAtNjAtMTMtMjE2JmU9NjM4NTMwMzUzMjk2MjI3NjY1",
  "callerDisplayName": "+12065551212",
  "customContext":
  {
    "voipHeaders":
    {
        "X-myCustomVoipHeaderName": "myValue"
    },
  },
  "incomingCallContext": "<CALL_CONTEXT VALUE>",
  "correlationId": "2e0fa6fe-bf3e-4351-9beb-568add4f5315"
}

Next steps