Synchronize Shifts from Microsoft Teams to Outlook Calendars

Have you ever wanted to synchronize shifts in Microsoft Teams to evens in Outlook calendars? This is how you can do that!


Today, one of my colleagues asked me if it was possible to forward my shifts schedule to my outlook calendar, so that when my work is scheduled in Shifts I receive alerts like for any other calendar event.

Shifts is an app in Microsoft Teams that can be used to schedule work for your team members. But most users sit within their Outlook apps working on emails.

Microsoft Shifts Schedule in Microsoft Teams

My response was of course, Yes! Power Automate can do all of that!

So where do we get started?

First of all it is important to understand how Shifts is used before we look at synchronizing shifts. My colleague stores the task details in the notes of a shift. Sets the start and end time and then that is it.

Now he would manually create a diary entry in the calendar and share that with the persons expected to complete the shift. The events that I will create in this flow are owned by the service account and then the assignee is invited to the event.

Just imaging if he could synchronize shifts to outlook in an automated way!

The Trigger

All flows in Power Automate start with a trigger and this synchronize shifts flow example is no different. I’m running this flow on a schedule of every 30 minutes.

Recurrence trigger

During the development I used a manual starting flow, but in the long run I want to run this flow on a regular basis.

New Line action

Later on in the flow we will need to handle new lines. For this I’m adding a compose action and rename this to New Line.

Synchronize Shifts from Microsoft Teams to Outlook Calendars Microsoft Office 365, Microsoft Outlook, Microsoft Power Automate, Microsoft Teams image

The new Line action may look empty but it is not. It has a new line in it. Just hit that enter key once and you will have the job done!

Getting the shifts

Then I want get all the shifts for everybody starting from today. I will not synchronize shifts that are older.

In the below example of List all shifts, I’ve limited the number of shifts to 50. But this can of course be increased if you need to synchronize shifts beyond the first 50. It will just depend on how much you have to schedule and how many shifts you want to synchronize.

List All Shifts action in Power Automate

I’m also not interested in anything in the past. So I will restrict the action by setting the From start time using the utcNow function.

The odd thing however is that this will now only give me up to 50 shifts somewhere in the future. Most likely the most recently updated shifts. This is not what I want.

By setting the end time as well this problem goes away. So I set the end time to 4 weeks from now using the following formatDateTime expression that then I managed to get a lot more shifts.

formatDateTime(adddays(utcNow(),28), 'yyyy-MM-ddT00:00:00.000Z')

Get the calendars

Then i’m getting my calendars in Outlook for the service account that runs my flow.

Get calendars action to get outlook calendar

This is a bit of a clumsy action as I can’t filter it to the one that I want. But later in my flow I will filter out the right one.

The Get Calendars will give us an array of calendars, even if we only have one calendar that we are interested in. It’s possible to process all of these calendars and use a condition but why not filter the unwanted ones out.

Synchronize Shifts from Microsoft Teams to Outlook Calendars Microsoft Office 365, Microsoft Outlook, Microsoft Power Automate, Microsoft Teams image 1

The compose action after the filter is there to turn the array into a single item. That way we avoid one of those unwanted apply to each steps making the overall Synchronize Shifts to Outlook Calendar events process that bit easier to understand.

All I have to do is use the following expression:


Process the shifts

Now that I’ve got hold of the calendars and the Shifts I can step through my data.

Apply to each Shift

For ease of use I’m now adding my shift by setting a compose action to


This especially helps to debug the flow as the compose action displays my data for the shift that I’m processing.

Start time and end time

I’m now going to collect the start and end time from the shift.

This is where trouble starts. In Shifts, you can have draft shifts and shared shifts. Once a shift has been shared the format of the data changes. this means that sometimes I find my data for a shift in a json property sharedShift and sometimes in draftShift.

To get my start time I’m using the following expressions that will pickup the start time form the shared shift or draft shift.

if(equals(items('Apply_to_each_Shift')?['sharedShift/startDateTime'],null), items('Apply_to_each_Shift')?['draftShift/startDateTime'], items('Apply_to_each_Shift')?['sharedShift/startDateTime'])
Shift Compose action followed by Start time compose

If that wasn’t bad enough. The time is also 1 hour out. My current time zone is British Summer time and this will need to be corrected.

Time and correcting time

Time in this flow is going to be a bit of a pain. As we will see later on not all actions use the same time zone method.

The time that I get for my shifts are 1 hours out. to correct this time I used the following code in a compose action.

Corrected start time to move to right time zone

This is the wrong way of doing things of course. But converting time zones is something that I will address in a separate post.

The expression I used here is:


Then the same needs to be repeated for the end time

End time

Notes in Shifts

And finally I’m going to take the notes from the shift

if(equals(items('Apply_to_each_Shift')?['sharedShift/notes'],null), items('Apply_to_each_Shift')?['draftShift/notes'], items('Apply_to_each_Shift')?['sharedShift/notes'])

Now to make sure that the notes don’t have nay quotes and other characters causing problems I’m adding an compose action with the following expression:

replace(replace(replace(replace(outputs('notes'),'\/','\\\/'),'&','and'),'''','\'''), outputs('New_Line'), ' ')
Synchronize Shifts from Microsoft Teams to Outlook Calendars Microsoft Office 365, Microsoft Outlook, Microsoft Power Automate, Microsoft Teams image 28

Create the calendar events

I will need to know who is assigned to a shift first. All I have is a user id


This user id can be used in the get user profile action to get a user’s email address.

Get user profile

We are now ready to synchronize shifts.

Checking if a Calendar event already exists

I’m going to need 3 actions to check if my event already exists in the calendar.

I will try to find all the events by checking their subject. The subject will be set to the notes that I collected earlier in this flow.

The notes however are not unique. Therefore I will use the shift id which is stored in the body of my event to filter down to a single event in the calendar.

get events

Now all we have to do is count the number of items that the filter array action is returning and we will know if the Outlook calendar contains an event already or not.

The way to count the number of calendar events found is:


Do we need to create or update an event? Well that depends on the number of events found. If we find one then we do an update and otherwise we will do a creation of a new event.

We could use a condition for this as shown below, however I’m preferring the Switch action here!

create event

Why use a switch?

Well I actually might have 3 situations.

  1. The event doesn’t exist
  2. The item already exists
  3. Multiple items are found ( this really shouldn’t happen but just in case…)
Synchronize Shifts from Microsoft Teams to Outlook Calendars Microsoft Office 365, Microsoft Outlook, Microsoft Power Automate, Microsoft Teams image 2

Create an event

If there are none found then the create event is easy. Just set the Subject to the notes. and the start and end time with a bit of date formatting magic will get the times sorted out.


Note that the corrected date is used here.

Synchronize Shifts from Microsoft Teams to Outlook Calendars Microsoft Office 365, Microsoft Outlook, Microsoft Power Automate, Microsoft Teams image 3

Update an event

When there is already an Outlook calendar event available and update is done. But the update should only be done if there is a change to be made.

update event

The conditions in the no updates needed will compare the values of the existing item with the new values. The can be done with the following 4 expressions:

convertToUtc(items('Apply_to_each_2')?['start'],'Central Europe Standard Time','yyyy-MM-ddTHH:mm:00.0000000')


convertToUtc(items('Apply_to_each_2')?['end'],'Central Europe Standard Time','yyyy-MM-ddTHH:mm:00.0000000')


So we start with a condition checking the values of the existing event with the values of the updates that we want to make. If this results in the flow finding updates to be required an update event action will be the final step.

update event details

Some more thoughts

In this post I ignored the try catch pattern, just so that I can keep the post simple. But of course you should implement this with the Try Catch pattern in place so that errors are handled.

Avatar for Pieter Veenstra

By Pieter Veenstra

Business Applications Microsoft MVP working as a Principal Architect at HybrIT Services Ltd. You can contact me using

90 thoughts on “Synchronize Shifts from Microsoft Teams to Outlook Calendars”
  1. Could you post the full flow? Im a little confused on how it all works together when its broken up like this.
    When do I start creating actions out side of “apply to each shift?”

    1. Hi Luka,

      for the first image:

      For the length expressions:

  2. Hi, and thanks for this huge work.
    I tried to implement it, but I encountered an issue, when I tried to save the flow:
    Flow save failed with code ‘InvalidTemplate’ and message ‘The template validation failed: ‘The action(s) ‘escaped_notes’ referenced by ‘inputs’ in action ‘No_update_needed’ are not defined in the template.’.’.

    I check the expression outputs(‘escaped_notes’), but no errors seen…

    Do you have any idea ?


    1. Hi David,

      It sounds, like the action “escaped notes” doesn’t exist. Can you check the compose action that is called escaped notes and check if things are spelled exactly like that ( case sensitive? ). Power Automate will replace all spaces form the actions with an underscore ( _ )

  3. Hi,
    I have a working flow running now, but one issue remains for me for it to perform well. Do you have any tips on how you would go forward to make the flow delete the shifts that are already synced with the outlook calendar if they are removed from the Shifts calendar?

    1. Do you know which part is slow?

      I wouldblook at using Microsoft Graph and remove any nested apply to each steps. Can you share any details on the amount of tems that you are syncing?

  4. Thank you for the response first of all. I don’t think it’s is running slow, so that’s not a problem. The flow synchronizes the shifts of one team (~10 people) for the next 90 days, and recur every afternoon. It synchronizes from UtcNow() and 90 days forward, and it doesn’t add duplicates if there already is a shift there in outlook. The only problem is that if changes are made in the teams shift plan, the flow will not delete the shift that someone else took over. I haven’t tried microsoft graph before, but will check it out.

    1. So far I haven’t looked at deleting. My client didn’t have a need for that. But if any future client wants that then Ibam sure it would be possible to implement with a flow.

  5. For some steps it’s just not clear if it’s part of a step or a new step in itself. Get user profile for example I had it as a new step, but then it automatically gets placed into a new ‘apply to each’ which doesn’t add up to the amount of apply to each actions you seem to have (by my count 3). But if I place it in the previous ‘apply to each shift’ action then it still doesn’t look exactly like your picture where you can clearly see the + button below it whereas I get the ‘add an action’ below this step.

    In the ‘apply to each’ from Finding the right calendar, it’s not clear what goes under the ‘if yes, if no’ part.

    Except for the part of ‘Update an event’, which is done really well, there’s just a lack of overview of what goes where for those who are just starting out with power automate.

  6. So I created a flow based on your guide above, thanks for the work! It has no errors, and it creates calendar events. The only thing is, the calendar events are only one hour of length, and they don’t have any information in them. So no notes/subjects. And also the start time doesn’t match with the shift start time. So apparently something is going wrong, do you have any idea?

    1. Hi Daan,

      You might have to do some of the following additional steps:

      1. Convert time zones to your local time zone. These things can be a real pain. Ideally if anybody is within the same time zone you could make it easy for your self and just add X hours. Alternatively you could use the time zone conversion functions available

      2. In the flow that I created I used the notes. Simply because that is what my client used for the short description of the shifts. Personally I think that the display name of each task would be better. Also because the displayname of the task is shorter. When using the notes potentially you could end up with very long descriptions in Outlook. And outlook would simply chop this to the first 60 characters.

  7. I follow everything exactly but the calendar invite getting created in my outlook instead of the assigned agents. any reason?

    1. The out of the box calendar action cannot get to shared calendars so the flow owner account will need to send the invite.

      If you can use Microsoft Graph to do the updates that problem will go away.

  8. Pieter,

    I am having the same trouble as david5786David above. I get an error message that says‘The template validation failed: ‘The action(s) ‘escaped_notes’ referenced by ‘inputs’ in action ‘No_update_needed’ are not defined in the template.’.’.

    You provided him directions about making sure the escaped notes expression was correct. In the instructions provided on the page, I do not see an escaped notes expression in the instructions above.

    Where do I put an escaped notes expression in the template? Will you please provide me instructions on that? Thanks!

  9. Thanks Pieter,

    I added the escaped notes and input the function replace(replace(replace(replace(outputs(‘notes’),’\/’,’\\\/’),’&’,’and’),””,’\”’), outputs(‘New_Line’), ‘ ‘) however, I received an error stating, “Correct to include a valid refence to ‘New_Line’ for the input parameters of action ‘escaped_notes’.

    Should I be adding another action for New Line? If not, how should I correct this? Thanks!

  10. Hi Pieter, I would like to use your flow, but my flow skills are not too good. Can you export the flow so I can download it?

    1. Sorry, I can’t export this flow as it is part of a bigger client related piece of work. All the steps are included in this post, if you need any help rebuilding the flow then please let me know.

  11. Hi Pieter, I can’t seem to get the Create Event function to work as its giving me an error on the formatdatetime expression. The error code is below. Any thoughts? Maybe it makes a difference (don’t think it does) but i skipped the Checking if a Calendar event already exists steps because we won’t have that issue here. I went straight from Apply to Each Shift to Create an Event…

    Unable to process template language expressions in action ‘Create_event_(V4)’ inputs at line ‘1’ and column ‘33603’: ‘The template language function ‘formatDateTime’ expects its first parameter to be of type string. The provided value is of type ‘Array’. Please see for usage details.’.

    1. Hi Bryce,

      How are you triggering your flow?

      In my case it runs every 30 minutes and every 30 minutes all events are picked up from Shifts. This means that on the second run you would be creating a second copy of the same.

      The error that you are getting is that in the format date function that is used you are referencing some data that is holding multiple objects. What is the exact expression that you are using there?

  12. Hello, Pieter,
    has the post been reviewed since the question of Selena Schipper? Because I am having the same issue of not knowing if it is part of a step or a new step itself.
    Thanks in advance.

  13. Pieter,

    is het mogelijk dat jij mij het zip bestand van jou sjabloon kan sturen?

    ik krijg array fouten en ophalen van outputs dat niet werkt.

    Translation: is it possible to send me the zip file of the template?

    I am getting array errors and the collection of outputs doesn’t work?

    1. Hi Henrico Sorry I cannot send the full template as it is client related.

      What are the errors that you are getting and in which action? I am happy to help with any issues. Feel free to hit the chat on this site and we can have a look at the issues.

  14. But you can make a copy and delete all the client related things and put dummy info in it… but ok

    I have found/made up another flow, except the update.
    so i will have a work around to update it. thanks anyway.

  15. Pieter, I’m having another issue. When I get down to the part where we check to see if a calendar event already exists, when I add each action and its components, its automatically generated an Apply to Each on top of it. My flow setup now looks very different from the screenshots you showed. Any tips on whether this is right or how to get around this?

    I don’t know how to provide you a screenshot of the flow otherwise I would.

  16. Hi Pieter, thanks for your latest reply. I had to use the type the expression method on various parts of the flow including the Get Events action. I get the error of:

    The ‘inputs.parameters’ of workflow operation ‘Get_events_(V4)_2’ of type ‘OpenApiConnection’ is not valid. Error details: The resolved string values for the following parameters are invalid, they may not be null or empty: ‘table’

    Again I used the method you noted in that link so I don’t think there is anything wrong with the expression. Any ideas?

  17. Hi Pieter, thank you again for being so responsive. Just checking in from my question on the 11th. Haven’t yet figured out what my problem here is yet. Thanks in advance!

    1. Sorry, the flow has some client specific parts in it now so I will not be able to share the full flow. But it can be rebuild quite quickly using the steps. If you need any help just let me know.

  18. I’m finding that when i try the flow ‘Get Events’ is coming up with an error

    I believe this is relating to

    Filter Query: Subject enq ‘@{outputs(‘Calendar’)}’

    Please can you advise why this is happening and possibly assist me with correcting this?

  19. Hi Pieter

    “error”:{“message”:”Invalid filter clause”,”code”:”BadRequest”,”originalMessage”:”Invalid filter clause”}

  20. Hi

    I can’t see Subject enq detailed anywhere else in the flow, apart from where it’s referenced under the search which is giving me an error

    Are you able to advise where this should go?

    Also, I’ve since removed this from the search and the flow continues, however it is not feeding through an outlook invite – when the flow reaches the switch it does not register that there are no other clashing invites, so it feeds through to ‘no update required’

    I’ve since tried removing the switch and putting it to just ‘Create Event’ – the flow works on Automate, but nothing is fed through to my calendar even in this way

    Any ideas why this is happening?

  21. hi , I’m having issues with the new line action at the start. In the flow checker its an error where ‘input is required’. and if i remove the new line i have an error with escaped notes (Correct to include a valid reference to ‘New_Line’ for the input parameter(s) of action ‘escaped_notes’.


    1. Hi Dan,
      The new line compose action should just have one empty line in it. So just add the Compose. hit enter in the compose. Then rename it to “New Line” and then all should be ok.

      1. Hi, thanks for your quick reply. I just tried again and I’m having the same results, is there something further down the line that is linked with this empty line that doesn’t like the compose action being empty.


      2. Hi, apologies what you just said worked, thank you very much, but I have 1 more error which is (Correct to include a valid reference to ‘Filter_array_2’ for the input parameter(s) of action ‘Number_of_items_found’.

      3. Can you send the expression in the number of items found. It is referring to the filter array 2 action but the filter array 2 action doesn’t exist.

      4. Hi Pieter

        Thanks for these great, I’m only stuck on the first ‘New Line’ compose, where it still error and asking asking for ‘Inputs’

  22. Hi Pieter,

    Thanks for this clear step by step guide.
    I am planning to use this to synch our shift planning with our employees calendar.

    Before starting I have one question: does the service account used to run the flow have to have specific permissions on employees calendar to create/update(delete) events ?


    1. The account that runs the flow sends out invites and includes the relevant staff members. So there are no specific permissions needed. Potentially using Microsoft Graph you could write events into staff member’s calendars but that would be a slightly different approach.

  23. Hi Pieter!

    This is an awesome guide to add a clearly missing functionality in Microsoft Shift, thank you. 🙂

    I’m struggling at the very last bit with creating calendar event, same as you replied to Bryce Lawlor about. No matter how I follow your suggestions, I can’t seem to get the bit with inputting the calendar ID to work.

    When using the added “apply to each step”, the flow runs successfully, but without adding anything to calendar. When using your suggested workarounds I get several errors, like missing value.

    I see you in your screenshot example have an output of “id” selected, is this from “Filter Calendar” step? Do you have your chosen method of getting this output without adding another “Apply to each” step? I get this id suggested as “ID” with caps and not in small letters, I’m sure there is something different in your setup.

    1. Hi Ulf,

      There are a few methods of avoiding those apply to each steps being added. If you type the expression rather than select it from the Dynamic content you will find that the apply to each steps aren’t added.

  24. I keep getting the following error:

    Flow save failed with code ‘InvalidTemplate’ and message ‘The template validation failed: ‘The repetition action(s) ‘Apply_to_each_Shift’ referenced by ‘inputs’ in action ‘Start_Time’ are not defined in the template.’.’.

    1. Hi Alex,

      In your flow try and find the action called Start Time. Then check the expression used in that. it looks like that is referencing Apply_to_each_Shift however you don’t have an “Apply to each Shift” step in your flow. Maybe you forgot to rename it while the example code did have that renamed?

  25. Hey Pieter, Im really struggling recreating it at several points throughout your guide. E.g. the 3x Get Events – what top do with the first 2? At the same step when I follow the guide a new Apply to each will be created automatically once I touch get Event 3 so later on on the yes / no condition its Apply to Each 3 and also the Condition no cant be ended. Would you be so kind and export your flow as zip file so the basic construct is correct for everyone?

    1. HI Jörg,

      Exporting the flow is a bit tricky as the client has company specific parts built into this flow now.

      Sometimes Power Automate will indeed add apply to each steps automatically. the way to avoid this is by editing the expression rather than selecting Dynamic content. When Power Automate identifies that you select an array it will add those Apply to each steps. This is one of the very annoying things that happen. But by typing expressions these things can be a copied luckily. Please feel free to open a chat and I’m happy to help you get unstuck.

  26. Hi,

    I have this set up great thank you, we are using this with Microsoft bookings for scheduled appointments, is there a way we can get the flow to block out times between engineers shifts and business hours as bookings can use the engineers calendar availability to see when they are available to book.
    So for example our business hours are 7am-7pm and we have a engineer on the early shift 7am-3pm can we get the flow to mark anything between 3pm and 7pm as unavailable for that engineer, as we run three shifts currently
    so this would be very handy if possible!

    Thank you

  27. Why not use the automatic trigger for Shifts (When a Shift is created, updated or deleted)? You will not need to get the list of shifts and check if it is already created in the Outlook Calendar.

  28. Hallo Pieter,

    erstmal vielen Dank für die Anleitung.
    Ich hätte nochmal eine Frage zu den Post. Bei mir ist im Moment das Problem, dass nach get user profile (V2) und Get events wegen ‘ Value id’ immer eine Schleife mit ‘Value’ als Wert automatisch erstellt wird und diese dann 3 mal durchlaufen wird. Dabei ist in dem ersten Durchlauf immer die richtige Aktion und in den zwei danach folgenden Iterationen wird immer ein neuer Termin erstellt. Ich hab mittels eines Union zwischen Filter Calendar und Calendar versucht, Duplikate von Emails zu beseitigen. Das hat nichts gebracht mit der Anzahl der Schleifendurchläufte. Ich habe dann anstelle von ‘ID’ ‘value ID’ benutzt und da bekomme ich nach dem ersten Durchlauf eine Failure von dem 2 Durchlauf. So ist die Anzahl an Termine im Outlook Kalender richtig, aber ich glaube nicht, dass es so gut ist, dass der Test meines Flows im ganzen failed ist. Hätten Sie da noch einen Topp für mich, wie ich es lösen könnte?
    Vielen Dank

  29. Hallo Pieter,
    vielen Dank für die Anleitung. Ich habe im Moment ein Problem mit Terminduplikate im Outlook Kalender. Das Problem besteht darin, dass ich für den Aufruf der Value id immer automatisch eine Schelife mit Value und drei durchläufen erstellt wird. Die erste Aktion ist dabei immer richtig und die 2 danach sind immer ein neues Erzeugen des Termins. Wenn ich anstelle von ID die Value ID verwende, bekomme ich nach dem ersten Durchlauf eine Failure. Also richtige Anzahl an Terminen, aber der Test im ganzen ist dann failed. Hätten Sie eine Ahnung, was ich machen könnte?

    Hello Pieter,
    Thank you for the instructions. At the moment I have a problem with duplicate appointments in the Outlook calendar. The problem is that when I call the value id, a loop with value and three passes is always automatically created. The first action is always correct and the 2 following are always a new creation of the appointment. If I use the Value ID instead of ID, I get a failure after the first run. So the right number of appointments, but the test as a whole then failed. Any idea what I could do?

  30. Hi Pieter. In the part where you are checking if an event already exists by filtering the calendar events by the subject, what if the subject has changed in the shift (requiring an update), surely the event will not be found as no events match the updated subject resulting in a shift being created instead of updated?

    1. Hi Kevin, that is right. It is one of the limitations that I assumed. The tricky thing of the solution is that there is no key that exists in shifts and in outlook. Potentially creating a table to keep things in sync could be a solution for that

  31. Anyone on this comment thread who manages to get this flow running and who can share their version without their own client data would help a lot of people out.

    1. HI Cliff,

      I would probably implement this quite differently now. Since writing the post a few years ago a trigger to fire flows on the creation of a new shift has been added. This will make life a lot easier.

  32. Thanks for replying. I wish I’d looked at it earlier. I just spent several hours trying to make this flow work. But now I’ll do some googling about the Shifts triggers and see if I can find something more elegant. It’s ok, because I want to learn more about how to use power automate, so even repeated failure is still educational.

    Again, thanks for putting me on this.

  33. Hi,
    not sure my first reply went through so i will ask again. would you please post a screenshot of the whole thing. i got lost at the step “Checking if a Calendar event already exists”. is that a new action or a new step.

  34. Hi again, would you please reflect on this step
    there is no reference on your flow to this filter? is that a step you didnt include?

  35. Hi Pieter, i would like to use a similar flow, but i want to do the opposite synchronization. I want to synchronize my calendar to my shifts, so whenever i accept, update or delete a calendar event my shift got updated. Can you please help me?

    Thx for ur support.

    1. It is 3 years ago when I wrote this post. The approach would be very different. When the next client comes around with the same requirements I will have a look at building it as a solution. Solutions didn’t exist back then. Also, the Shifts connector has been extended so much that the flow(s) would be very different.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Discover more from SharePains by Microsoft MVP Pieter Veenstra

Subscribe now to keep reading and get access to the full archive.

Continue Reading