This document contains the instructions to operate the Broadcast Development Kit (BDK) using Postman. This includes:
- How to use the Management API
- Download and Install Postman
BDK Management API endpoints are secured. To be able to make API requests, it is necessary to include a valid bearer token in the requests' headers.
In this section we will describe how to generate an access token with the App registration used in the SPA (if you have already configured the Web UI for the Broadcast Development Kit for the implicit grant flow or by creating a new App registration for the client credential flow.
Create a new App registration to be able to configure it and thus be able to obtain the access token. Review the following Microsoft documentation that will explain how to do it, and consider the following settings:
- Name: Meaningful name.
- Supported account types: Accounts in this organizational directory only (
your-organizationonly - Single tenant).
Once you've registered the App registration you must add a client secret, copy the value and save it together with the application client id in a secure place, we will need them for future steps.
From the App registration created view, go to the API permissions option that is in the Manage menu, click the Add a permission button and then ensure the APIs my organization uses tab is selected. Search for the App Registration created for the Management API and click on the search result.
Then inside App registration created, select AccessAll and click on Add permissions.
NOTE: If your user does not have the necessary permissions to enable the add-on permission. You must ask a user with the required permission to enable it.
Once the App registration is created, follow the steps below to generate the access token from Postman:
-
Open Postman and create a new request.
-
In the Authorization tab, select the type OAuth2.
-
Then in the Configure New Token section enter the parameters that are highlighted.

Configure New Token in Postman with Client Credentials Placeholder Description Grant Type Select the Client Credentials option. accessTokenURL https://login.microsoftonline.com/{{tenantId}}/oauth2/v2.0/tokentenantId Tenant Id of the subscription. clientId Client Id of the App registration created. clientSecret Client Secret of the App registration created. scope api://{{clientIdManagementAPI}}/.defaultclientIdManagementAPI Client Id of the App Registration of the ManagementApi. -
Next, to generate the token, click on the Get New Access Token button, a new window will be displayed, and then it will show the generated
access token. To use this token, click on the button Use Token, this will use theaccess tokenin the created request.
Management Access Tokens
NOTE: It is necessary to use the App registration of the SPA to generate the access token, please review the following documentation.
To generate the access token with implicit grant type, the following steps are suggested:
-
Open Postman and create a new request.
-
In the Authorization tab, select the type OAuth2.
-
Then in the Configure New Token section enter the parameters that are highlighted.

Configure New Token in Postman Placeholder Description Grant Type Select the Implicit option. callbackUrl https://{{spaRedirectUrl}}spaRedirectUrl Spa redirect URL configured. authUrl https://login.microsoftonline.com/{{tenantId}}/oauth2/v2.0/authorizetenantId Tenant Id of the subscription. clientId Client Id of the App registration created. scope api:// {{clientIdManagementAPI}}/access_as_producerclientIdManagementAPI Client Id of the App Registration of the ManagementApi. -
Next, to generate the token, click on the Get New Access Token button. Login, and after a few seconds, a pop-up window Manage Access Tokens will be displayed, which shows the generated
access token. To use this token, click on the button Use Token, this will use theaccess tokenin the created request.
Management Access Tokens
The life cycle of a call follows these steps:
- First, we need to start the service (i.e. the VM) that host the bot using the
Start Serviceendpoint. - After requesting the provisioning of the service, we must poll the
Get Service Stateendpoint several times until the service isProvisioned. - Once the service is provisioned, to invite the bot into a meeting we must use the
Initialize Callendpoint. The request will return a call with state equals to0(Establishing). - Then, we must poll the
Get Call Detailsendpoint until the state of the call is1(Established) and the Participants are available. - After that, we are able to start and stop the streams we want to capture.
- Once the service is not longer needed in the call and all streams are stopped, we can disconnect it using the
Delete Callendpoint. - After the bot usage is done, it can be terminated using the
Stop Serviceendpoint. - To make sure that the VM has been stopped, we must poll the
Get Service Stateendpoint several times until the service isDeprovisioned.
The REST endpoints exposed by the Management API are documented by using OpenAPI specification. To access Swagger UI go to https://{{MANAGEMENT_API_URL}}/Swagger
Optionally, you can import the OpenAPI specification in Postman by importing the following JSON https://{{MANAGEMENT_API_URL}}/swagger/v1/swagger.json
The following instructions are the steps necessary to start the service, join the bot into the meeting, start an extraction or injection, disconnect the call and stop the service.
It is necessary to have a scheduled meeting in Microsoft Teams to join the bot in it and also to have already configured the access token in Postman in order to follow the instructions.
-
Get service state: We need to verify the state of the service before join the bot into the meeting, check if the service is available or not. If it is not we need to start it.
Method:
GET
Endpoint:https://{{appServiceUrl}}/api/service/{{serviceId}}/statePlaceholder Description appServiceUrl AppService Url serviceId Service Id (the default id is 00000000-0000-0000-0000-000000000000)Response:
{ "id": "serviceId", "resource": { "id": "serviceId", "callId": null, "name": "serviceName", "state": 3, "createdAt": "2021-08-02T11:26:13.7519720+00:00", "infrastructure": { "virtualMachineName": "test-vm", "resourceGroup": "test-bot-vm", "subscriptionId": "a874b409-c14a-448b-935d-296ac546e568", "id": "/subscriptions/a874b409-c14a-448b-935d-296ac546e568/resourcegroups/test-bot-vm/providers/microsoft.compute/virtualmachines/test-vm", "powerState": "PowerState/deallocated", "ipAddress": "21.120.170.210", "dns": "domain.co", "provisioningDetails": { "state": { "id": 3, "name": "Deprovisioned" }, "message": "Service serviceName deprovisioned." } } }If the provisioningDetails is deprovisioned we need to start the service by calling the start endpoint.
-
Start Service:
Method:
POST
Endpoint:https://{{appServiceUrl}}/api/service/{{serviceId}}/startPlaceholder Description appServiceUrl AppService URL serviceId Service Id (the default id is 00000000-0000-0000-0000-000000000000)Response:
{ "id": "serviceId", "resource": { "id": "serviceId", "callId": null, "name": "serviceName", "state": 0, "createdAt": "2021-08-02T11:26:13.7519720+00:00", "infrastructure": { "virtualMachineName": "test-vm", "resourceGroup": "test-bot-vm", "subscriptionId": "a874b409-c14a-448b-935d-296ac546e568", "id": "/subscriptions/a874b409-c14a-448b-935d-296ac546e568/resourcegroups/test-bot-vm/providers/microsoft.compute/virtualmachines/test-vm", "powerState": "PowerState/deallocated", "ipAddress": "21.120.170.210", "dns": "domain.co", "provisioningDetails": { "state": { "id": 0, "name": "Provisioning" }, "message": "Provisioning service serviceName." } } } }After calling the start endpoint the service will start provisioning, it is necessary to check the status until the provisioningDetails changes to
Provisioned
-
Initialize Call: To join the bot into the meeting we will call the initialize-call endpoint with the Teams meeting URL. The service needs to be
Provisionedto join the bot to the call.Method:
POST
Endpoint:https://{{appServiceUrl}}/api/call/initialize-callPlaceholder Description appServiceUrl AppService Url Headers: Verify the
Content-typekey has as valueapplication/json
Body:rawComplete the body in Postman with the following:
{ "MeetingUrl": "{{teamsMeetingUrl}}" }Placeholder Description teamsMeetingUrl Teams meeting URL To obtain the Teams meeting URL, join a Microsoft Teams meeting, press the Show participants button, then press Share invite and copy the link.

Steps to get the Teams meeting URL Response:
{ "id": "callId", "resource": { "id": "callId", "meetingUrl": "teamsMeetingUrl", "meetingId": "meetingId", "state": 0, "createdAt": "2021-08-03T16:17:57.4086259+00:00", "startedAt": "0001-01-01T00:00:00", "endedAt": "0001-01-01T00:00:00", "meetingType": 0, "botFqdn": "domain.co", "botIp": null, "defaultPassphrase": null, "defaultLatency": 0, "graphId": null, "streams": [], "injectionStream": null, "publicContext": {}, "privateContext": { "streamKey": "hJw5wZxAETkyExVhqdtw" } } }The returned status is equals
0(Establishing) means that the bot is joining the call. To be able to start a stream, the call state has to be in1(Established). You can use theGet Call Detailsoperation to verify the status of the call. The returnedIdvalue must to be copied to be used in the following steps. -
Get call details: The call details endpoint will retrieve call and participants information. If the state of the call is
1(Established), it means that the bot is joined in the call.Method:
GET
Endpoint:https://{{appServiceUrl}}/api/call/{{callId}}Placeholder Description callId Call Id obtained as response in the previous step Response:
{ "id": "callId", "meetingUrl": "", "meetingId": "meetingId", "state": 1, "createdAt": "2021-08-03T16:17:57.4086259+00:00", "startedAt": "2021-08-03T16:18:05.9058345Z", "endedAt": "0001-01-01T00:00:00", "meetingType": 0, "botFqdn": "domain.co", "botIp": null, "defaultPassphrase": null, "defaultLatency": 0, "graphId": "graphId", "streams": [ { "id": "screnShareId", "aadId": null, "callId": "callId", "participantGraphId": "screenShareGraphId", "displayName": "Screen Share", "photoUrl": null, "type": 0, "state": 0, "isHealthy": true, "healthMessage": null, "audioMuted": false, "isSharingAudio": false, "isSharingVideo": false, "isSharingScreen": false, "details": { "streamUrl": null, "audioDemuxed": false, "passphrase": null, "keyLength": 0, "latency": 0, "previewUrl": null }, "createdAt": "0001-01-01T00:00:00", "leftAt": null, "error": null }, { "id": "primarySpeakerId", "aadId": null, "callId": "callId", "participantGraphId": "primarySpeakerGraphId", "displayName": "Primary Speaker", "photoUrl": null, "type": 1, "state": 0, "isHealthy": true, "healthMessage": null, "audioMuted": false, "isSharingAudio": false, "isSharingVideo": false, "isSharingScreen": false, "details": { "streamUrl": null, "audioDemuxed": false, "passphrase": null, "keyLength": 0, "latency": 0, "previewUrl": null }, "createdAt": "0001-01-01T00:00:00", "leftAt": null, "error": null }, { "id": "participant1Id", "aadId": "aadId1", "callId": "callId", "participantGraphId": "participant1GraphId", "displayName": "Participant 1 Name", "photoUrl": "https://appServiceUrl/api/participant/photo/00000000-0000-0000-0000-000000000000", "type": 2, "state": 0, "isHealthy": true, "healthMessage": "", "audioMuted": true, "isSharingAudio": true, "isSharingVideo": false, "isSharingScreen": false, "details": { "streamUrl": null, "audioDemuxed": false, "passphrase": null, "keyLength": 0, "latency": 0, "previewUrl": null }, "createdAt": "0001-01-01T00:00:00", "leftAt": null, "error": null }, { "id": "participant2Id", "aadId": "aadId2", "callId": "callId", "participantGraphId": "participant2GraphId", "displayName": "Participant 2 Name", "photoUrl": "https://appServiceUrl/api/participant/photo/00000000-0000-0000-0000-000000000000", "type": 2, "state": 0, "isHealthy": true, "healthMessage": "", "audioMuted": true, "isSharingAudio": true, "isSharingVideo": true, "isSharingScreen": false, "details": { "streamUrl": null, "audioDemuxed": false, "passphrase": null, "keyLength": 0, "latency": 0, "previewUrl": null }, "createdAt": "0001-01-01T00:00:00", "leftAt": null, "error": null } ], "injectionStream": null, "publicContext": {}, "privateContext": { "streamKey": "hJw5wZxAETkyExVhqdtw" } }The list of the participants joined to the call is listed in the streams property of the response.
-
Start extraction stream: To start the extraction of a participant it is necessary to check if the
isSharingVideoproperty of the participant is true. In previous step request response we can see that participant 2 is sharing video.Method:
POST
Endpoint:https://{{appServiceUrl}}/api/call/{{callId}}/stream/start-extractionPlaceholder Description callId Call Id Headers: Verify the
Content-typekey has as valueapplication/json
Body:rawBody for RTMP
Complete the body in Postman with the following:
{ "participantId": "{{participantId}}", "participantGraphId": "{{participantGraphId}}", "resourceType": 2, "protocol": 1, "mode": 1, "streamUrl": "{{streamUrl}}", "streamKey": "{{streamKey}}", "timeOverlay": false, "enableSsl": false }Placeholder Description participantId Participant Id participantGraphId Participant Graph Id resourceType Select a value from ResourceType table protocol Use 1for RTMP extraction protocolmode Use 1for Pull,2for PushstreamUrl Stream Url for RTMP in Pushmode, usenullinPullmodestreamKey Stream key for RTMP in Pushmode, usenullinPullmode (InPullmode the extraction uses the stream key of the call's privateContext)timeOverlay trueorfalseenableSsl Enable SSL: trueorfalseit is inPullmode, usefalseinPushmodeBody for SRT
Complete the body in Postman with the following:
{ "participantId": "{{participantId}}", "participantGraphId": "{{participantGraphId}}", "resourceType": 2, "protocol": 0, "mode": 2, "streamUrl": "{{streamUrl}}", "streamKey": "{{streamKey}}", "timeOverlay": false, "keyLength": 0, "latency": 750 }Placeholder Description participantId Participant Id participantGraphId Participant Graph Id resourceType Select a value from ResourceType table protocol Use 0for SRT extraction protocolmode Use 1for Caller,2for Listener.streamUrl Stream Url for extraction in SRT Callermode, usenullforListenermodestreamKey use a passphrase or nulltimeOverlay trueorfalsekeyLength Allowed values: 0,16,24,32latency Latency for SRT (eg: 750)Table of Available ResourceTypes
ResourceType Value Screen Share 0PrimarySpeaker 1Participant 2TogetherMode 3LargeGallery 4LiveEvent 5Response:
{ "id": "participant2Id", "resource": { "id": "participant2Id", "aadId": "aadId2", "callId": "callId", "participantGraphId": "participant2GraphId", "displayName": "Participant 2 Name", "photoUrl": "https://appServiceUrl/api/participant/photo/00000000-0000-0000-0000-000000000000", "type": 2, "state": 2, "isHealthy": true, "healthMessage": "", "audioMuted": true, "isSharingAudio": true, "isSharingVideo": true, "isSharingScreen": false, "details": { "streamUrl": "rtmps://domain:2940/secure-extraction/hJw5wZxAETkyExVhqdtw?callId=callid", "audioDemuxed": false, "passphrase": "hJw5wZxAETkyExVhqdtw", "keyLength": 0, "latency": 0, "previewUrl": null }, "createdAt": "0001-01-01T00:00:00", "leftAt": null, "error": null } }When started the extraction in SRT Listener mode or in RTMP Pull mode, you can use the
streamUrlin thedetailsof the response to consume the extraction with a player as VLC -
Stop extraction stream:
Method:
POST
Endpoint:https://{{appServiceUrl}}/api/call/{{callId}}/stream/stop-extractionHeaders: Verify the
Content-typekey has as valueapplication/json
Body:rawComplete the body in Postman with the following:
{ "participantId": "{{participantId}}", "participantGraphId": "{{participantGraphId}}", "resourceType": 2, }Placeholder Description participantId Participant Id participantGraphId Participant Graph Id resourceType Select a value from ResourceType table Response:
{ "id": "participant2Id", "resource": { "id": "participant2Id", "aadId": "aadId2", "callId": "callId", "participantGraphId": "participant2GraphId", "displayName": "Participant 2 Name", "photoUrl": "https://appServiceUrl/api/participant/photo/00000000-0000-0000-0000-000000000000", "type": 2, "state": 0, "isHealthy": true, "healthMessage": "", "audioMuted": false, "isSharingAudio": true, "isSharingVideo": true, "isSharingScreen": false, "details": { "streamUrl": null, "audioDemuxed": false, "passphrase": null, "keyLength": 0, "latency": 0, "previewUrl": null }, "createdAt": "0001-01-01T00:00:00", "leftAt": null, "error": null } }
-
Start injection stream:
Method:
POST
Endpoint:https://{{appServiceUrl}}/api/call/{{callId}}/stream/start-injectionPlaceholder Description callId Call Id Headers: Verify the
Content-typekey has as valueapplication/json
Body:rawBody for RTMP
{ "protocol": 1, "mode": 1, "streamUrl": "{{streamUrl}}", "enableSSl": false }Placeholder Description protocol Use 1for RTMP injection protocolmode Use 1for Pull,2for PushstreamUrl RTMP in Pullmode,nullforPushmodeenableSsl Enable SSL: trueorfalseused when mode isPushBody for SRT
{ "protocol": 0, "mode": 1, "streamUrl": "{{streamUrl}}", "streamKey": "{{streamKey}}", "latency": 750, "keyLength": 0, }Placeholder Description protocol Use 0for SRT injection protocolmode Use 1for Caller,2for Listener.streamUrl Stream Url for injection in SRT Callermode, usenullinListenermodestreamKey Use a passphrase or nulllatency Latency (eg: 750)keyLength Allowed values: 0,16,24,32Response:
{ "id": "streamId", "resource": { "id": "streamId", "callId": "callId", "injectionUrl": "rtmps://domain.co:2936/secure-ingest/streamkey?callId=callid", "passphrase": "streamkey", "latency": 0, "keyLength": 0, "state": 2, "startingAt": "2021-08-04T15:41:37.9975386Z", "startedAt": "2021-08-04T15:41:38.1097951Z", "endingAt": "0001-01-01T00:00:00", "endedAt": "0001-01-01T00:00:00", "protocol": 1, "streamMode": 2, "error": null } }The id of the response should be copied for later use when stopping the injection.
-
Optionally, you can use the following operations to control the injection:
-
Hide:
Method:
POST
Endpoint:https://{{appServiceUrl}}/api/call/{{callId}}/injection/hide -
Display:
Method:
POST
Endpoint:https://{{appServiceUrl}}/api/call/{{callId}}/injection/display -
Mute:
Method:
POST
Endpoint:https://{{appServiceUrl}}/api/call/{{callId}}/mute -
Un-mute:
Method:
POST
Endpoint:https://{{appServiceUrl}}/api/call/{{callId}}/unmute -
Set Volume:
Method:
POST
Endpoint:https://{{appServiceUrl}}/api/call/{{callId}}/injection/set-volumeNote: Check OpenAPI specification for information about request body
-
-
Stop injection stream:
Method:
POST
Endpoint:https://{{appServiceUrl}}/api/call/{{callId}}/stream/{{streamId}}/stop-injectionPlaceholder Description callId Call Id streamId Obtained Stream Id when started the injection Response
{ "id": "streamId", "resource": { "id": "streamId", "callId": "callId", "injectionUrl": "rtmps://domain.co:2936/secure-ingest/streamkey?callId=callid", "passphrase": "streamkey", "latency": 0, "keyLength": 0, "state": 0, "startingAt": "2021-08-04T16:01:22.4798097Z", "startedAt": "2021-08-04T16:01:22.7420745Z", "endingAt": "2021-08-04T16:03:36.9824056Z", "endedAt": "2021-08-04T16:03:37.0222905Z", "protocol": 1, "streamMode": 2, "error": null } }
-
Generate RTMP stream key: This endpoint updates the streamKey in the privateContext of the call, used key for RTMP injections and extractions. Only need to use this endpoint if you want to change the RMTP key of the current meeting, each meeting has its own RTMP stream key.
Method:
POST
Endpoint:https://{{appServiceUrl}}/api/call/{{callId}}/generate-stream-keyPlaceholder Description callId Call Id Headers: Verify the
Content-typekey has as valueapplication/json
Body:rawResponse
{ "callId": "callId", "streamKey": "QBfeZihvokOGiISvQ2pyTw" }
-
Disconnect the bot from the call:
Method:
DELETEEndpoint:https://{{appServiceUrl}}/api/call/{{callId}}Response:
{ "id": "callid", "resource": { "id": "callid", "meetingUrl": "teamsMeetingUrl", "meetingId": "meetingId", "state": 2, "createdAt": "2021-08-03T16:17:57.4086259+00:00", "startedAt": "2021-08-03T16:18:05.9058345Z", "endedAt": "0001-01-01T00:00:00", "meetingType": 0, "botFqdn": "domain.co", "botIp": null, "defaultPassphrase": null, "defaultLatency": 0, "graphId": "graphId", "streams": [], "injectionStream": null, "publicContext": {}, "privateContext": { "streamKey": "hJw5wZxAETkyExVhqdtw" } } }
-
Stop the service:
Method:
POST
Endpoint:https://{{appServiceUrl}}/api/service/{{serviceId}}/stopPlaceholder Description appServiceUrl AppService URL serviceId Service Id (the default id is 00000000-0000-0000-0000-000000000000)Response:
{ "id": "serviceId", "resource": { "id": "serviceId", "callId": null, "name": "serviceName", "state": 3, "createdAt": "2021-06-07T15:44:16.6961976+00:00", "infrastructure": { "virtualMachineName": "test-vm", "resourceGroup": "test-bot-vm", "subscriptionId": "a874b409-c14a-448b-935d-296ac546e568", "id": "/subscriptions/a874b409-c14a-448b-935d-296ac546e568/resourcegroups/test-bot-vm/providers/microsoft.compute/virtualmachines/test-vm", "powerState": "PowerState/running", "ipAddress": "21.120.170.210", "dns": "domain.co", "provisioningDetails": { "state": { "id": 2, "name": "Deprovisioning" }, "message": "Deprovisioning service serviceName" } } } }



