Azure Communications Gateway Provisioning API
Azure Communications Gateway's Provisioning API for telecommunications operators allows you to provision the details of your customers and the numbers assigned to them into Azure Communications Gateway (ACG). The Provisioning API also supports flow-through provisioning of some backend communications services.
Provisioning customers and numbers is mandatory (with the Provisioning API or the browser-based Number Management Portal) for all use cases except for Operator Connect and Teams Phone Mobile. For Operator Connect and Teams Phone Mobile, use of the Provisioning API and/or Number Management Portal is optional and you can integrate directly with the Operator Connect API instead.
Getting started
Prerequisites
- A tenant with the Azure Communications Gateway application deployed.
- The fully qualified domain name (FQDN) for your Azure Communications Gateway which is displayed on the Overview page for the resource in the Azure portal. The API is available on port 443 of
provapi.<base-domain>
. - A machine with an IP address that allows access to the API, as configured in an allowlist as part of deploying Azure Communications Gateway.
Authentication and authorization
The Provisioning API uses OAuth 2.0 to control access to resources. The client application must obtain a valid authentication bearer token to access the Provisioning API. The bearer token indicates that the application is authorized for one or more of the scopes (roles) for the Provisioning API. We recommend using the client credentials flow (designed for a server-side process).
The following scopes are available for the Provisioning API:
ProvisioningAPI.Admin
: Ability to invoke any operation across the API.ProvisioningAPI.Read
: Ability to invoke any read (GET) operation across the API.ProvisioningAPI.Write
: Ability to invoke any write (PUT, PATCH) operation across the API.ProvisioningAPI.Delete
: Ability to invoke any delete (DELETE) operation across the API.
To set up a client credentials flow:
- Ensure your application can support the client credentials flow.
When your application requests a token for the Provisioning API, it must use the following fields.
Parameter Condition Description tenant required The directory tenant containing the Azure Communications Gateway, in guid or domain-name form. scope required The scope of authorization against the Azure Communications Gateway resource ID. For the client credentials flow described here, the scope should be https://func-voiceservice-rp-prod-eastuseuap.azurewebsites.net/.default
.client_id required The application (client) ID assigned to your app. The
roles
claim in the received token specifies the roles (scopes) that the client application is authorized to access.Requests to the Azure Communications Gateway Provisioning Platform must have an
Authorization
header with this bearer token.See the client credentials flow documentation for examples of using tokens.
- Use the Azure portal to register the application in the same tenant as your Azure Communications Gateway deployment. See Quickstart: Register an app in the Microsoft identity platform - Microsoft Entra | Microsoft Learn.
- Assign yourself as an owner for the app registration. See Assign application owner.
- Configure the app registration created by registering the application with app roles that use the scopes for the Provisioning API, as described earlier.
- See Assign app roles to applications.
- The API for which you need to assign permissions is
AzureCommunicationsGateway
, listed under APIs my organization uses.
- As an administrator for the tenant, allow the application to use the app roles that you assigned. See Grant admin consent.
The Provisioning API uses standard Microsoft chains of trust for security certificates.
Key concepts
The Provisioning Platform has three key resources that the operator can manage: accounts, numbers, and requests for information.
- Account resources are descriptions of operator customers (typically, an enterprise), and per-customer settings for service provisioning.
- Number resources belong to an account. They describe numbers, the services (for example, Microsoft Teams Direct Routing) that the numbers make use of, and any extra per-number configuration.
- Request for Information (RFI) resources are descriptions of potential customers for operators who are expressing interest in receiving service from the operator through specific backend services. Currently only RFIs produced from Operator Connect and Teams Phone Mobile consents are available.
For example, to provide Microsoft Teams Direct Routing service to a customer, Contoso, create an account resource with the Provisioning API for Contoso. The account contains configuration for Direct Routing (for example, a subdomain and corresponding tokens, needed to set up DNS records that Microsoft Teams can use to validate the customer’s configuration). You must then add number resources to the account, and enable each number for Direct Routing.
Tip
You must enable service on both the account and numbers within the account.
Provisioning backend services with backend service sync
Azure Communications Gateway must have information about the numbers it's providing service to in order to properly connect calls. We recommend the Azure Communications Gateway Provisioning API for providing this information to Azure Communications Gateway, but you can also use the Number Management Portal. Most backend services also need to be provisioned with information about the numbers and accounts to be used. This requirement often means that multiple IT integration projects are needed to enable new services. The Azure Communications Gateway Provisioning platform has been preintegrated with some backend services to provision them for you, reducing your IT integration requirements. You can use this function by enabling backend service sync for the relevant services. This also means that any IT integration to the Azure Communications Gateway provisioning platform is reusable for other backend services.
For example Operator Connect mandates that all numbers are uploaded via the Operator Connect API. If backend service sync is enabled for Operator Connect, any number provisioned onto Azure Communications Gateway and enabled for Operator Connect will automatically be provisioned into Operator Connect, meaning you don't need to integrate with the Operator Connect API.
Provisioning via the Azure Communications Gateway provisioning platform is optional for some services where Azure Communications Gateway can obtain information directly from the backend. However, some features such as the addition of customer SIP headers for billing purposes won't be available. For any services not supporting backend service sync, other IT integration may be required directly to the backend service. The status of provisioning support is described in the following table:
Backend Service | Requirement to provision via ACG Provisioning Platform | Provisioning of backend service supported |
---|---|---|
Direct Routing | Mandatory | ❌ |
Zoom | Mandatory | ❌ |
Azure Operator Call Protection | Mandatory | ❌ |
Operator Connect | Optional | ✅ |
Teams Phone Mobile | Optional | ✅ |
Sync to backend services is asynchronous, meaning that your provisioning request may succeed before the backend service has been provisioned. This status is signified in the API response using the serviceProvisioningStatus
field set to pending
. We recommend that you query the object to check its provisioning status until this field is set to success
. Any errors from provisioning the backend system are made available in the response directly.
Examples
The following examples show sample requests for managing RFIs, accounts, and numbers.
Create an account representing a customer
Use PUT on the accounts/<accountName>
endpoint to create an account named contoso
for the customer Contoso and configure one or more communications services for the account. Use an If-None-Match header to verify that an account resource with this name doesn't already exist.
In the following example:
- Direct Routing is configured.
- Caller ID screening is enabled (the default).
- The subdomain for the customer is
contoso
. - The customer-provided DNS TXT values needed to set up DNS records are in the
region1Token
andregion2Token
fields.
Request:
PUT /accounts/contoso?api-version=2024-02-29 HTTP/1.1
{
"name": "contoso",
"serviceDetails": {
"teamsDirectRouting": {
"syncEnabled": true,
"enabled": true,
"configuration": {
"callScreening": true,
"subdomain": "contoso",
"subdomainTokens": {
"region1Token": "region1TokenValue",
"region2Token": "region2TokenValue"
}
}
}
}
}
Response:
{
"serviceProvisioningStatus": "synced",
"serviceProvisioningErrors": null,
"name": "contoso",
"serviceDetails": {
"teamsDirectRouting": {
"syncEnabled": true,
"enabled": true,
"numberCount": 0,
"configuration": {
"callScreening": true,
"subdomain": "contoso",
"subdomainTokens": {
"region1Token": "region1TokenValue",
"region2Token": "region2TokenValue"
}
},
"subdomainStatus": "provisioned"
},
}
}
In the following example, we create an account for use with only Teams Operator Connect, with backend sync enabled so that information about this account (such as any numbers uploaded) are also provisioned into Teams:
Request:
PUT /accounts/contoso?api-version=2024-02-29 HTTP/1.1
{
"name": "contoso",
"serviceDetails": {
"teamsTenantId": "tenantIdString",
"teamsOperatorConnect": {
"syncEnabled": true,
"enabled": true
},
}
}
Response:
{
"serviceProvisioningStatus": "pending",
"serviceProvisioningErrors": null,
"name": "contoso",
"serviceDetails": {
"teamsTenantId": "tenantIdString",
"teamsOperatorConnect": {
"syncEnabled": true,
"enabled": true,
"numberCount": 0
}
}
}
View the details of the account
Use GET on the accounts/<accountName>
endpoint to get the details of the account. The response includes the following fields:
- All configuration previously set (or the default, if a field wasn't set).
- Subscriber counts for each of the services available on ACG.
- The status of backend service provisioning, if enabled.
subdomainStatus
, representing the state of DNS record provisioning, only relevant for Direct Routing.- An
ETag
header representing the current state of the account. You can use the value in anIf-Match
header on subsequent update requests to ensure you don't overwrite changes made by other API users.
Request:
GET /accounts/contoso?api-version=2024-02-29 HTTP/1.1
Response:
ETag: 12345
{
"serviceProvisioningStatus": "synced",
"serviceProvisioningErrors": null,
"name": "contoso",
"serviceDetails": {
"teamsTenantId": "tenantIdString",
"teamsOperatorConnect": {
"syncEnabled": true,
"enabled": true,
"numberCount": 0
},
}
}
The equivalent request if the account has multiple services configured appears as follows:
Request:
GET /accounts/contoso?api-version=2024-02-29 HTTP/1.1
Response:
ETag: 12345
{
"serviceProvisioningStatus": "synced",
"serviceProvisioningErrors": null,
"name": "contoso",
"serviceDetails": {
"teamsTenantId": "tenantIdString",
"teamsOperatorConnect": {
"syncEnabled": true,
"enabled": true
},
"teamsDirectRouting": {
"syncEnabled": true,
"enabled": true,
"numberCount": 0,
"configuration": {
"callScreening": true,
"subdomain": "contoso",
"subdomainTokens": {
"region1Token": "region1",
"region2Token": "region2"
}
},
"subdomainStatus": "provisioned"
},
}
}
Update the configuration for the account
Use PUT on the accounts/<accountName>
endpoint to update the configuration for the account. To ensure that the update doesn't overwrite a change made by another user, add an If-Match
header with the ETag from the most recent response for the account.
Request:
PUT /accounts/contoso?api-version=2024-02-29 HTTP/1.1
ETag: 12345
{
"name": "contoso",
"serviceDetails": {
"teamsTenantId": "tenantIdString",
"teamsOperatorConnect": {
"syncEnabled": false,
"enabled": true
},
"teamsDirectRouting": {
"syncEnabled": true,
"enabled": true,
"numberCount": 0,
"configuration": {
"callScreening": true,
"subdomain": "contoso",
"subdomainTokens": {
"region1Token": "region1",
"region2Token": "region2"
}
},
"subdomainStatus": "provisioned"
},
}
}
Response:
ETag: 56789
{
"serviceProvisioningStatus": "pending",
"serviceProvisioningErrors": null,
"name": "contoso",
"serviceDetails": {
"teamsTenantId": "tenantIdString",
"teamsOperatorConnect": {
"syncEnabled": false,
"enabled": true
},
"teamsDirectRouting": {
"syncEnabled": true,
"enabled": true,
"numberCount": 0,
"configuration": {
"callScreening": true,
"subdomain": "contoso",
"subdomainTokens": {
"region1Token": "region1",
"region2Token": "region2"
}
},
"subdomainStatus": "provisioned"
},
}
}
Add one number to the account
Use PUT on the account/<accountName>/numbers/<telephoneNumber>
endpoint to add a number to the account, enable one or more communications services and add any other configuration. The chosen communications services must also be configured on the account. Use an If-None-Match header to verify that a number resource with this number doesn't already exist. All numbers must be created in E.164 format.
In the following example:
- The number is +123451.
- Operator connect is enabled.
- The configuration required to upload the number to Operator Connect is provided
customSipHeader
specifies that Azure Communications Gateway should add a header with the valueexampleHeaderContents
to messages sent to the operator network. The name of the header is set as part of deploying Azure Communications Gateway.- The
serviceProvisioningStatus
field in the response shows the status of the sync to the backend service.
PUT /accounts/contoso/numbers/+123451?api-version=2024-02-29 HTTP/1.1
{
"telephoneNumber": "+123451",
"accountName": "contoso",
"serviceDetails": {
"teamsOperatorConnect": {
"enabled": true,
"configuration": {
"usage": "CallingUserAssignment",
"choosableCapabilities": [
"InboundCalling",
"OutboundCalling"
],
"civicAddressId": "civicAddressIdString",
"allowTenantAddressUpdate": true,
}
},
},
"configuration": {
"customSipHeader": "exampleHeaderContents"
}
}
Response:
{
"serviceProvisioningStatus": "pending",
"serviceProvisioningErrors": null,
"telephoneNumber": "+123451",
"accountName": "contoso",
"serviceDetails": {
"teamsOperatorConnect": {
"enabled": true,
"assignmentStatus": "assigned",
"configuration": {
"usage": "CallingUserAssignment",
"choosableCapabilities": [
"InboundCalling",
"OutboundCalling"
],
"civicAddressId": "civicAddressIdString",
"allowTenantAddressUpdate": true,
}
},
},
"configuration": {
"customSipHeader": "exampleHeaderContents"
}
}
Checking provisioning status after some time
Use GET on the account/<accountName>/numbers/<telephoneNumber>
after a provisioning action to check the status of the number. If the number was successfully provisioned, the serviceProvisioningStatus
field updates from pending
to synced
.
Request:
GET /accounts/contoso/numbers/+123451?api-version=2024-02-29 HTTP/1.1
Response:
{
"serviceProvisioningStatus": "synced",
"serviceProvisioningErrors": null,
"telephoneNumber": "+123451",
"accountName": "contoso",
"serviceDetails": {
"teamsOperatorConnect": {
"enabled": true,
"assignmentStatus": "assigned",
"configuration": {
"usage": "CallingUserAssignment",
"choosableCapabilities": [
"InboundCalling",
"OutboundCalling"
],
"civicAddressId": "civicAddressIdString",
"allowTenantAddressUpdate": true,
}
},
},
"configuration": {
"customSipHeader": "exampleHeaderContents"
}
}
Error in backend service provisioning for uploading a number
In this example, the backend provisioning when uploading the number hits an error, which is reflected back on the response. Error messages are passed through transparently from backend services.
Note
Initially when provisioning a number it has pending
status, which needs to be queried again to confirm success/failure.
The original request, which is missing a value for the usage
field:
PUT /accounts/contoso/numbers/+123451?api-version=2024-02-29 HTTP/1.1
{
"telephoneNumber": "+123451",
"accountName": "contoso",
"serviceDetails": {
"teamsOperatorConnect": {
"enabled": true,
"configuration": {
"usage": "",
"choosableCapabilities": [
"InboundCalling",
"OutboundCalling"
],
"civicAddressId": "civicAddressIdString",
"allowTenantAddressUpdate": true,
}
},
},
"configuration": {
"customSipHeader": "exampleHeaderContents"
}
}
Response from GET query after some time:
{
"serviceProvisioningStatus": "failed",
"serviceProvisioningErrors": [
{
"code": "InvalidRequest",
"message": "Invalid/missing required configuration attributes: Usage",
"target": "oc",
}
],
"telephoneNumber": "+123451",
"accountName": "contoso",
"serviceDetails": {
"teamsOperatorConnect": {
"enabled": true,
"assignmentStatus": "assigned",
"configuration": {
"usage": "",
"choosableCapabilities": [
"InboundCalling",
"OutboundCalling"
],
"civicAddressId": "civicAddressIdString",
"allowTenantAddressUpdate": true,
}
},
},
"configuration": {
"customSipHeader": "exampleHeaderContents"
}
Update configuration for a number
Use PUT on the account/<accountName>/numbers/<telephoneNumber>
endpoint to update configuration for a number. To ensure that the update doesn't overwrite a change made by another user, add an If-Match header with the ETag from the most recent response for the number.
Request:
PUT /accounts/contoso/numbers/+123451?api-version=2024-02-29 HTTP/1.1
ETag: 123
{
"telephoneNumber": "+123451",
"accountName": "contoso",
"serviceDetails": {
"teamsOperatorConnect": {
"enabled": true,
"configuration": {
"usage": "CallingUserAssignment",
"choosableCapabilities": [
"InboundCalling",
"OutboundCalling",
"Mobile"
],
"civicAddressId": "civicAddressIdString",
"allowTenantAddressUpdate": true,
}
},
},
"configuration": {
"customSipHeader": "exampleHeaderContents"
}
}
Response:
{
"serviceProvisioningStatus": "pending",
"serviceProvisioningErrors": null,
"telephoneNumber": "+123451",
"accountName": "contoso",
"serviceDetails": {
"teamsOperatorConnect": {
"enabled": true,
"assignmentStatus": "assigned",
"configuration": {
"usage": "CallingUserAssignment",
"choosableCapabilities": [
"InboundCalling",
"OutboundCalling",
"Mobile"
],
"civicAddressId": "civicAddressIdString",
"allowTenantAddressUpdate": true,
}
},
},
"configuration": {
"customSipHeader": "exampleHeaderContents"
}
}
List the Requests for Information
Use a GET on the /teamsRequestsForInformation
endpoint to obtain a list of the Teams consents that have been submitted to you by potential customers.
Request:
GET /teamsRequestsForInformation?api-version=2024-02-29 HTTP/1.1
Response:
{
"value": [
{
"serviceProvisioningStatus": "synced",
"serviceProvisioningErrors": null,
"id": "contoso",
"tenantId": "contosoTenantId",
"accountName": "contoso",
"productContext": "teams",
"operatorId": "string",
"status": "active",
"consentedOn": "2024-05-07T11:15:10.519Z",
"lastModifiedOn": "2024-05-07T11:15:10.519Z",
"consentedCountries": [
"string"
],
"contacts": [
{
"fullName": "Example Name",
"email": "example@contoso.com",
"telephoneNumber": "+1234567890",
"companyName": "contoso",
"companySize": "size"
}
],
"customerRelationship": {
"status": "example status",
"lastModifiedOn": "2024-05-07T11:15:10.520Z",
"comment": "example comment"
}
},
{
"serviceProvisioningStatus": "synced",
"serviceProvisioningErrors": null,
"id": "contoso2",
"tenantId": "contosoTenantId2",
"accountName": "contoso2",
"productContext": "teams",
"operatorId": "string",
"status": "active",
"consentedOn": "2024-05-07T11:15:10.519Z",
"lastModifiedOn": "2024-05-07T11:15:10.519Z",
"consentedCountries": [
"string"
],
"contacts": [
{
"fullName": "Example Name2",
"email": "example@contoso2.com",
"telephoneNumber": "+1234567891",
"companyName": "contoso2",
"companySize": "size"
}
],
"customerRelationship": {
"status": "example status",
"lastModifiedOn": "2024-05-07T11:15:10.520Z",
"comment": "example comment"
}
},
... // more RFIs
],
"nextLink": "string"
}
Update a Request for Information
Use PATCH on the /teamsRequestsForInformation/<tenantID>
endpoint to update the status of the RFI, which is reflected in the backend service. Operator Connect and Teams Phone Mobile allows you to indicate the status of the request back to the end customer so the updated status appears in the customer's Teams Admin Center.
Request
PATCH /teamsRequestsForInformation/contosoTenantId
{
"customerRelationship": {
"status": "new status",
"comment": "new comment"
}
}
Response
{
{
"serviceProvisioningStatus": "pending",
"serviceProvisioningErrors": null,
"id": "contoso",
"tenantId": "contosoTenantId",
"accountName": "contoso",
"productContext": "teams",
"operatorId": "string",
"status": "active",
"consentedOn": "2024-05-07T11:15:10.519Z",
"lastModifiedOn": "2024-05-07T11:15:10.519Z",
"consentedCountries": [
"string"
],
"contacts": [
{
"fullName": "Example Name",
"email": "example@contoso.com",
"telephoneNumber": "+1234567890",
"companyName": "contoso",
"companySize": "size"
}
],
"customerRelationship": {
"status": "new status",
"lastModifiedOn": "2024-05-07T12:15:10.520Z",
"comment": "new comment"
}
}
}
List all the numbers assigned to an account
Use a GET request on the /accounts/<accountName>/numbers
endpoint to obtain a list of the numbers that have been provisioned for that account.
Request:
GET /accounts/contoso/numbers?api-version=2024-02-29 HTTP/1.1
Response for an account with only Operator Connect numbers:
{
"value": [
{
"serviceProvisioningStatus": "pending",
"serviceProvisioningErrors": null,
"telephoneNumber": "+123451",
"accountName": "contoso",
"serviceDetails": {
"teamsOperatorConnect": {
"enabled": true,
"assignmentStatus": "assigned",
"configuration": {
"usage": "CallingUserAssignment",
"choosableCapabilities": [
"InboundCalling",
"OutboundCalling",
"Mobile"
],
"civicAddressId": "civicAddressIdString",
"allowTenantAddressUpdate": true,
}
},
},
"configuration": {
"customSipHeader": "exampleHeaderContents"
}
},
... // more OC numbers
],
nextLink: "string"
}
Response for an account with both Operator Connect and Direct Routing numbers provisioned:
{
"value": [
{
"serviceProvisioningStatus": "synced",
"serviceProvisioningErrors": null,
"telephoneNumber": "+123451",
"accountName": "contoso",
"serviceDetails": {
"teamsOperatorConnect": {
"enabled": true,
"assignmentStatus": "assigned",
"configuration": {
"usage": "CallingUserAssignment",
"choosableCapabilities": [
"InboundCalling",
"OutboundCalling",
"Mobile"
],
"civicAddressId": "civicAddressIdString",
"allowTenantAddressUpdate": true,
}
},
},
"configuration": {
"customSipHeader": "exampleHeaderContents"
}
},
{
"serviceProvisioningStatus": "synced",
"serviceProvisioningErrors": null,
"telephoneNumber": "+123452",
"accountName": "contoso",
"serviceDetails": {
"teamsDirectRouting": {
"enabled": true
}
},
"configuration": {
"customSipHeader": "exampleHeaderContents"
}
},
... // more DR and OC numbers
],
nextLink: "string"
}
List all emergency locations for a specific account
Use a GET request on the /accounts/<accountName>/teamsCivicAddresses
endpoint to obtain the full list of Civic Addresses configured in the Teams Admin Center for that account. You can use the population of this list as the locationid
when creating or updating numbers within the account.
Request:
GET /accounts/contoso/teamsCivicAddresses?api-version=2024-02-29 HTTP/1.1
Response:
{
"value": [
{
"id": "string",
"country": "string",
"houseNumber": "string",
"houseNumberSuffix": "string",
"preDirectional": "string",
"streetName": "string",
"streetSuffix": "string",
"postDirectional": "string",
"stateOrProvince": "string",
"countyOrDistrict": "string",
"cityOrTown": "string",
"cityOrTownAlias": "string",
"postalOrZipCode": "string",
"description": "string",
"companyName": "string",
"companyId": "string",
"defaultLocationId": "string",
"validationStatus": "notValidated",
"tenantId": "string",
"partnerId": "string",
"locations": [
{
"id": "string",
"civicAddressId": "string",
"description": "string",
"additionalInfo": "string",
"isDefault": true,
"elin": "string"
}
],
"latitude": "string",
"longitude": "string"
},
... // more locations
],
"nextLink": "string"
}
Remove a number from the account
Use DELETE on the /accounts/<accountName>/numbers/<telephoneNumber>
endpoint to release a number from a tenant. This operation will unassign a number from a user if assigned and then release the number from the tenant.
Request:
DELETE /accounts/contoso/numbers/+123451?api-version=2024-02-29 HTTP/1.1
Response:
204 Status Code
Troubleshooting
Teams Direct Routing isn’t working for numbers on an account.
- Check the DNS token has been validated by sending a GET on the account, verifying that
serviceDetails.teamsDirectRouting
hassubdomainStatus
equal toProvisioned
.
- Check the DNS token has been validated by sending a GET on the account, verifying that
I’ve configured a number to use Direct Routing/Zoom, but it doesn’t seem to be working.
- Check that the account has been configured to use Direct Routing/Zoom, and that the number has this specific feature enabled.
I’ve managed to contact the API, but after making multiple requests my connections start timing out.
- The provisioning API is rate limited (to a reasonable per-second rate). Space out your requests, or use the batch endpoint, to avoid being rate limited. The rate limit times out eventually, and you'll be able to connect.
Next steps
Start integrating with the Azure Communications Gateway Provisioning API.