Header Logo

Developer Portal

These API Guides are a great place to start when you're looking to understand and use any of the Mitel APIs. While they don't go into full detail on every operation or resource, they will explain the main principles, walk through common uses, and act as a supplementary resource to the primary API Documentation.

Admin API Guide

The Admin API is for (wait for it)…administering your CloudLink account! We were going to come up with a more creative name, but Frank in Accounting lost our marketing budget at the track.

This particular API allows you to do things like:

  • Create and manage users

  • Manage accounts

  • Create and manage organizational entities such as groups and sites

  • Upload user photos

There are quite a few components to the Admin API, but it’s all really very logical and therefore quite boring. Not like that highly irrational and overly dramatic Notification API. Most components can be read, created, updated, and deleted provided that you have the appropriate permissions. There are some exceptions where the permissions required are only held by Mitel staff, and for this reason we have not discussed those components here in this guide although you may seem them referenced in the API documentation itself.

These are the components that you can work with as a Mitel partner:


In addition to managing your own Partner account, you can also view and manage accounts for your customers. When working with an account, it is recommended that you only use GET and PUT operations to view or update existing accounts. When you need to create a new account, best practice is to use the Accounts app/portal since creating a properly functioning account requires additional operations that you will not have access to through the API.


Users are of key importance, as I imagine you want people to actually use your app. Users are able to obtain access tokens which will be required in order for that user to actually interact with CloudLink (e.g. send or receive chat messages). Most user properties are self-explanatory and explained in more detail in the API Documentation, however users can be assigned one of three roles:

PARTNER_ADMIN - Grants full access to your Partner account and any customer accounts. ACCOUNT_ADMIN - Grants full access to a specific account USER - Grants basic user rights within a specified account. A user can create conversations, send messages, execute call control, and most other tasks you would need to perform within a client application.

In addition to creating, updating, and deleting Users you can also send welcome emails and temporary access codes for applications that require their use.


Clients are typically used to represent a server side application, as opposed to a user. A client can obtain an access token and be granted any of the same roles that can be assigned to a user.


Sites are meant to represent geographical locations can be assigned an address, map coordinates, timezone, and specific dialing rules (inbound/outbound digits). Sites can be useful as a kind of directory, or to obtain the appropriate dialing rules needed to dial into our out of the location without needing to hard code the values. You can associate Groups to a site.


Photos can be assigned to Users, Contacts, and Groups. When performing a GET operation on one of those objects, the URL for the uploaded photo will be provided in the response body.


Contacts are used in different ways by different Mitel applications. In the event that your CloudLink account is connected to a supported PBX through a CloudLink Gateway, contacts are synced from that PBX. There are a variety of contact types, some of which are purely administrative and assigned to the account itself, such as the MAIN_CONTACT and TECHNICAL_SUPPORT contact. The contact types you will be working with the most often are:

CORPORATE - Considered a generic contact, these will include those synced from the PBX. PERSONAL_CONTACT - Used by the MOWA application and pulled from Office 365. Only accessible by the user who created the contact. GUEST - Used by MiTeam Meetings, this contact type can obtain a guest token for the purpose of interacting with CloudLink services.


While not considered a unique object, entities are considered to be Users, Contacts, Groups, or Clients. The purpose of entities is to provide an easy way to pull a list of all the aforementioned objects with a single API call, or identify an object when all you have is the UID (userID, groupID, etc).


Similar to Contacts, groups are synced if you have a supported PBX connected to the CloudLink account through a CloudLink Gateway. Groups are primarily considered a PBX concept as they are dialable and contain members that calls can be routed to.


For practical purposes, members are analogous to Users and are assigned to Groups. The operations available for members focuses on assigning, removing, or retrieving a list of members within a specified group.


Short for System for Cross-domain Identify Management, SCIM is an open standard to allow automated user provisioning. Currently Mitel allows you to create a SCIM based integration with Azure AD and OKTA AD through the CloudLink Accounts app, however you could create your own integration with a different Identity Provider that supports SCIM using the API. You can use POST /accounts/{accountID}/scimkey to obtain the appropriate secret key.

Authentication API Guide

The Authentication API is a loosely OAuth 2.0 compliant authentication service that grants tokens that allow access to the various Mitel APIs. It’s basically the bouncer of the Mitel API Club. It doesn’t care who you know, you better be on the token list or you’re going to be standing out in the cold. Except Frank in Accounting, he’s banned for life and he knows why.

The Authentication API looks simple at a first glance, but it’s actually really quite complex. To help simplify things, we’re going to group everything into four primary functions:

  • Open ID Connect (OICD) Workflow - Auth Portal - Allows you to leverage the Mitel Auth Portal to authenticate users securely, then redirect the user back to your app.

  • Open ID Connect (OICD) Workflow - Custom - Create your own authentication process within your app that uses the user’s credentials, a unique application identifier, and an optional (PKCE) key.

  • OAuth Authentication (aka ‘Bearer’) Tokens - Allows you to exchange an authorization code obtained through the OICD workflow for a bearer token that can be used to make API calls. You can also use this route to authenticate and obtain a token for a client or guest (see the Admin API Guide), refresh an existing token, or bypass the OICD workflow to directly authenticate a user.

  • Single Sign On - Leverage the available SAML 2.0 based SSO with Azure ADP

There is other functionality available, however for the most part those routes are only used for very specific functions by Mitel apps and will not be covered in this guide.

OpenID Connect (OICD) Workflow - Auth Portal

The Authentication API uses the /authorize routes to securely authorize both the application and the user attempting to utilize the Mitel API. This is done by providing important identifying information, which returns a code that can be exchanged for an authentication (bearer) token for subsequent API calls. Using this workflow requires that your application has been registered, approved, and issued a unique application identifier known as a ‘Client ID’. The Client ID and optional Redirect URI is discussed later in this guide.

The best way to use this workflow is to redirect the user to the Mitel Auth Portal (https://auth.mitel.io) with information to identify the requesting application. The user will be prompted to enter their login credentials (and possibly account ID), at which point the Mitel Auth Portal will initiate the Open ID authorization process and return an authorization code. This process is described in detail here, and will not be covered in this guide.

The alternative to using the Mitel Auth Portal is to build your own login portal that utilizes the same API calls that the Mitel Auth portal uses.

OpenID Connect (OICD) Workflow - Custom

Building your own login portal to replicate the Mitel Auth portal is actually a relatively straightforward process, as creating an authorization code only requires a single API call. You can also add an additional layer of security through the use of a PKCE code, if you wish. Obtaining the authorization code is done by making a POST call to the /authorize route. The request body schema is application/x-www-form-urlencoded and contains quite a few fields, however there are only a few which are relevant to a third party developer (that’s you). While the formal documentation only lists the first two fields as required, in practice additional fields should always be used so the ‘required’ fields listed here may differ somewhat from the formal documentation:

  • client_id - This is a unique, pre-existing ID that identifies the application making the request (required)

  • response_type - This will always be ‘code’ (required)

  • username - The user’s CloudLink username (required)

  • password - The user’s CloudLink password (required)

  • account_id - The user’s CloudLink Account ID. If the user exists in multiple accounts, this becomes a required field. The API will return an HTTP 409 error if this is true and the account is not specified.

  • redirect_uri - Where the auth code (and user) will be returned to. Only required if using the redirect to the Mitel Auth portal, as otherwise the code is returned to whomever is making the API call. Similar to the Client ID, these values must be pre-registered and not only associated with your app but also associated with a specific account. More on this below.

  • code_challenge_method - An optional additional layer of security using a Proof Key for Code Exchange (PKCE). Only ‘S256’ is supposed at this time.

  • code_challenge - PKCE code (SHA-256). Used in conjunction with ‘code_challenge_method’, this will need to be provided again when requesting an authentication token later on.

  • Id_token_hint - Used as a way to get an authorization code when moving between applications using a refresh token (so you don’t have to login twice). This replaces the username/password.

  • target_client_id - Used in conjunction with ‘id_token_hint’, this is the Client ID for the application the user is switching to.

Once you have received the authorization code, you can now exchange it for an authentication token using the OAuth Authentication Token workflow described below with a grant_type of “authorization_code”.

Client ID and Redirect URI

In the Sandbox environment (e.g. https://authentication.dev.mitel.io), a generic Client ID will have been provided that can be used by anyone in your organization for app development.

In the Public environment, you will need to submit your app for approval before a unique application specific Client ID is generated for you. To do this, follow the steps outlined here.

For both environments, the Redirect URI will always be specific to the account where the application is deployed. This allows you to supply a different Redirect URI for each deployment of the application, which facilitates on-premise or other single tenant implementations of your app. You can also have multiple Redirect URIs and then choose which one to use each time you use the OICD workflow with the Mitel Auth portal. Fortunately you can manage these yourself using a PUT operation on the /applications route to update the redirectUris property.

OAuth Authentication Tokens

Generally speaking there are 3 categories of tokens that you can obtain, two of which use the same Authentication API route and one which is unique for providing guest access:

  • User tokens - Issued to users

  • Client tokens - Issued to clients, which typically represent a server application

  • Guest tokens - Issued to anonymous users, typically used for MiTeam Meetings. Given their simplicity, guest tokens are not discussed in detail here but information is available in the API documentation.

Furthermore, there are two types of tokens issued for each of the above:

  • Authentication Token - This is the token used to authenticate the user/client/guest when making an API call. These tokens are typically valid for 60-70 minutes.

  • Refresh Token - This token can be used to obtain an updated authentication token without going through the full authentication process. These tokens are valid for 15 days.

Tokens are obtained by using POST /token and providing different values in the application/x-www-form-urlencoded request body. When obtaining a token, there are three different ways to do so by using the ‘grant_type’ field:

  • Password - Used to obtain authentication tokens for users, requires credentials

  • Client_credentials - Used to obtain authentication tokens for clients

  • Authorization_code - Requires that the OAuth workflow (as described above) be followed to first obtain an authorization code.

  • Refresh_token - Used to refresh the authentication code with a previously issued refresh token.

Depending on the value that you specify in grant_type, different fields are required.

Grant_Type = Password Used to obtain a new authentication token for a user, the following fields must be used in the request body:

  • account_id - Required if the user exists in more than one account

  • username - The user’s CloudLink username

  • password - The user’s CloudLink password

Grant_Type = Client_Credentials Used to obtain a new authentication code for a client, the following fields must be used in the request body:

  • client_id - The unique identifier (GUID) for the client

  • client_secret - The secret key for the client

When requesting an authentication code for a client, you can optionally provide additional properties that allow the client to function as an anonymous user. These properties are listed in the API documentation but include most properties available for a typical user.

Grant_Type = Authorization_Code Used to obtain an authentication token when you have completed the OAuth workflow, the following fields must be used in the request body:

  • code - The authorization code provided by executing the OAuth workflow

  • client_id - This is a unique, pre-existing ID that identifies the application making the request

  • code_verifier - Optional, used if a PCKE key was included in the OAuth workflow

Grant_Type = Refresh_Token Used to obtain an updated authentication token when the original has expired (valid for 60-70 mins), the following fields must be used in the request body:

  • refresh_token - The refresh token obtained with the most recent authentication token. Must be used within 15 days or the full authentication workflow will need to be run again.

Single Sign On

SSO is available using SAML. While Mitel only officially supports Azure as an Identify Provider (IdP), in theory you can use the Azure SSO setup in the Mitel Accounts Portal (https://accounts.mitel.io) to add any IdP that support SAML 2.0.

When your account has SSO configured, you will need to use the OpenID Connect Workflow and make use of the Mitel Auth portal. You cannot recreate this functionality using the Authentication API. When the user provides their username (and optionally account ID), they will be presented with a ‘Single Sign On’ button that will redirect the user to the IdP to provide their credentials.

Conversations API Guide

Do you hate talking to people in person? When your phone rings, does your anxiety spike through the roof? Why couldn’t they just text instead!? Well, have I got an API for you! The Chat (or Conversation) API is a great way to bake messaging functionality into your application, or if you don’t have your own application, store bought is fine. There are a variety of Mitel applications that use the Chat API, so you can easily leverage it to insert messages from an outside source (aka chat federation) or add functionality such as custom routing of messages based on rules you define. This means you could:

  • Create an application that uses the Chat API to pass messages between your chat client and your choice of social media platform.

  • Create a ‘Chat IVR’ that routes messages based on the selections the user makes

  • Build an interactive chat bot

We’ll start by talking about the various resources of the API and the structure of a chat conversation, then discuss the steps required to create a basic working conversation. You’ll notice that when working with the Chat API that you’re never asked to indicate who is creating the conversation, message, or reaction. This is because the API will automatically determine who is creating the object based off which authorization token was used to make the API call. This means you cannot use the API to do something like send messages on behalf of someone else.



Considered the ‘core’ object which all other objects belong to, a chat conversation sets the basic properties that control how your participants will interact and what functionality is available. Conversations can are classified as either “direct” (between two participants), “group” (between 3+ participants), or “stream” (topic based). Stream conversations have a property that flags them as such, whereas direct and group conversations are informally labelled as such based on the number of participants involved. The key properties of a conversation that you can control are (as labelled in the JSON request body):

  • Name - Used for stream conversations, not required for 1-to-1 conversations.

  • LanguageCode - ISO 639-1 standard language codes, such as “en-US” or “de”

  • Stream - Stream conversations are considered ‘topic based’ and participants can be added/removed as needed, and the conversation can be private or public. If set to false (the default), a Direct conversation will be created which has fixed participants and is always considered private (see Visibility).

  • Visibility - Only applies if Stream = true, accepted values are PUBLIC and PRIVATE. Private stream conversations require participants to be invited.

  • Hidden - If set to true, the conversation will not be searchable unless you explicitly search for hidden conversations.

  • GenerateSystemMessages - If set to true, system messages such as “Person X has left the chat” will be automatically added to the conversation.

  • OriginalParticipants - Not to be confused with “Participants” which has been deprecated, this is how you determine who is included in the initial conversation. For practical purposes, the type should always be USER and the participantId is typically the unique identifier for the user, such as the UserId or ContactId (for guests).

  • AccessCode - At the time this article was published, this isn’t used by Mitel apps. You can use it for your own app if desired to control access to stream conversations.

Conversations can also make use of custom tags to whatever end your heart desires.


Messages are really quite straight forward and (I hope) self-explanatory. There are a few properties that you can set when creating a message:

  • Body - The actual content of the message itself.

  • ContentType - Officially, you can use any of the text/* or application/* MIME types, with the most common being text/plain. The API will actually accept almost any value, however it’s up to the chat client to interpret the data. For most Mitel apps, this will be text/plain.

  • FallbackText - Only required if the ContentType is something other than text/plain. In the event that the body of the message can’t be displayed, or the app doesn’t recognize the contentType, this will be the alternative text to display.

  • parentMessageID - At the time this article was written, this isn’t really used by Mitel apps, but you can use this for your own purposes if needed. This will let you nest messages under each other indefinitely (similar to how Reddit works).

  • Tag - Can be set to whatever you like, as long as it’s a string. Only one tag can be set for a message, and the label cannot be changed.

If you’re working with a chat client and keep seeing only the FallbackText, there are a couple possible reasons for this:

  • There’s a problem with the Body of the message

  • The chat client can’t handle the contentType that you set. Commonly occurs if the contentType is set to “text/*”.

For more information on content types, such as the JSON schema for action cards, please see this documentation.


For “direct” conversations (stream=false), participants need to be added when the conversation is created. When you need to modify the participants, a new conversation needs to be created. For stream conversations, you can create a participant and add them to a conversation as needed. The available properties are:

  • UserID - This is the UserId (or ContactId for guests) for the relevant user. If you leave out this field, the API will use the UserId associated with the auth token used to make the call (i.e. You).

  • Status - By default this will be INVITED, however other values are ACCEPTED, DECLINED, and BLOCKED. When BLOCKED, the participant cannot rejoin the conversation.

  • ConversationVisibility - Different from visibility on the conversation itself, this lets you hide the conversation from the participant without actually removing them. Values are HIDDEN and VISIBLE. This lets a user ‘close’ a conversation in the client but retain the ability to view it again, without worrying about the complications that come with removing them entirely (e.g. you can’t re-add someone to a direct conversation).

  • ParticipantType - For practical purposes, this should always be USER.

You also have the ability to show a ‘User X is typing’ message in the chat client by using POST /conversations/{conversationId}/participants/{participantId}/typing. You provide the ConversationId and ParticipantId as path parameters, and in the request body indicate either START or STOP for the ‘action’ property. If you’re using your own custom chat client, you will need to determine how the UI handles these events and when to make them appear/disappear.

Participants also have an important property called “lastReadMessageDate” that should be updated to the precise datetime that the last message in the conversation was read. This will update the unread messages count in Mitel apps, and can be used by custom apps to indicate which messages have been read by a particular participant.


File attachments in a conversation can be up to 10MB, and operations are limited to GET, PUT and DELETE. Unique to attachments, the PUT operation is used to both create and update attachments, as opposed to separate POST and PUT operations. Attachments also contain metadata which you can use for…reasons. The attachments and the metadata are accessed through different paths and operations, as you’ll see in the API documentation.

When creating attachments, the request body will need to be the binary for the attachment itself. In Postman, you can simply choose ‘binary’ under the Body tab and browse to the file. When you GET an attachment, you will get the same binary data back.


In simplest terms, reactions allow a user ‘reply’ to a message with an emoji. When working with reactions, you will typically need to provide the ConversationId and MessageId as a path parameter, and the request body has a single property called “reaction” that will accept any valid unicode character.


Commands are unique in that they are not associated with a particular conversation, but are instead associated with your account. Commands can be invoked from the chat client to perform various actions, such as:

WHO - Runs a GET Participants for the provided conversationId KICK - Deletes the specified participant by participantId LEAVE - Lets you remove yourself from a conversation SHRUG - Sends a ¯_(ツ)_/¯ message, or appends it to the current message.

There are various pre-built commands, however if you’re feeling particularly brave you can try making your own. That goes a bit above and beyond this article, however you can use the pre-built commands as a template if you want to get creative.

User Conversations

While not technically an object, the User Conversations API calls allow you to get a list of conversations for a given user that includes unread message count and a few other pieces of data. You can use this API call to build a list of open conversations, showing how many unread messages are in that conversation for the user.

This particular call is quite performance intensive and can contain a large amount of data, so it should ONLY BE USED ONCE ON CLIENT APP LAUNCH.


We had to make sure that we labelled this section appropriately to avoid any confusion when we write guides in the future for how to create a completely broken conversation. Let’s create a simple direct conversation between two participants, send a message, and react to it.

Step 1 - Create the Conversation In this step, we will assume that you’ve already used something like GET /users to obtain the userId’s for the participants in your conversation. We’ll use the following call with request body:

POST /Conversations

{ "stream": false, "hidden": true, "generateSystemMessages": false, "originalParticipants": [ { "type": "USER", "participantId": "22d77ed5-770d-4044-8b48-0084cedec7ae" }, { "type": "USER", "participantId": "db4a5a46-96fe-4a6c-93de-e60d719aee98" } ] }

Step 2 - Send a Message In this step, we’ll take the ConversationId that would have been provided in the response body when we created the conversation. We’ll use the following call with request body:

POST /Conversations/64d6fa5f-66b7-4b1f-b9ff-dee39b0f8e13/messages

{ "body": "Hello World!", "contentType": "text/plain" }

Step 3 - React to the Message In this step, we’ll take the MessageId that would have been provided in the response body when we created the message, and we’ll also use the ConversationId from the prior steps as well.

POST /Conversations/64d6fa5f-66b7-4b1f-b9ff-dee39b0f8e13/messages/0847e8e3-7c52-4d6d-b760-49a790db463d/reactions

{ "reaction": "😊" }

Optional Steps There are a lot of other things that you can do if you’re working with your own chat client, such as:

Media API Guide

Disclaimer: The Media API will be undergoing additional development to add additional functionality, remove legacy functionality, and be otherwise modified. As such this guide will be continually updated to reflect those changes and reflect the evolution of Mitel's recommended best practices. Existing supported functionality will be retained and will be backwards compatible with future releases of the API to ensure that any custom applications will continue to function as originally designed.

While the Media API has considerable functionality, it is primarily utilized to perform call control. You can route or redirect incoming calls, initiate outbound calls, or even build an application that automatically prank calls Frank in Accounting at random intervals. He microwaved salmon in the office break room, he deserves what he gets.

When we say the Media API has considerable functionality, we mean that there are a lot of resources and routes that are actually intended for very specific use cases by specific Mitel applications. You're never going to use them, you don't have permissions for most of them, and you probably shouldn't be monkeying around with them anyways. In the future some of these resources will become useful, and others will likely be hidden. For now, we will classify them for you as such:

Totally Cool Resources

  • Call History

  • Conference

  • Participant

  • Endpoint

  • Recording

  • Tags

Totally Un-Cool Resources

  • Calls

  • DID Number

  • Extension

  • Prompt

  • VM Boxes

  • Sites

  • Gateway Links

  • PBX Capabilities

  • User

  • Media

This means that you can perform basic call control on an supported on-premise PBX, manage conferences, and access call records and recordings. You cannot currently play media, control a softphone, or access PBX configuration.


Most of you are going to gloss over this, but kudos to those of you who don't! Understanding how the basic workflow for Media call control works is extremely useful for getting the most out of the API and understanding what you can and cannot do.


The above is heavily oversimplified and is probably making our Media API Team Lead's eye twitch, but it does illustrate how an API request to perform a call control action works at a very high level. Once the Media API receives the request, and assuming it hasn't yelled at you for making a bad request, it communicates with the CloudLink Gateway which in turn translates the request into the appropriate CTI commands and issues them to the PBX.

Call Control

The most common use for the Media API is to perform call control actions, be it simply initiating a call or performing logic based call routing of inbound calls. There are a few requirements and caveats when it comes to call control which are important to be aware of:

  • If the device you are trying to control does not support CTI, you cannot control it with the Media API. This largely limits call control to physical desk phones, however other devices may have limited CTI support (such as Call Distribution Elements on the MiVO400). In some cases (like CDE on the MiVO400) you may have to enable this functionality through PBX settings.

  • You cannot (currently) play audio through the Media API to on-premise PBXs. This means you cannot play prompts, listen for DTMF, etc.

  • When performing call control with the Media API where a softphone client such as Mitel One or MOWA is being used, the client may not accurately reflect your call state and audio will not be initialized through the softphone. The physical phones being controlled will still function as expected.

Call control actions for on-premise PBXs are exclusively performed using the /endpoints route. In this guide we will assume that the endpoint is a physical Mitel IP phone. You are able to initiate a call, control an active call, determine or set the state of a phone (available, busy, dnd, away), setup call forwarding, and get active call information. Detailed guidance on some of these activities will be published in the Tutorials section of the Developer Portal, however a few simple examples are provided here.

Making a Call

The following would initiate a call from extension 2001 to extension 2002 using the "Make a Call" call, then timeout after 15 seconds if there is no answer.

POST /endpoints/2001/calls { "to": "2002", "originateTimeout": "15" }

It really is that simple!

Redirecting a Call

One of the more common scenarios you will encounter is the need to redirect or transfer an inbound call, modifying the destination based on some sort of logical criteria such as time of day (e.g. route to X from 0900-17:00, route to Y outside of those hours). In this example we will be redirecting a ringing call on extension 3001 to extension 4001, since transferring the call requires that it first be answered by 3001. This is accomplished with the "Change an Active Call State" call:

PUT /endpoints/3001/calls/123456 { "action": "redirect", "destination": "4001" }

Note: The 'redirect' value is not listed in the API documentation as a valid option, however it does work and will be listed in a future documentation update.

A Note on Call IDs

The Call ID is the unique value assigned to every call. The Media API simply requires that the Call ID be a string, so the format will vary depending on the platform you're working with. For example, calls on the MiVO400 use an integer whereas calls on the Connect platform use a GUID. When initiating a call through the Media API the Call ID will be generated for you automatically and returned by the API on a successful "Make Call" request, however you do have the option of including your own "associatedCallId" for tracking purposes.


The conferences routes will allow you to integrate with applications such as MiTeam Meetings and MitelOne to create and manage conference calls. While you can't exactly run your own conferencing app off these routes, they will allow you to do things like:

• Create a conference plugin for calendar applications • Create your own conference scheduling and management tools • Integrate meeting attendance records into an Human Resource Management System

An important detail to keep in mind is that a conference can be a multi-instance object. In the case of a recurring meeting, each meeting is considered an 'instance' of the same conference. You can get details of specific instances using the /conferences/{conferenceId}/instances/{instanceId} route.

Creating a Conference

Setting up a conference is easy enough with the "Create a Conference" call, in that you only have a few options to choose from. These options are entirely optional and are described as follows:

Id - Unique identifier for the conference. One is automatically generated if not provided (recommended). Name - Label or title for the conference Type - "inviteOnly" requires the participant to be invited, "everyoneAllowed" allows the participant to join as long as they have the meeting Id. Defaults to "everyoneAllowed". lobbyPolicy - Controls the access policy for the meeting lobby. "pstnLobby" will send anyone dialing in through the PSTN into a lobby where they have to be accepted or rejected. "guestLobby" and "nonOwnerLobby" work the same way, in that guests who are not logged in users are directed to the lobby to be accepted or rejected. "noLobby" will allow everyone with the meeting Id and access code to join without first going through the lobby. Defaults to "guestLobby". accessCodeLength - The number of digits for the automatically generated meeting access code, between 1 and 99. Defaults to 9. expiryDateTime - The datetime that the meeting access code will expire. If not provided, no expiration is set. participantId - the principalId of the participants, be it the user's unique userId, or the principalId provided with the /guest/token route. Defaults to include the user who created the conference.

On creation, the Conference will automatically include an "addresses" object that contains dial-in numbers based on where the account is located. Here's an example where we create an "invite only" conference for 2 participants:

POST /conferences { "name": "Cool People Club", "type": "inviteOnly", "lobbyPolicy": "pstnLobby", "accessCodeLength": 9, "expiryDateTime": "2021-12-31T16:00:00Z", "participants": [ { "participantId": "bdbce10a-a2e5-4cc4-ab17-620f4d6c741b" }, { "participantId": "16a52caa-df92-4f18-af0d-e479e7a5049f" } ] }

You will notice that the response body includes dial-in numbers. These are determined based on the region/cloud where the account exists (North America, Europe, Asia-Pacific) and will almost always include a US and Canadian dial-in number by default. You cannot control which dial-in numbers are provided at this time.

Adding a Participant

If you have the conference type set to "inviteOnly" and need to add additional participants, you will need to 'invite' participants before they join which is done by adding them to the participant list of the conference. The API call is very simple, here's where we add a single participant to an existing conference with a conferenceId of '5344397d-f93a-43e2-a009-c1559fb4b394'.

POST /conferences/5344397d-f93a-43e2-a009-c1559fb4b394/participants { "participantId": "16a52caa-df92-4f18-af0d-e479e7a5049f" }

Meeting Attendance

A potentially useful component of the conferences resource is the ability to determine which conferences a user has attended. There are two routes which offer similar data, albeit with a few differences:

/users/{userId}/conferences/ /users/{userId}/attendance

The data returned by these two calls can include detailed information about not only which conferences the user attended, but who else attended them, if there are recordings, and what time the user joined and left the conference.

Recordings, Tags, and Call History

Aside from call control and conferences, there are a few other routes which can be useful:

Recordings The recordings resource is useful for obtaining recordings of conferences/meetings that were generated by those apps. At this time you cannot start/stop recording using the Media API, but you can retrieve them, update the recording metadata, and delete them.

Tags Tags are optional properties that can be assigned to other resources such as calls, conferences, and participants. When creating a tag, you provide the name of the tag and the value itself (eg. A tag labelled, "Project" with a value of, "Next Gen App Development"). The use of tags will allow you to easily search and identify the object you have tagged in the future.

Tags for conferences and participants persist when the conference has been completed, however tags for calls do not persist past the end of the call.

Call History Populated by a PBX connected through a supported CloudLink Gateway, call history allows you to retrieve call records for the user making the request. You can also retrieve specific call records if you have the CallId, as well as a hide individual records as needed.

Notifications API Guide

At first glance, the Notification API seems confusing and complicated. When you take a closer look, you’ll notice that it really IS confusing and complicated. Not to worry though, we’re here to here to walk you through the basics of using this extremely useful API.

At a high level, the Notification API facilitates passing around information about specific events using the “Pub/Sub” model. While that term may sound like something disappointing you might order in a bar at last call, it’s actually short for “Publish-Subscribe”. The general idea is that something that’s carrying out an action (such as an app or one of the other APIs) uses the Notification API to create a “Publication” with information about that action, then the API looks for anyone who has created a “Subscription” that matches the scope of that Publication and sends the appropriate notification. For example, you could:

  • Receive notifications when a chat message is sent using the Chat API

  • Receive a notification when a specific PBX extension is ringing

  • Create a publication from your server side app when a specific event occurs, then your client apps can subscribe to those publications to receive a notification.

You’ll find that the Notification API ends up becoming a component of most custom apps or integrations that you build using CloudLink. So how do you actually use it?


All of the CloudLink APIs create their own publications whenever they carry out an action, and most Mitel apps will do the same. Simply put, publications are how you let the Notification API know that an event has occurred. From there, other apps and services can be notified of that event if they have created a subscription that matches the properties of the associated publication. Conveniently, you can use the Notification API to create your own publications.

Why would someone do that, I pretend you asked? It’s a convenient way to handle certain server-to-client or even client-to-client communications. For example, someone opens their client app and uploads a file to share with other team members. That client app creates a publication of that upload event, and all other clients who have a subscription that matches the properties of that publication will be notified that a shared file was uploaded.

The call to create a publication is pretty straightforward. The mandatory fields in the request body are:

  • Topic - This is the highest level of detail in the publication and typically works as an app/service identifier. For example, “App-Budd-Chat” could be the topic for any events that my custom chat app publishes to the API.

  • Method - This equates to the REST operation that was used for this event, such as PUT, POST, or DELETE. If the event wasn’t a REST operation, you can use something else that makes sense in the context of your app.

  • Subject - The subject typically refers to the resource that you completed the action on. The other APIs will typically use the path of the resource (e.g. /conversations/12345/messages/10004).

  • Content - The actual content of the publication which can be in just about any format you like, including JSON.

There are some other fields in the request body that are optional, such as the AccountID or PrincipalID that act as a sort of signature on the publication (Customer and User, respectively). You can also make use of the PrincipalFilter to control which users will receive a notification for this specific publication, provided that they have a subscription for it in the first place.

WebSockets vs Webhooks

Before we talk about creating your Subscriptions, let's review the two ways that you can receive notifications from the Notification API:

Webhooks - Used for server-server communication, the connection is not persistent. When a notification needs to be sent by the Notification API, it is pushed to the endpoint (URI, IP address, etc) defined in your Subscription. When you have a sever application that needs to receive notifications and action them accordingly, this is the transport method to use.

WebSockets - Used for server-client communication, the connection is persistent and typically maintained throughout the client session. The WebSocket connection is made prior to creating the Subscription. When you have a client application that needs to receive Notifications directly from the Notification API, this is the transport method to use.

While this topic doesn’t cover the witchcraft involved with creating/opening a WebSocket, you can obtain the WebSocket URL with the connections route:

POST https://notifications.api.mitel.io/2017-09-01/connections

The Notification API uses MQTT, so using an MQTT library for your chosen programming language is recommended to help establish the websocket connection. Some further guidance is available here: WebSockets - Basic Workflow


When working with the Notification API, most of the time you’ll be just creating Subscriptions to events that are being published by existing apps and services. The question is, how do you know what to subscribe to? When using POST /subscriptions you will have 4-5 fields in the request body that must be populated to create a basic Subscription. The first three are simple:

  • ApplicationID - This is a unique ID specific to your app that’s required if you’re using a webhook (as opposed to a websocket). You can generate this yourself, just make sure it’s unique, so something like a 128-bit GUID is a good choice. The ApplicationID will be used as a key to encode the body of the webhook, which is then included in the “x-notify-signature” header of the webhook. This essentially acts as a signing key for trust purposes, so you can verify that the webhooks you receive are legitimate. Since you cannot decode the signature, you will need to encode the body of the webhook yourself with SHA256 using the ApplicationID as the key and then compare with the content of the “x-notify-signature” header.

  • DeviceID - For webhooks, this is the target URL for notifications to be delivered to (your server). For websockets, this is the client id you (or your library) created when establishing the MQTT connection.

  • Transport - This will need to be either “websocket” or “webhook” as appropriate.

Now, we have to actually decide what we’re subscribing to. We do that by creating filters for the Topic and Subject of the publication. You can filter on other values as well, but these two are the important ones and require a bit more explanation that the other fields:


The Topic of the publication will be whatever you set it to, if you’re the one who created the publication. Otherwise, the Topic is typically a service identifier. The service identifiers for the CloudLink APIs are:

  • Admin API = platform-api-admin

  • Chat API = platform-api-conversations

  • Media API = platform-api-media

  • Presence API = platform-api-presence

There are identifiers for other APIs and Mitel apps, but the above should cover the vast majority of use cases. If you need to find a Topic to subscribe to for something else specific, let us know here in the forum.


Similar to the Topic, the Subject is set by you…if you’re the one who created the publication. Otherwise, the Subject typically equates to the operation/resource path. For example, when the Chat API is used to send a message the subject would be:


To ensure that you’re subscribing to the correct publication, you’ll want to make use of Regular Expression (RegEx) filters. For example, the following expression would subscribe you to all events pertaining to messages in conversationID 12345:


Here’s a good third party resource for building and testing RegEx filters: https://regex101.com/. If you need to know more about RegEx, a quick Google search will yield a far better information than you can find here.

Other Fields

There are other fields which you can optionally include, such as a filter on the Method (PUT, DELETE, etc) or on the actual Content of the publication by using publicationFilter. You can also set a time to automatically expire the subscription with expiryDateTime. Most of the other fields pertain to specific Mitel mobile apps and cannot be used at this time.

Closing Remarks

There really aren't any closing remarks, this was a clever ruse to trick you into suffering through this joke:

GET /API/v1/jokes { “JokeID”: “100879”, “Title”: “Funniest Joke Ever” } { “JokeID”: “100880”, “Title”: “Decent Joke” }

GET /API/v1/jokes/100879 { “JokeID”: “100879”, “Body”: “Did you hear about the monkeys who shared an Amazon account?” }

GET /API/v1/jokes/100879/punchline { “JokeID”: “100879”, “Punchline”: “They were Prime mates.” }

DELETE /API/v1/jokes/100879 HTTP 200 Ok

Presence API Guide

The concept of presence in Unified Communications can be a bit tricky. If I’m running 5 different apps, a softphone, and a physical set that all have their own status…how on earth are you supposed to manage all that? The Presence API does a lot of the work for you automatically, while still giving you a lot of control if you want to modify the way that presence is handled in your application.

There are two main ‘resources’ that are used by the Presence API:

Presentity - A combination of "presence’ and “entity”, a presentity is any entity that has presence information associated with it. In the context of this article, this typically refers to a user.

Source - Something like a phone or app that is assigned to the presentity that can have its own state, or contribute to the state of the presentity. A source could be a physical phone, a softphone, a user’s calendar, or a specific application.

This guide is broken down into four major sections:

  • An overview of Presentities and the two types of presences they have

  • An overview of Sources and how they impact Presentities

  • A brief overview (and warning) around Locations

  • A practical guide to using Presence

The guide will cover the most important aspects of the Presence API and help you understand how presence is managed, however it only provides guidance on the most commonly used aspects of Presence. We tell you how to control and obtain presence, but instructions for tasks such as creating presentities are not discussed here as they constitute fringe use cases. That’s a fancy way of telling you that we have no idea why you would ever need to do those things, and you probably shouldn’t anyway. It’s like how Linux has commands that will delete the root of the file system without asking for confirmation. Sure you can do it, but you’re going to have a bad time.


Presentities have the following presences (or statuses) to choose from:

  • Available

  • Busy

  • Do Not Disturb

  • Away

  • Unknown

  • Offline

Presentities typically have Sources assigned to them, such as a user (the presentity) who is assigned an extension on a PBX (the source). A presentity can have multiple sources assigned to them, which is why there are two presence types available:

Aggregate Presence - This is set by an algorithm that the Presence service uses to decide what the appropriate presence should be based on the status of all the various sources that are assigned to the presentity. Different sources will have different ‘weight’ that is taken into account by the algorithm, depending on the category of the source. Before you ask, this algorithm is highly classified and (unless you secretly bribe me) I can’t tell you how it works.

Principal Presence - This is an override that you can set which will disable the aggregate presence algorithm and set both the principle presence and the aggregate presence to whatever status you indicate. The aggregate presence will only resume using the algorithm when you set the principal presence back to ‘Available’.

Keep in mind that updating the Principal Presence on a presentity doesn’t typically change the presence on the sources (e.g. phones) that are assigned to the principal (i.e. user). If you built an app where you can set your status to ‘Do Not Disturb’, and did this by setting the Principal Presence to ‘Do Not Disturb’, you may prevent other users from contacting you through your app, but your phone (as a source) would not be impacted. Typically you would need to set the status for each source using the appropriate API, such as using the /endpoint path in the Media API to set the state for a specific extension.

Both presence types can contain the following properties:

  • Status - Available, Unknown, Offline, Busy, DoNotDisturb, Away

  • Reason - Free text field allowing you to provide context around the current status. Some apps may display this information (like an away message), others ignore it entirely.

  • EnteredOn - The datetime of the status change

  • Extended - Free JSON field allowing you to provide your own additional information. Used by various apps for different purposes, such as PBXs attaching the CallId when changing status on starting an inbound/outbound call, or inserting an incremental sequenceId to help order status change events when they are being fired off very quickly and are potentially being received out of order.


While many of the general properties that apply to the presentity itself as self-explanatory, the following properties warrant a bit more explanation:

  • PrincipalId - The UID for the presentity. For users, this will be their UserId.

  • Type - User, Group, or External

  • Links - Contains the URI for the sources contributing to the Aggregate Presence, the source that was used to determine the aggregate presence, and the presentity itself.


Sources are often created automatically by other apps and services, a good example being an extension assigned to a user. Sources are grouped by category which factors into the algorithm used to set Aggregate Presence, with sources in the same category treated equally. The available categories are:

  • Phones - Extension, SIP line in a web app, really anything telephony related

  • ExternalEngines - This is presence supplied by third parties, like presence engines in certain PBXs, or other apps that want to feed presence information into the Presence service.

  • ACD - Related to contact center, such as agent state, queue state, etc.

  • Calendar - Not currently utilized, but you might see it referenced.

When creating a source manually, a sourceId is required. While this can be anything, it should be unique and a 128-bit GUID is recommended.


While locations are currently available to be used with the Presence API, at the time this article was written, they are not currently utilized by any Mitel applications or services.

Since location information cannot be changed once set (it will expire after a set period), it is not recommended to use this component of the Presence API at this time.

Working with Presence

For most potential Presence API use cases, you will only be reading and updating presentities. While you can create and delete presentities, this is typically handled automatically.

Determining Presence

By using GET /presentities, you can obtain data on all presentities that your user has access to. By using GET /presentities/{principalId}, you can obtain presence data for a specific presentity (typically a user).

Important: You should typically use the Aggregate Presence. This will give you the best idea of what the ‘overall’ presence is, and in the event that the Principal Presence has been set, it will update the Aggregate Presence anyways to be the same.

If you need to get status for a specific source, you can do this with GET /presentities/{principalId}/categories/{categoryId}. Since the categoryId consists of relatively static values such as ‘phones’ or ‘ACD’, this is feasible with one API call. That said, since the categories are subject to change, a better practice would be the following:

  1. Use GET /presentities/{principalId} and determine the available categoryId values from the URIs provided in the _links/categories fields.

  2. Use GET /presentities/{principalId}/categories/{categoryId}

Updates to presence can also be seen using the Notification API.

Updating Presence

When you need to update presence, there are two ways to typically handle this depending on what it is you’re trying to accomplish:

Option 1

If you’re trying to set status across multiple apps and devices, you will want to update the presence of the appropriate source directly. This doesn’t mean updating the source object in the Presence API, but updating the ‘root entity’ that is setting the status of that source such as the relevant extension or the app. In theory you could change the status on the source object, but that approach isn’t reliable since it’s not a true reflection of the status of the ‘root entity’ and can be overridden by that entity at any time.

Option 2

Override presence by setting the Principal Presence. This can be useful if you just want to set the status in your app without impacting the status of other sources, with the understanding that all other apps that use the Aggregate Presence to determine status will be impacted until you remove the override by setting the Principal Presence to ‘Available’. As such you need to be very careful about using this method.

Creating a Source

Typically if you are deploying an application where you can set status, you will also want to create a Source for that application using POST /presentities/{principalId}/categories/{categoryId}. You can then update the presence of your source as needed using PUT/presentities/{principalId}/categories/{categoryId}/{sourceId}.

Most of the fields in the request body when creating/updating a source are self-explanatory or have already been covered in this guide, the sourceId is a value that you will need to provide. It must be unique and it is recommended to use a 128-bit GUID.

Service Delivery API Guide

The Service Delivery API provides access to subscription and product information, allowing Mitel Distributors to integrate information for their resellers into an ERP solution. The Service Delivery API uses CloudLink Authentication to obtain a token, specifically through the OAuth 2.0 grants.

The Service Delivery API is effectively a ‘read only’ service that provides access to:

Agreements – Analogous to subscription contracts, the agreement resource contains detailed information about a particular reseller’s subscription(s) and references its associated products.

Products – Provides details of a Mitel product or SKU, including details such as pricing (regular and discount pricing) and user licenses that were agreed to on purchase of the product.

The Service Delivery API is inspired by the Product Inventory Management API REST Specification (TMF637) from TMForum.org, therefore the naming conventions for for various objects and properties may differ from typical Mitel nomenclature.

Getting Started

The expectation is that anyone using the Service Delivery API is a Mitel distributor and as such will already have the necessary account information. If you are not already a Mitel distributor or partner, please reach out to us through here.


The OAuth 2.0 grants (grant_type = password) are used to obtain the bearer token necessary to use the Service Delivery API. The user’s CloudLink account is mapped to an associated account in Mitel’s ERP system which ensures that the user may only obtain information about subscriptions within their account and provides near real-time access to product information.


Agreements are a TMForum concept that is analogous to Mitel’s concept of Subscription Contracts. Agreements contain high level information such as:

  • Id – The unique identifier for the agreement

  • initialDate – The date the agreement was created

  • Status – The current validity of the agreement

  • agreementPeriod – The start and end dates for the subscription/agreement period

  • agreementItems – The products associated with the agreement, including pricing and license count

  • engagedParties – Information about the accounts/organizations involved with the agreement.

There is a considerable amount of information available within the Agreement resource. For more information, please see the Service Delivery API reference.


A product is the actual billable item associated with an agreement. There is considerable information available about each product, but the commonly useful properties are:

  • productSerialNumber – Any serial number associated with hardware or software packages

  • Id – The unique identifier for this specific instance of this product. Note this is NOT the equivalent of a SKU.

  • productPrices – The base pricing and any pricing alterations (i.e. discounts).

For more information, please see the Service Delivery API reference.