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:

  1. 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.

  2. 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.
  3. Assign yourself as an owner for the app registration. See Assign application owner.
  4. Configure the app registration created by registering the application with app roles that use the scopes for the Provisioning API, as described earlier.
  5. 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 and region2Token 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 an If-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 value exampleHeaderContents 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 has subdomainStatus equal to Provisioned.
  • 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.