Edit

Integrate telephony channels with a voice agent

Use Teams Phone extensibility (TPE) or Twilio to connect a phone number to your Microsoft Foundry voice agent. Receive inbound calls and place outbound calls through the call-job API.

Important

Items marked preview in this article are currently in preview. This preview is provided without a service-level agreement, and Microsoft doesn't recommend it for production workloads. Certain features might not be supported or might have constrained capabilities. For more information, see Supplemental Terms of Use for Microsoft Azure Previews.

Prerequisites

  • A saved voice-first agent in a Foundry project that passes browser testing. See Quickstart: Create a voice-first prompt agent.

  • Foundry User role on the project, to create and manage telephony bindings.

    Important

    The Foundry RBAC roles were recently renamed. Foundry User, Foundry Owner, Foundry Account Owner, and Foundry Project Manager were previously named Azure AI User, Azure AI Owner, Azure AI Account Owner, and Azure AI Project Manager. You might still see the previous names in some places while the rename rolls out. The role IDs and core permissions are unchanged by the rename.

  • Permission to update the agent and its channels, manage project connections, and create or update telephony bindings.

Teams Phone extensibility prerequisites

For the Teams Phone extensibility path, you need:

  • A Teams Phone service number assigned to a resource account. See Get Microsoft Teams Calling Plan phone numbers for your tenant.
  • The setup in Prepare Teams Phone extensibility, completed with a Teams or Microsoft 365 administrator.
  • Access to the selected Azure Communication Services resource.
  • For inbound calls, Event Grid write permission on that resource.
  • Help from a tenant administrator when secure webhook setup requires Microsoft Entra application or app-role changes.

The current project-managed-identity flow requires the Foundry project and Azure Communication Services resource to be in the same Microsoft Entra tenant.

Twilio prerequisites

For the Twilio path, you need:

  • A Twilio account with at least one active, voice-capable phone number for inbound calls.
  • A Twilio project connection, or the Account SID and Primary Auth Token to create one.

Twilio doesn't require an Azure Communication Services resource, Teams resource account, or Event Grid subscription. Store the Auth Token in the project connection, not in binding requests, logs, screenshots, or support requests.

Compare the channel integrations

Choose the provider that owns your number.

Path Use it when What you provide in Foundry
Teams Phone extensibility Your organization owns a service number in Teams and routes it through a Teams resource account to Azure Communication Services. An Azure Communication Services project connection and the resource-account object ID as a GUID, without the 28:orgid: prefix.
Twilio Your organization owns one or more active voice-capable numbers in Twilio. An existing Twilio project connection, or the Account SID and Primary Auth Token for a new connection.

The Twilio option appears only in supported public-cloud environments.

Understand the call path

The channel integration determines how an inbound call reaches the agent.

Caller
  -> Teams resource-account number or Twilio number
  -> telephony provider
  -> Foundry project telephony endpoint
  -> telephony binding
  -> voice-based agent

For Teams Phone extensibility, your Teams tenant keeps ownership of the number and Azure Communication Services provides the call automation and media path:

  1. A caller dials the Teams phone number.
  2. Teams routes the call to your resource account.
  3. The resource account routes the call to your Communication Services resource.
  4. Communication Services raises a Microsoft.Communication.IncomingCall event through Event Grid to your agent's telephony callback endpoint.
  5. Foundry matches the callee to a telephony binding, answers the call on your Communication Services resource, and streams the audio to the agent.

Because the Teams number stays in Teams, the agent can act as one destination among others in your existing call flow. You can leave auto attendants and call queues in place and route selected paths to the agent.

For Twilio, Foundry uses the selected project connection to discover the numbers in your Twilio account and create a binding for each number you select. You don't create an Azure Communication Services resource or Event Grid subscription for the Twilio path.

Prepare Teams Phone extensibility

A Teams or Microsoft 365 administrator usually completes these steps. Skip this section for Twilio.

  1. Follow Teams Phone System extensibility quick start to provision the application, bot, resource account, and Azure Communication Services association.
  2. Create or select the Azure Communication Services resource that receives calls for the resource account.
  3. Create the Teams resource account with the Phone Extensibility application ID.
  4. Associate the resource account with the Azure Communication Services resource, and synchronize the resource account.
  5. Acquire a Teams service number and assign it to the resource account. The number can use Calling Plan, Operator Connect, or Direct Routing according to your Teams telephony configuration.
  6. Assign the Microsoft Teams Phone Resource Account license when required.
  7. Provide Azure Communication Services server consent for the exact tenant and resource-account object ID.
  8. For inbound calls, place a controlled call and confirm that Azure Communication Services emits Microsoft.Communication.IncomingCall.

Provide the Foundry owner with the callable Teams number, Azure Communication Services ARM resource ID, and resource-account object ID. For inbound setup, also confirm that Azure Communication Services received the test call.

Set up your preferred interface

Choose Foundry portal, Python SDK, or REST. The Python examples share a client and variables across sections.

Open your project in the Foundry portal. You don't need the Python packages or a manually acquired bearer token for the portal steps.

Configure a provider connection

Use a project connection for your provider. The portal number setup flow can create it for you. For SDK or REST requests, create or select the connection first.

Create a connection to Communication Services

For Teams, use the category AzureCommunicationServices. Set target to the resource's HTTPS endpoint, such as https://<resource-name>.communication.azure.com, not its Azure resource ID. Store the matching resource ID in ResourceId metadata.

Choose an authentication type supported by the connection:

Authentication authType What you provide
Project managed identity ProjectManagedIdentity Grant the project identity access to the Communication Services resource.
Account managed identity AccountManagedIdentity Grant the Foundry account identity access to the resource.
User-assigned managed identity RegistryIdentity Identity resource ID and client ID, with access to the resource.
Service principal ServicePrincipal Tenant ID, client ID, and client secret, with access to the resource.
Connection string CustomKeys The Communication Services connection string in the connectionString credential key.

For a managed identity or service principal, grant the required Communication Services permissions before creating a binding or placing calls.

Configure the Twilio project connection

For Twilio, use these settings:

Setting Value
category Twilio.
target Your Twilio Account SID, not a Twilio API URL or phone number.
authType ApiKey.
Credential key Your Twilio Primary Auth Token.

Use the connection's name as connection_name in binding and outbound requests. Don't include provider credentials in those requests.

Understand telephony bindings

A binding routes inbound calls to one agent. You don't need a binding for outbound call jobs.

Property Purpose
id Service-generated binding ID for later reads, updates, and deletion.
provider teams_phone_extension or twilio.
connection_name Foundry project connection name. Secrets stay in the connection.
resource_account_object_id Required Teams resource-account GUID, without 28:orgid:. Don't send this property for Twilio.
phone_number Optional display metadata for Teams; required owned E.164 number for Twilio.
status active or suspended. Suspension stops new inbound calls without deleting the binding.
label Optional display label.
incoming_call_url Service-generated incoming-call delivery URL.

Create and list bindings at {projectEndpoint}/agents/{agentName}/telephony/bindings. Append /{bindingId} to read, update, or delete one. Use the latest ETag for conditional updates and deletion.

Creating a Twilio binding updates the number's voice URL and status callback, replacing its existing inbound voice routing. Use a number you want to route to this agent.

Connect a number in the Foundry portal

The portal provides separate setup flows for Teams Phone extensibility and Twilio. Select Microsoft Teams for the Teams Phone extensibility flow.

Open the phone-number channel

  1. In Foundry, open Build > Agents.
  2. Select the voice-based agent.
  3. Open Channels.
  4. In Phone numbers, select Add a number.
  5. Select Microsoft Teams or Twilio.

Connect a Microsoft Teams number

The Teams Phone extensibility flow creates or updates the Azure Communication Services project connection and telephony binding. Depending on the secure-delivery option, Foundry can also configure the webhook application and IncomingCall Event Grid subscription.

Choose secure incoming-call delivery

When Secure incoming-call delivery is shown, choose the approved mode.

Option Use it when
Automatic configuration (Recommended) Use when the tenant allows Foundry to create or update the webhook app, app-role assignments, and Event Grid subscription.
Manual configuration Use an approved customer-managed application, or select an existing Event Grid and app-registration configuration.

Under Manual configuration, choose the option that matches your setup:

  • Use a customer-managed app registration and let Foundry configure Event Grid: Use a dedicated single-tenant application that you own, or ask an administrator to complete the changes.
  • Event Grid and app registration are already configured: Use only when authenticated Event Grid delivery to the exact Foundry webhook is complete. Foundry skips that bootstrap work.

For a customer-managed application:

  1. Select an eligible single-tenant app registration.
  2. Enter any governance reference required by your organization.
  3. If the application isn't listed, confirm that you're an owner and refresh.
  4. Ask a tenant administrator to complete required API permissions and app-role assignments.

Never disable authentication to work around a webhook validation error.

Select Azure Communication Services

In Azure Communication Services resource, select the resource that receives the calls.

If it isn't listed:

  • Confirm the tenant and subscription.
  • Confirm you can read the resource.
  • Confirm the expected resource provider is registered.
  • Ask an Azure administrator to correct RBAC.

Foundry creates or updates the project connection to the Azure Communication Services endpoint and uses the project managed identity. Don't enter an Azure Communication Services key or connection string.

Enter the Teams number details

Enter the Resource account object ID that the Teams administrator provides. Foundry constructs:

28:orgid:<resource-account-object-id>

Connect a Twilio number

  1. In Phone numbers, select Add a number > Twilio.
  2. Select a compatible existing Twilio connection. If none is available, select Create a new Twilio connection.
  3. To create a connection, enter:
    • A unique Connection name.
    • The Twilio Account SID. It starts with AC and contains 34 characters.
    • The Twilio Primary Auth Token.
  4. Select Connect or Continue.
  5. Select one or more voice-capable phone numbers discovered from the Twilio account. You can't select numbers without the Voice capability.
  6. Select Continue.
  7. Review the agent, connection, masked Account SID, and selected numbers.
  8. Optionally change the display label for each number.
  9. Select Add selected numbers.
  10. Review the result for each number. Retry any number that has a failed or unconfirmed result.
  11. Select Done after all binding operations finish.

Foundry stores the Twilio credentials in the project connection and creates a separate telephony binding for each selected number.

If you don't find any voice-capable numbers, get a number in the Twilio Console and retry discovery.

Manage a connected number

The Phone numbers card shows each connected number, its optional label, and its provider.

  1. Open the number's actions menu.
  2. Select View details.
  3. Select Edit if the binding supports updates.
  4. For Microsoft Teams, update the displayed phone number or label. The resource-account object ID and connection are read-only.
  5. For Twilio, update the label. The phone number and connection are read-only.
  6. Select Save.

Binding update rules

Set status to active to accept new inbound calls again. Both providers support updates to status and label.

Teams bindings also support changes to connection_name and the display phone_number. The provider and resource-account object ID are immutable. For Twilio, delete and recreate the binding to change its phone number or connection.

Register the Event Grid subscription

This section applies only to Teams inbound calls, not Twilio or outbound-only setup. Create an Event Grid subscription on the Communication Services resource that targets the binding's incoming_call_url. If the portal already configured secure delivery, don't create a duplicate subscription.

Before running the Bash example, sign in with az login and set these variables:

Variable Value
eventSubscriptionName A name for the Event Grid event subscription.
acsResourceId The Azure resource ID of the Communication Services resource.
webhook The incoming_call_url returned when you create the binding.
webhookTenantId The tenant ID configured for the webhook application.
webhookApplicationIdOrUri The webhook application's client ID or Application ID URI.

Complete the application and role setup in Deliver events to Microsoft Entra protected endpoints first. Obtain the webhook tenant and audience from the administrator who configures secure incoming-call delivery. The webhook application identifies the token audience; it isn't the Microsoft Event Grid first-party application.

az eventgrid event-subscription create \
  --name "$eventSubscriptionName" \
  --source-resource-id "$acsResourceId" \
  --endpoint-type webhook \
  --endpoint "$webhook" \
  --included-event-types Microsoft.Communication.IncomingCall \
  --event-delivery-schema eventgridschema \
  --azure-active-directory-tenant-id "$webhookTenantId" \
  --azure-active-directory-application-id-or-uri "$webhookApplicationIdOrUri"

Reference: az eventgrid event-subscription create.

During creation, Event Grid sends a validation event that the endpoint answers automatically.

Foundry validates the Microsoft Entra token that Event Grid attaches to each delivery, and then requires an active binding whose project, agent, Communication Services resource, and callee all match the event. Calls that don't match a binding aren't answered.

Event Grid allows up to 500 event subscriptions per system topic. Plan incoming-call delivery within the Event Grid quotas and limits.

Place outbound calls by using the API

Create an outbound call job to call a phone number through a project connection. Creating a call job doesn't change inbound routing.

Complete Set up your preferred interface before running the Python or REST examples. Use a destination you have permission to call.

Create or select the provider connection, but skip the inbound binding and Event Grid procedures for outbound-only use.

Set the outbound caller and destination

The connection category selects the provider. Use these required settings:

Field Teams Phone extensibility Twilio
connection_name The name of an AzureCommunicationServices connection in the current project. The name of a Twilio connection in the current project.
source The Teams resource-account object ID as a nonzero GUID. Don't include 28:orgid: or use the account's display phone number. An E.164 caller number authorized for the connected Twilio account.
destination {"type": "phone_number", "value": "+14255550123"} with the recipient's E.164 number. The same phone-number destination shape.

Supply a connection name, not a resource ID or URL. Don't send provider, telephony_binding_id, or the legacy connection field in an outbound request.

Generate an idempotency key of 1 to 256 characters for each new call intent. Reuse it with an equivalent request when retrying an uncertain response. The service returns the same job; reusing the key with a different request returns 409 Conflict.

Creation returns 202 Accepted with the job's id, ETag, Location, and Retry-After in seconds. Acceptance doesn't mean the recipient answered or the call completed.

The service resolves the latest saved agent definition for each attempt. Provide any required agent inputs in structured_inputs.

This section covers outbound calls through the API. Select Python SDK or REST to create, read, or cancel a call job. You can use a project connection created in the portal without creating an inbound binding.

Interpret the job status

Inspect status, attempt_count, and terminal_reason. Terminal statuses are completed, blocked, expired, failed, and cancelled. Interpret the reason with the status: a queued job can retain a temporary dispatch-deferral reason.

Cancellation returns 202 Accepted while pending, or 200 OK when the job is terminal. Continue reading the job until it reaches a terminal status. Cancellation doesn't hang up a connected call; a cancellation request after connection can return 409 Conflict.

A job ID isn't a live call ID. Don't pass it to /telephony/calls/{callId}.

Optional outbound settings

Use these settings in the Python request model or REST JSON body. The SDK converts Python date and duration objects to the REST wire format.

Optional setting Usage
purpose A description of why you're placing the call.
structured_inputs Values available to the agent and its greeting. Declared inputs must match their schemas.
schedule.not_before The earliest dispatch time. Use a timezone-aware datetime.datetime in Python or a Unix timestamp in seconds in REST.
schedule.expires_at The time after which the job expires without dispatch. Use a timezone-aware datetime.datetime in Python or a Unix timestamp in seconds in REST.
retry_policy Use a fixed_interval policy with max_attempts from 1 through 5, including the first attempt. Use datetime.timedelta for interval in Python or seconds in REST. Omit the policy for one attempt.

For Python, use TelephonyCallJobSchedule and TelephonyOutboundFixedIntervalRetryPolicy to construct these optional values.

Configure transfer to a person

An agent that can't complete a request should reach a human rather than end the call. Configure named transfer targets for the agent. These targets are separate from its telephony bindings.

The examples use PSTN targets, which work with either provider. Use destinations approved for your call flow.

Select Python SDK or REST for the transfer-target configuration examples.

Use these destination kinds for provider-specific routing:

Destination kind Value Supported provider
pstn E.164 phone number. Teams or Twilio.
teams Teams user or resource-account GUID, optionally prefixed with 28:orgid:. Teams through Azure Communication Services.
sip SIP or SIPS URI. Twilio.

There's no twilio destination kind. For Python, use PSTNTelephonyTransferDestination, TeamsTelephonyTransferDestination, or SipTelephonyTransferDestination for the corresponding destination.

Give each target a description that says when to use it. The agent chooses based on that text.

Transfer requests select a target from the agent's configured list rather than supplying an arbitrary destination.

Manage a live call

These operations list and manage inbound calls. Use a live call ID, not an outbound job ID. Complete Set up your preferred interface before running the examples.

Select Python SDK or REST to list calls or send a transfer or end-call request.

Configure audio for phone calls

Phone networks carry narrowband audio, so tune the agent for the channel:

  • Set noise_reduction.type to azure_deep_noise_suppression for contact center traffic.
  • Increase silence_duration_ms. Callers on a phone pause more than callers at a keyboard.
  • Add phrase_list hints for the identifiers callers read aloud, such as order or policy numbers.
  • Attach the end_conversation system tool so the agent can end a completed call.

See Configure a voice agent.

Test the call

  1. Wait for the selected provider and binding changes to propagate. For Microsoft Teams, also allow time for Azure Communication Services and Event Grid propagation.
  2. From a controlled caller, call the configured number.
  3. Confirm the agent answers.
  4. Complete a short conversation.
  5. Interrupt the agent.
  6. Use one safe tool or knowledge path.
  7. Test the approved human-handoff behavior.
  8. End the call.
  9. Confirm that monitoring and trace data appear.

Record:

  • Test timestamp and time zone.
  • Called number.
  • Callee identifier.
  • Telephony provider and provider call ID, such as an Azure Communication Services correlation ID or Twilio Call SID.
  • Agent name and version.
  • For Microsoft Teams, the Azure Communication Services resource ID and Event Grid delivery result.
  • For Twilio, the Foundry project connection name.

For an outbound test, create a job for an approved test recipient and record the job ID, status, attempt count, and reason. Don't treat 202 Accepted as proof that the call connected.

Trace phone calls

When a call arrives through telephony, the session's root trace span records the provider, the provider's call ID, and the dialed and calling numbers. You can correlate a Foundry session with a record in your telephony provider.

Caller and callee phone numbers are personal data. Review who can read your project's Application Insights resource before you enable content capture. See Voice agent tracing, monitoring, and evaluation.

Troubleshoot telephony

Symptom What to check
Busy signal and no Azure Communication Services event Teams number activation, license, PSTN connectivity, calling bot channel, TPE association, synchronization, and Azure Communication Services consent.
Azure Communication Services receives the call but Foundry reports no binding Compare IncomingCall.data.to.rawId with 28:orgid:<resource_account_object_id> constructed from the binding's GUID. Keep the prefix out of the REST binding field.
Event Grid validation fails Endpoint, tenant, webhook audience, app-role assignment, delivery identity, and Event Grid write access.
Secure setup reports a Graph permission error Ask a tenant administrator to grant the approved application-management permissions and retry.
Azure Communication Services AnswerCall returns 401 or 403 Project connection target, project managed identity, Azure Communication Services role assignment, and RBAC propagation.
Call connects but is silent Media endpoint reachability, selected model and voice, output device path, and required PCM media format.
Call disconnects unexpectedly Callback reachability, media errors, service limits, and correlation logs.
The wrong agent answers Confirm the agent that owns the binding collection and inspect that agent's active version.
The number doesn't appear after refresh Query active bindings and confirm provisioning completed; don't rely only on temporary browser state.
Twilio numbers don't load Check the connection's Twilio category, Account SID target, and ApiKey credential containing the current Primary Auth Token. Confirm that you can read the connection credentials.
No Twilio numbers can be selected Confirm the Twilio account owns an active number with the Voice capability.
Twilio rejects the credentials Update or recreate the project connection with the current Account SID and Primary Auth Token.
A Twilio number is already bound Use the existing binding or disconnect it from the other agent before retrying.
A Twilio number doesn't reach the agent Check that the number is active and voice-capable, its binding is active, and its voice URL points to Foundry. Review webhook delivery errors in Twilio.
A binding update, deletion, or transfer-target replacement fails with a precondition error Read the resource again, review any concurrent changes, and use the latest ETag in If-Match.
Outbound creation returns 400 Bad Request Check connection_name, the provider-specific source, the E.164 destination, required structured inputs, and the Idempotency-Key header.
Outbound creation returns 409 Conflict The idempotency key was used with a different request. Reuse the original request to recover its job, or use a new key for a new call intent.
Outbound creation returns 503 Service Unavailable with a dispatch-not-configured message Outbound dispatch isn't configured for the service instance. Contact support to confirm availability; creating an inbound binding doesn't enable dispatch.
Call-job cancellation returns 409 Conflict Read the latest job and ETag. Retry with the current ETag only if the call hasn't connected and cancellation is still appropriate.
An outbound job is accepted but the destination doesn't ring Read the job's status, attempt count, schedule, and reason. Check caller authorization and provider connectivity; 202 Accepted isn't confirmation of a connected call.

Disconnect a number

Remove the inbound mapping when you no longer want the number to route calls to this agent. Outbound jobs don't require a binding; cancel any unwanted jobs separately.

  1. Open the agent Channels tab.
  2. In Phone numbers, open the number's actions menu.
  3. Select Disconnect number.
  4. Confirm.

Disconnecting removes the Foundry binding. It doesn't:

  • Release the Teams or Twilio number.
  • Remove the Teams resource account.
  • Delete Azure Communication Services.
  • Remove the bot or app registration.
  • Revoke Teams Phone extensibility consent.
  • Delete the Twilio account or the Foundry project connection.

Coordinate upstream cleanup separately. For Twilio, review the number's voice routing before using it with another application.

Security checklist

  • Use Microsoft Entra authentication for Foundry API requests. Prefer managed identity for the Azure Communication Services connection.
  • Don't share Azure Communication Services keys, connection strings, tokens, bot secrets, or portal cookies.
  • Store the Twilio Auth Token in the project connection. If it's exposed, rotate it and update the connection.
  • Confirm that the Twilio account owns each inbound number and authorizes each outbound caller number.
  • For Teams inbound calls, validate that the Event Grid topic is the intended Azure Communication Services resource.
  • Use the exact public Foundry project webhook endpoint.
  • Keep webhook audience and Event Grid delivery identity concepts separate.
  • Use a dedicated single-tenant webhook application when governance requires it.
  • Don't persist service-generated call callback or media tokens.
  • Confirm the binding target before update or deletion.
  • Apply recording, consent, disclosure, retention, and privacy requirements.
  • Prevent sensitive caller data from being read aloud or unnecessarily stored.

Use these guides to configure and validate the agent behind your telephony channel: