The Ultimate Teams Shared Voicemail Solution

In June last year I wrote about handling Teams shared voicemail more efficiently. Although the principle is the same today, a couple of…

In June last year I wrote about handling Teams shared voicemail more efficiently. Although the principle is the same today, a couple of things have changed which has allowed me to build a much more elegant version of this Power Automate flow.

About a year ago, I also wrote about shared voicemail now supporting not just Microsoft 365 groups but also distribution lists and mail enabled security groups. While it’s great to have more options, that doesn’t solve the issue at its core.

My main complaint about shared voicemail in Teams is that it’s hard to keep an overview over which voicemails have already been processed (as in the person who left the message has been called back). You either train people to regularly check their Microsoft 365 group inboxes or you enable follow in inbox for all the group’s members. Follow in inbox will make sure that no voicemail/email gets lost, but it also means that more communication is required within a team to make sure that the caller is not called back multiple times or not at all (because everybody believes that someone else will call back). My Power Automate Flow solves this issue by delivering the voicemails directly into a Teams channel.

Call Flow Diagram

This is the example scenario. A relatively simple call flow with an auto attendant which routes calls to a call queue during business hours and redirects to a second auto attendant after hours. The second auto attendant offers callers a choice to either leave a message or get connected to the on-call service.

I purposely used a dedicated auto attendant for the after-hours so it can be re-used if the call flow is extended in the future. You can see that the after-hours call flow of the main auto attendant (PS Test EV Enabled AA) only plays the greeting Welcome to You’re calling us outside of our business hours. And PS Test EV Enabled On Call AA does the actual after-hours routing. By splitting the greeting, I could also use the IVR of the second auto attendant for another call queue or for holidays configured on the main auto attendant. This way, I won’t have to build a new/the same IVR per holiday call handling which saves time and reduces management effort.

The second reason I built it like this is that the name of the auto attendant includes the keyword On Call. Because the on-call call queue uses the same naming convention. This means that the email containing the voicemail includes the name of the voice app in which the call was sent to voicemail, which makes it easy to determine if a voicemail was left during business hours or during on-call/after hours.

Shared Voicemail

As you can see in the diagram, in every case where a call cannot be answered, it goes to the same shared voicemail group. This Microsoft 365 group is the same group that’s linked to the Team which also hosts the two voice enabled channels which are linked to the call queues.

Instead of relying on people either checking the group mailbox for new voicemail messages or subscribing to the group mailbox, we’re going to build a Power Automate flow to deliver voicemail messages directly into a Teams channel.

The first thing we need to do is to create or define a shared mailbox so we can subscribe it to new messages of the M365 group. The flow will only process emails which have attachments, and the attachments name must end with .mp3 . But I still strongly recommend to create a dedicated shared mailbox which will only receive emails from the M365 group. To keep things clean, I would also deter from sending any kind of other emails to the M365 group. If you also use a dedicated Team for your call queues, that shouldn’t be an issue.

Once the shared mailbox is created, we need to give the user which will own the Flow full access to it.

You will need the PowerShell module ExchangeOnlineManagement and need to run Connect-ExchangeOnline before you can run these commands.

Create shared mailbox

New-Mailbox -Shared -Name “PS Test EV Enabled SharedVoicemail” -DisplayName “PS Test EV Enabled SharedVoicemail” -Alias “PSTestEVEnabledSharedVoicemail”

Start-Sleep -Seconds 30

Grant full mailbox access to the flow owner

Add-MailboxPermission -Identity “PSTestEVEnabledSharedVoicemail” -User “” -AccessRights FullAccess -InheritanceType All

Subscribing the Shared Mailbox to the M365 Group Inbox

Next, we add the newly created shared mailbox as a member of the group and then also subscribe it to the group’s inbox.

Add the shared mailbox as a member to the M365 group

Add-UnifiedGroupLinks -Identity $groupId -LinkType Members -Links "PSTestEVEnabledSharedVoicemail"

Subscribe the shared mailbox to the M365 group’s emails (enable follow in inbox)

Add-UnifiedGroupLinks -Identity $groupId -LinkType Subscribers -Links "PSTestEVEnabledSharedVoicemail"

We can verify that the shared mailbox is indeed subscribed by using this code. (I have no idea why it says UserMailbox though…)

PS V:\GitHub\TeamsPhoneAutomation> Get-UnifiedGroupLinks -Identity $groupId -LinkType Subscribers

Name                               RecipientType  
----                               -------------  
PS Test EV Enabled SharedVoicemail UserMailbox

The idea here is that only the shared mailbox gets new voicemails in its mailbox. None of the users should get the voicemails in their personal inbox because they receive them in Teams. This allows for much better and efficient collaboration.

The Result

This is the Team I created for this call flow. Channels 1 and 2 are linked to the queues which receive calls. 3 and 4 are the ones which receive the voicemails. As explained earlier in this article, the flow can easily determine at which stage in the call flow (during business hours or on-call/after hours) a voicemail was left. That’s why we have two separate voicemail channels.

Voicemail During Business Hours

Adaptive Cards in Teams now support media. This means that we can now embed an audio file which is stored on SharePoint directly on an adaptive card and we’ll get a neat little Stream powered media player inside Teams.

The card will include the number or the email address of the user who left the voicemail.

After members of the channel have listened to a voicemail, they can call the person who left a voicemail back directly from the channel.

Updated Adaptive Cards

Once a team member has called back, a card can be marked as completed. This will update the card so that all other team members can see that there’s no action left to take. It’s even possible to see who has marked a card as completed.

Completed cards don’t show the media player anymore. However, the Voicemail Details button will reveal the original card including the audio file again. This is helpful in case somebody needs to listen to a message again or the Call Completed button was clicked by accident.

Users don’t really need to worry about this, but technically, the files get uploaded to the channel’s folder in the Teams’ SharePoint site.

Completed cards also feature a Show Metrics button which will tell you when a voicemail was received, when it was completed and how many hours and minutes it took until it was marked as completed. Based on that, it will display a different emoji.

Less or equal 1 hour = 😊
Less or equal 2 hours = 😐
More than 2 hours = 😞

Voicemail During After Hours (On Call)

Voicemails which were left outside business hours will go into the Voicemail On Call channel. When a voicemail is left during on-call hours, it means that the people who are on-call missed a call and should call back as soon as possible. Thus, we want to have an appropriate alerting system in place.

I’m using a shift which is linked to my Call Queue Team which defines which user is on call on which days.

The great thing about using Shifts is that this will automatically create a Tag with the Shift name in the Team which is linked to the shift and update the members of the Tag dynamically. In other words, the tag always includes only the users which have a currently active shift assigned.

When a new voicemail is received in the On Call channel, all members of the On Call Shift Tag will get notified about new voicemails every 15 minutes for 4 hours if a card is not completed sooner. As soon as card is completed, the notifications stop.

In case of an on-call voicemail the flow will take at least 15 minutes to complete, even if the card was marked as completed sooner. This is because there’s a delay action which waits for 15 minutes before it checks again if the card has been completed.

Because the flow is still waiting on the card to be completed in the on-call scenario, there’s no way to get the message Id of the adaptive card that was sent to the channel from a subsequent step in the flow. Instead, the flow needs to retrieve the latest message which was sent to the channel in a parallel branch.

Because of that, I strongly recommend turning on channel moderation and prohibit members from posting new messages to the On Call Voicemail channel. This way, we can make sure that the newest message in the channel is always the adaptive card which was sent by the flow and not something posted by a user.

Channel members should only be allowed to reply to messages so that they can e.g. mention a team member who should carry out a call back action.

I changed the delay action from 15 minutes to 15 seconds for demonstration purposes. Here you can see three Teams activity feed notifications which link directly to the channel message containing the voicemail. Using a Teams activity feed notification instead of a channel message reply has two main advantages. It doesn’t clutter the channel feed and members don’t need to specifically enable notifications for all channel messages to get the alerts.

The Flow

The flow has become quite large and Power Automate keeps reminding me that it contains too many actions to use the new AI-powered designer. 🙃

To make it easier for you, I exported the flow and made it available for download here. To import it, go to and select Import and then Import Package (Legacy).

Select an existing or set up a new connection for each of the resources listed before you click Import.

Unfortunately, you still need to expand every action and check if you need to modify/update the values. To make it easier for you, I added a note to all the actions which need to be updated. Here are some examples.

You get the gist. At least you won’t have to create all the actions manually.


Even though it took me days to finally get this right, I had so much fun building this. Besides that, I also learned a lot of new stuff about Power Automate. In fact, this was the very first parallel branch I ever built. It’s awesome that Adaptive Card in Teams now support media. I think this is a really great example of how powerful Adaptive Cards can be. They not only look beautiful, but they also provide a fantastic user experience since users will be able to interact with them directly in Teams.

I’d be lying if I said that I wasn’t a little proud of the flow and process I built. The only thing I’m still missing with this solution is to be able to create Teams deep links which also specify which outbound caller id should be used when a call back link is clicked. Wouldn’t it just be great if we could say that users should always use the queue’s/attendant’s phone number when they call somebody back from a voicemail card?

What’s funny is that the Teams mobile apps actually prompt users which have multiple calling line identities assigned to select an identity before the call is made.

As you can tell, I’m pretty excited about all of this. I hope you like it too and that you can implement it for your users or customers as well.

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Hosted on GitHub Pages
Built with Hugo
Theme Stack designed by Jimmy