web
You’re offline. This is a read only version of the page.
close
Skip to main content

Notifications

Announcements

Community site session details

Community site session details

Session Id :
Power Platform Community / Forums / Copilot Studio / Guide - How to build a...
Copilot Studio
Suggested Answer

Guide - How to build an AI meeting scheduler

(4) ShareShare
ReportReport
Posted on by 113

Hi all,

Copilot Studio has serious potential as a meeting scheduler. For example, if a user asks to set up a meeting “tomorrow morning,” it will search for the first time all participants are free the next day between 9am and 12pm. Say “Wednesday arvo,” and it finds the first available slot that afternoon between 1pm and 5pm.

Beyond just finding times, it also generates the meeting invite—complete with an introduction, agenda, and preparation notes—adds a disclaimer noting it was generated by AI, and asks for approval before sending.

To show it in action, I’m sharing a video demo. For anyone who prefers to skip the reading, I’ve also attached an unmanaged solution with a multi-agent setup, where the Office Assistant agent connects to the Meeting Maestro agent for scheduling meetings. All you need to do is populate the meeting rooms environment variable when importing.

Please note: This solution will need further testing and improvements before being used in a production environment.

For anyone wanting more detail, I’ve included a full write-up with setup details, and YAML for each of the tools.

How to Build an AI Meeting Scheduler with Microsoft Copilot Studio

Arranging meetings can take longer than it should. With Copilot Studio, you can save time and improve meeting quality by automating the meeting setup process from a single prompt.

This solution:

  • Understands time frames like “Monday arvo”, “tomorrow morning”, rather than needing to specify a time like 2pm.
  • Finds a slot and books a room Finds the earliest time within the requested timeframe when all attendees, then selects a free meeting room.
  • Builds a clear agenda and prep notes by understanding the meeting subject, researching it, and adding relevant information.
  • Asks for approval before sending
  • Adds an AI disclaimer to each invite

This solution needs further testing before being used in production, but you can try it out today. If you want to skip ahead, you’ll find the full Power Platform solution—built using a multi-agent setup where the Office Assistant agent connects to the Meeting Maestro agent—and YAML for each of the tools at the end of this post.

See It in Action: From Prompt to Calendar

To demonstrate how this works in practice, here’s a real example using just a single natural language prompt.

🗣️ The Prompt

Schedule a meeting with Nigel M and Lewis S on Monday arvo to discuss how we can save energy in the office.

I didn’t provide a specific time or meeting length — just a list of attendees, a rough timeframe, and a meeting topic. The agent took care of the rest.

🤖 The Agent’s Response

Please provide approval to send out the following meeting invite

Meeting Invite Message

Disclaimer: This meeting invite was generated by AI

Hi Nigel and Lewis,

We are scheduling this meeting to discuss energy-saving strategies in the office. The goal is to identify and implement effective measures to reduce our energy consumption and promote sustainability within our workplace.

Agenda:

  • Introduction and purpose of the meeting (5 mins)
  • Current energy consumption overview (10 mins)
  • Discussion on potential energy-saving strategies (10 mins)
  • Action items and next steps (5 mins)

Preparation:

You may wish to consider any current energy usage data or reports you have access to. Feel free to bring any questions or suggestions you have regarding energy-saving measures.

AI Knowledge:

Energy-Saving Strategies: Implementing energy-saving strategies can significantly reduce operational costs and environmental impact. Common strategies include upgrading to energy-efficient lighting, optimizing HVAC systems, and encouraging energy-saving behaviors among employees.

Looking forward to our discussion.

Best regards,
Dave Barnett


📥 What the Attendee Sees

Here’s how the final invite looks in one of the attendee’s Outlook inboxes:

This shows the end-to-end experience — from natural language prompt to a polished, structured invite in the recipient’s calendar.


🧠 Behind the Scenes in Copilot Studio

The agent uses a combination of tools to make this all happen. Below is a screenshot showing how it orchestrates the workflow:

This gives you a glimpse into the logic behind the scenes — no hardcoded steps, just dynamic orchestration based on the prompt.

Prerequisite: Setting Calendar Time Zones for Room Mailboxes

Before diving in, here’s a tip that could save you hours of troubleshooting if you’re testing this in a development environment.

By default, resource mailboxes (like meeting rooms) in Microsoft 365 tenants are set to Pacific Time, which means they may reject meetings booked from other time zones (such as Australia). In most production environments, these are already configured, but in dev tenants, you’ll need to set the correct working hours and time zone for each room mailbox you use.

You can do this with a single PowerShell command. For example:

Set-MailboxCalendarConfiguration -Identity <room-mailbox>@yourtenant.onmicrosoft.com -WorkingHoursTimeZone "E. Australia Standard Time"

https://learn.microsoft.com/en-us/powershell/module/exchange/set-mailboxcalendarconfiguration?view=exchange-ps

 

How to Build the Agent: It’s All About Tool Descriptions

You might assume the “smarts” are all in the system instructions—but the real magic is in how you describe each tool’s inputs and outputs.

A well-written tool description doesn’t just help the AI fill in the blanks; it also helps the agent figure out the right sequence of actions. For example, if the create event tool says it needs a meeting room, and the meeting room tool says it needs attendees’ free time, the agent can automatically chain these steps together.

In the next section, I’ll walk through the tools I used and how their descriptions drive this orchestration.

Tools Used

  • Search for users Finds attendee email addresses
  • Find meeting times Finds free times for all attendees
  • Find free meeting rooms Finds an available meeting room for the selected time
  • Get calendar ID Gets the organiser’s calendar ID
  • Create event Sends the meeting invite after approval

Search for users

The Outlook actions that find free time and create the meeting request will need the attendees email addresses so this tool looks them up.

This is the Office 365 – Users Search for users (V2) tool, it’s very straight forward tool so needed didn’t any updates other than changing the ‘Search term’ input to “Search string e.g first name, last name full name or even nick name.” to help it find partial name matches.

Find meeting times

This tool finds a time when all attendees are free.
AI needs to provide the attendees’ email addresses, plus a start and end time.

The tool uses UTC times, so (being in Melbourne, Australia) I told the AI to subtract 10 hours from local times before entering them.

The start and end time inputs also include clear explanations for date ranges like “morning” and “afternoon” to help the AI interpret times e.g. the start of a morning in Aus is 9am which is 11pm UTC, then the end of a morning is 12pm which is 2am UTC.

This is the Office 365 Outlook Find meeting times (V2) tool renamed to Find meeting times. The description and then description for each input and output are configured as follows.

Tool Description
  • Find meeting time suggestions based on organiser, attendee availability, and time.
  • This tool cannot find free for any time more than 10 hours prior to now e.g. don’t try using it to search for a time 24hrs prior to now.
  • This tool can also be used to check employees’ calendars to find out when they are free or busy.
  • Always anchor natural language date expressions (e.g., “today”, “this afternoon”, “tomorrow”) to the current day in UTC+10 before converting to UTC. Do not anchor to UTC day.
Input – Required attendees

List of semicolon separated email addresses of the people to find free time for – Search for their email addresses before using this tool. No need to add the organiser, the tool already checks for this person.

Input – Start time
  • You will receive a time expression resolved in UTC+10 (AEST).
  • Always anchor times to the current date in UTC+10.
  • Convert the final time to UTC by subtracting 10 hours.

Start times should reflect:

  • Morning = 9:00 AM AEST → 11:00 PM UTC (previous day)
  • Afternoon = 1:00 PM AEST → 3:00 AM UTC (today)
  • Tomorrow = 9:00 AM AEST next day → 11:00 PM UTC tonight
  • “Wednesday afternoon” = 1:00 PM AEST Wednesday → 3:00 AM UTC Wednesday
  • “Thursday” (time not specified, so choose morning) = 9:00 AM Thursday AEST → 11:00 PM Wednesday UTC
  • Today = 9:00 AM AEST → 11:00 PM UTC (previous day)
  • This = 9:00 AM AEST → 11:00 PM UTC (previous day)
  • Ensure start time is not earlier than now minus 10 hours (UTC).

— If the request doesn’t provide a time, default to 11:00 PM UTC (previous day) —

Input – End time
  • You will receive a time expression resolved in UTC+10 (AEST).
  • Always anchor times to the current date in UTC+10.

End times should reflect:

  • Morning = 12:00 PM AEST → 2:00 AM UTC (previous day)
  • Afternoon = 5:00 PM AEST → 7:00 AM UTC (today)
  • “Tomorrow” (no time) = 5:00 PM AEST → 7:00 AM UTC (today)
  • “Wednesday afternoon” = 5:00 PM AEST Wednesday → 7:00 AM UTC Wednesday
  • “Thursday” (unspecified) = 5:00 PM Thursday AEST → 7:00 AM Wednesday UTC
  • Today = 5:00 PM AEST → 11:00 PM 7:00 AM UTC (today)
  • Ensure start time is not earlier than now minus 10 hours (UTC).

— If the request doesn’t provide a time, default to 7:00 AM UTC —

Input – Meeting duration

Set to 30 minutes when unsure.

Output – Meeting Time Suggestions (UTC)

To help other tools work with this the Meeting Times Suggestions output has been renamed to Meeting Time Suggestions (UTC), and the description set to the following:

This is the time that all meeting attendees and current user are free – All date times are in UTC, not UTC+10

Find free meeting rooms

This tool finds a free meeting room at the time provided by the Find Meeting Times tool.
It’s a copy of the Office 365 Outlook: Find meeting times (V2) tool, just renamed. Its run separately to the find meeting rooms tool as we only need one free room.
Description:
Finds meeting rooms (resource attendees) that are available to book.
Below are the configured inputs and outputs.

Input – Start time

Get the start time of the first meetingTimeSlot in the Meeting Time Suggestions (UTC) (Table) from the Find meetings tool – The time should be in UTC don’t try and convert it.

Input – End time

Get the end time of the first meetingTimeSlot in the Meeting Time Suggestions (UTC) (Table) from the Find meetings tool.

Input – Resource attendees

Instead of letting AI dynamically fill, I’ve set this to a custom value, with the email addresses of all the meeting rooms/resource mailboxes I want it to choose from. This be made smarter to select meeting rooms on the floor where the current user is located.

Input – Minimum attendee percentage

Again this one is set to a custom value of 10 as it only needs to find 1 free meeting room.

Input – Max candidates

Custom value set to 1, as we only need it to provide 1 room.

Input – Meeting duration
  • Duration of the meeting in minutes.
  • Set to 30 mins unless the request specifically states a length.
Output – Resource attendees

To help the create event tool understand the output this is Meeting Times Suggestion renamed to Resource attendees.

Then the description: This provides the resource attendee’s email address and free times.

Get calendar ID

This will provide the current users calendar ID to the create event tool. Its the Office 365 Outlook Get calendars (V2) renamed.

It has the following description: This operation lists available calendars of the current user. Its required for creating meetings, to get the current users calendar ID.

This tool doesn’t have any inputs, however it will return multiple calendars for the current user so the output is set to: “There will be multiple calendars, so only return the calendar that has is “DefaultCalendar”: true”

Create event

This tool sends out the calendar invite, its the Office 365 OutlookCreate event (V4) tool. The following configuration. Along with configuring the description, inputs and outputs, the Additional details section is configured to show the meeting invite to the user and ask for approval before sending.

The output is also configured to confirm the meeting invite has been sent by showing an adaptive card.

You’ll notice its configured to take the start and end time from the find meeting room tool as this is the time that all attendees and the room is free.

The body input, adds the responsible AI disclaimer, and generates the intro agenda etc, in a real world scenario the AI notes section may not be needed but I found it helpful when troubleshooting to show it was looking at the correct subject and so on.

Tool description
  • This operation creates a new event in a calendar.
  • The find free meeting rooms must be run before attempting to use this tool.
Input – Start time

Get the start time from the Find free meeting rooms tool

Input – End time

Get the end time from the Find free meeting rooms tool

Input – Required attendees

Required attendees for the event separated by semicolons

Input – Calendar id

Populate this with the ID from the Get calendar ID tool.

Input – Body

If a meeting topic is mentioned, separate products, services, or platforms from activities (e.g. roadmap, demo), and research only the named tools or concepts based on known capabilities. Do not speculate or assume features.

  • Write in the first person, I not we.
  • Start by greeting the meeting attendees.
  • Describe the purpose of the meeting, expand on theme of the conversation if you can.
  • If the request doesn’t provide a topic, ask for further details.
  • Always reword to sound professional.
  • Create an agenda, with time in mins for each item.
  • Use formatting such as headings and bullet points.
  • Don’t include meeting date and times in the message body.
  • Add an “AI Knowledge” section (Provide details of what your research uncovered, format with smaller subheadings for each product, service, etc).
  • Finish the message with the current user’s full name.
  • Format in HTML for Outlook.
Input – Show as

This is set to the custom value to hard code it to busy – With this events default to free time.

Input – Time zone

This may not be required as I only ever had success with supplying times in UTC, but for completeness I’ve set this as a custom value hard coded to UTC.

Input – Meeting title
  • This is the title of the meeting, try to extract it from the request to setup the meeting.
  • If the conversation only contains date and attendee info, you’ll need to ask the user for more details about the meeting.
  • Meeting titles should have real substance, not just “meeting with bob” etc.
Input – Resource attendee

This is resource email address

Input – Important

Set to a custom value to hard code the important to normal, rather than the default of low.

Additional details

We don’t want the agent to send out the invite until the user is happy with the request so the ask the user before running is set to yes. I initially tried using an adaptive card to show the meeting invite, but the input variables wouldn’t show in it, this is probably for the best as the html formatting wouldn’t show in the text box.

I should add this works well when the user approves, but it doesn’t understand when the user rejects, so I’ve configured the escalate topic to ask what was wrong with the meeting request. This works well when using the agent directly, but in a multi agent scenario it would use the source agents escalate topic, so I need to work more on rejections and modifying meeting requests.

Messsage to display to the user has the following:

<h3>Please provide approval to send out the following meeting invite</h3>
Start: 
Text(DateAdd(Topic.Input.start,10,TimeUnit.Hours), "ddd dd/MM/yy hh:mm AM/PM")
​ - 
Text(DateAdd(Topic.Input.end,10,TimeUnit.Hours), "hh:mm AM/PM")
​
Meeting Room: 
Left(Topic.Input.resourceAttendees, Find("@", Topic.Input.resourceAttendees) - 1)
​
Required Attendees: 
Input.requiredAttendees
string
​
Meeting Title: 
Input.subject
string
​

<H4>Meeting Invite Message</H4>

Input.body
string
​
Completion

To confirm the meeting has been sent the send adaptive card in is set to the following – It probably doesn’t need the body added given the user just reviewed it, but added for completeness in plain text.

{
  type: "AdaptiveCard",
  '$schema': "https://adaptivecards.io/schemas/adaptive-card.json",
  version: "1.5",
  body: [
    {
      type: "Container",
      items: [
        {
          type: "ColumnSet",
          columns: [
            {
              type: "Column",
              width: "auto",
              items: [
                {
                  type: "Icon",
                  name: "calendar"
                }
              ]
            },
            {
              type: "Column",
              width: "stretch",
              items: [
                {
                  type: "TextBlock",
                  wrap: true,
                  text: "Meeting Invite Sent",
                  style: "heading"
                }
              ]
            }
          ]
        }
      ],
      verticalContentAlignment: "Center"
    },
    {
      type: "ColumnSet",
      columns: [
        {
          type: "Column",
          width: "100px",
          items: [
            {
              type: "TextBlock",
              text: "Title",
              wrap: true,
              weight: "Bolder"
            }
          ]
        },
        {
          type: "Column",
          width: "stretch",
          items: [
            {
              type: "TextBlock",
              text: Topic.Output.subject,
              wrap: true
            }
          ]
        }
      ]
    },
    {
      type: "ColumnSet",
      columns: [
        {
          type: "Column",
          width: "100px",
          items: [
            {
              type: "TextBlock",
              text: "Attendees",
              wrap: true,
              weight: "Bolder"
            }
          ]
        },
        {
          type: "Column",
          width: "stretch",
          items: [
            {
              type: "TextBlock",
              text: Topic.Output.requiredAttendees,
              wrap: true
            }
          ]
        }
      ]
    },
    {
      type: "ColumnSet",
      columns: [
        {
          type: "Column",
          width: "100px",
          items: [
            {
              type: "TextBlock",
              wrap: true,
              weight: "Bolder",
              text: "Date & Time"
            }
          ]
        },
        {
          type: "Column",
          width: "stretch",
          items: [
            {
              type: "TextBlock",
              text: Concatenate(Text(DateAdd(Topic.Input.start,10,TimeUnit.Hours), "ddd dd/MM/yy hh:mm AM/PM"), "-", Text(DateAdd(Topic.Input.end,10,TimeUnit.Hours), "hh:mm AM/PM")),
              wrap: true
            }
          ]
        }
      ]
    },
    {
      type: "ColumnSet",
      columns: [
        {
          type: "Column",
          width: "100px",
          items: [
            {
              type: "TextBlock",
              wrap: true,
              text: "Room",
              weight: "Bolder"
            }
          ]
        },
        {
          type: "Column",
          width: "stretch",
          items: [
            {
              type: "TextBlock",
              text: Left(Topic.Input.resourceAttendees, Find("@", Topic.Input.resourceAttendees) - 1),
              wrap: true
            }
          ]
        }
      ]
    },
    {
      type: "Container",
      items: [
        {
          type: "TextBlock",
          text: "Message (In plain text)",
          wrap: true,
          weight: "Bolder",
          horizontalAlignment: "Center"
        },
        {
          type: "TextBlock",
          text: PlainText(Topic.Output.body),
          wrap: true,
          separator: true
        }
      ],
      showBorder: true,
      roundedCorners: true
    }
  ]
}

System Instructions

The following instructions help to strengthen the instructions within the tools descriptions but are not enough on their own.

## 🤖 AI Meeting Strategist – Agent Instructions

### 🧠 Role
You are an AI Meeting Strategist. Your role is to help users schedule high-quality, outcome-driven meetings based on natural language inputs. You create meeting invites that are clear, contextual, and helpful — going beyond logistics to add real value.

---

### 🎯 Responsibilities

- Understand informal scheduling requests like “book a meeting Tues arvo” or “catch-up next Thurs”.
- Expand minimal inputs into professional, HTML-formatted meeting invites that include:
  - A clear explanation of why the meeting matters
  - A structured, outcome-oriented agenda
  - A preparation section that only suggests relevant, optional actions based on known context. Do not reference attachments or links unless explicitly provided. Use a suggestive tone (e.g., “You may wish to consider…”, “Feel free to bring questions about…”).
- Anticipate missing elements (e.g. cost discussion, effort estimation, Q&A).

---

### 🧠 AI Research & Reasoning

- When a meeting topic is provided, identify and separate any named products, services, platforms, or business concepts from meeting types or activities (e.g. roadmap, demo, planning).
- Perform focused research or reasoning on the named items to improve your understanding before drafting the invite.
- Do not simply rephrase the user's request — expand meaningfully by explaining what the topic is, how it works, and why it matters.
- Ensure all content is based on factual, present-day capabilities. Do not speculate or assume features unless they are explicitly known. If the topic involves a tool or system that is being proposed or developed, describe what the underlying platform enables — not hypothetical implementations.


---

### 📌 Behavior Guidelines

- Begin with context: Start the meeting invite with a short paragraph explaining the purpose and importance of the meeting.
- Use Australian English e.g. personalise, not personalize.
- Structure the agenda: Use bullet points for agenda items, focusing on outcomes.
- Include preparation steps: Mention any reading, questions, or decisions needed beforehand.
- Clarify vague requests: Ask follow-up questions if the user's input is incomplete or ambiguous.
- Do not include attendee names or time in the message body — Outlook handles those automatically.
- Always write meeting invites in the first person, as if the user is speaking directly.
- Always Find free meeting rooms tool before using the Create event tool to provide the create event tool with a meeting room/resource attendee.

---

### 🕓 Time Display Rules

- Do not include UTC equivalents or time zone labels like “AEST” or “UTC”.
- Example format: "9:30 AM – 10:00 AM".
- Assume the user understands times are in their local time zone (UTC+10).

---

### 🧾 Message Structure & Formatting

- Format the meeting invite body using HTML.
- Start the body with a large H3 heading with: "Disclaimer: This meeting invite was generated by AI"
- Always write meeting invites in the first person, as if the user is speaking directly.
- At the bottom of the meeting requests show an "AI Knowledge" section, so that you add value to the meeting invite.
- Structure content using:
  - Short paragraphs for background and purpose
  - Bold section headings (e.g., Agenda, Preparation, AI Research Notes)
  - Bullet points for agenda and preparation steps
- Maintain a tone that is concise, polished, and executive-appropriate.
- Avoid emojis, informal language, or decorative formatting.
- Ensure the output renders cleanly in Microsoft Outlook and standard calendar clients.

---

Power Platform Solution

If you’d rather just import the solution, it's attached at the bottom of this post. This is a multi-agent setup, where the Office Assistant agent connects to the Meeting Maestro agent. All you should need to do is enter your meeting room email addresses into the environment variable when you import it.

YAML

Below is the YAML for each tool. You can add the Office 365 tools in Copilot Studio, then replace the YAML as shown here.
Email addresses have been redacted for security, as these are effectively logins.

Search for users (V2)

kind: TaskDialog
inputs:
  - kind: AutomaticTaskInput
    propertyName: searchTerm
    name: Search term
    description: Search string e.g first name, last name full name or even nick name.

modelDisplayName: Search for users (V2)
modelDescription: Retrieves the user profiles that match the search term (V2).
outputs:
  - propertyName: @odata.nextLink

  - propertyName: value

action:
  kind: InvokeConnectorTaskAction
  connectionReference: crad4_meetingScheduler.shared_office365users.shared-office365user-397a30b7-1a6b-4e6f-ac1f-80b65c9782ca
  connectionProperties:
    mode: Invoker

  operationId: SearchUserV2

outputMode: All

Find meeting times

kind: TaskDialog
inputs:
  - kind: AutomaticTaskInput
    propertyName: RequiredAttendees
    description: List of semicolon separated email addresses of the people to find free time for - Search for their email addresses before using this tool. No need to add the organiser, the tool already checks for this person.

  - kind: AutomaticTaskInput
    propertyName: Start
    name: Start time
    description: |-
      - You will receive a time expression resolved in UTC+10 (AEST). 
      - Always anchor times to the current date in UTC+10. 
      - Convert the final time to UTC by subtracting 10 hours.

      Start times should reflect:
      - Morning = 9:00 AM AEST → 11:00 PM UTC (previous day)
      - Afternoon = 1:00 PM AEST → 3:00 AM UTC (today)
      - Tomorrow = 9:00 AM AEST next day → 11:00 PM UTC tonight
      - "Wednesday afternoon" = 1:00 PM AEST Wednesday → 3:00 AM UTC Wednesday
      - "Thursday" (time not specified, so choose morning) = 9:00 AM Thursday AEST → 11:00 PM Wednesday UTC
      - Today = 9:00 AM AEST → 11:00 PM UTC (previous day)
      - This = 9:00 AM AEST → 11:00 PM UTC (previous day)
      - Ensure start time is not earlier than now minus 10 hours (UTC). 

      -- If the request doesn't provide a time, default to 11:00 PM UTC (previous day)  --

  - kind: AutomaticTaskInput
    propertyName: End
    name: End time
    description: |-
      - You will receive a time expression resolved in UTC+10 (AEST). 
      - Always anchor times to the current date in UTC+10. 

      End times should reflect:
      - Morning = 12:00 PM AEST → 2:00 AM UTC (previous day)
      - Afternoon = 5:00 PM AEST → 7:00 AM UTC (today)
      - "Tomorrow" (no time) = 5:00 PM AEST → 7:00 AM UTC (today)
      - "Wednesday afternoon" = 5:00 PM AEST Wednesday → 7:00 AM UTC Wednesday
      - "Thursday" (unspecified) = 5:00 PM Thursday AEST → 7:00 AM Wednesday UTC
      - Today = 5:00 PM AEST → 11:00 PM 7:00 AM UTC (today)
      - Ensure start time is not earlier than now minus 10 hours (UTC).

      -- If the request doesn't provide a time, default to 7:00 AM UTC   --

  - kind: AutomaticTaskInput
    propertyName: MeetingDuration
    name: Meeting duration
    description: Set to 30 minutes when unsure.

modelDisplayName: Find meeting times
modelDescription: |-
  - Find meeting time suggestions based on organizer, attendee availability, and time.
  - This tool cannot find free for any time more than 10 hours prior to now e.g. don't try using it to search for a time 24hrs prior to now.
  - This tool can also be used to check employees' calendars to find out when they are free or busy.
  - Always anchor natural language date expressions (e.g., "today", "this afternoon", "tomorrow") to the current day in UTC+10 before converting to UTC. Do not anchor to UTC day.
outputs:
  - propertyName: emptySuggestionsReason

  - propertyName: meetingTimeSuggestions
    name: Meeting Time Suggestions (UTC)
    description: This is the time that all meeting attendees and current user are free - All date times are in UTC, not UTC+10

action:
  kind: InvokeConnectorTaskAction
  connectionReference: crad4_meetingScheduler.shared_office365.shared-office365-6ff43ac4-0a69-40dd-b57b-57459f986591
  connectionProperties:
    mode: Invoker

  operationId: FindMeetingTimes_V2

outputMode: All

Find free meeting rooms

kind: TaskDialog
inputs:
  - kind: AutomaticTaskInput
    propertyName: Start
    name: Start time
    description: Get the start time of the first meetingTimeSlot in the Meeting Time Suggestions (UTC) (Table) from the Find meetings tool - The time should be in UTC don't try and convert it.

  - kind: AutomaticTaskInput
    propertyName: End
    name: End time
    description: Get the end time of the first meetingTimeSlot in the Meeting Time Suggestions (UTC) (Table) from the Find meetings tool.

  - kind: ManualTaskInput
    propertyName: ResourceAttendees
    value: headoffice12-1@xxxx.onmicrosoft.com;headoffice12-2@xxxxx.onmicrosoft.com;headoffice12-3@xxxx.onmicrosoft.com

  - kind: ManualTaskInput
    propertyName: MinimumAttendeePercentage
    value: 10

  - kind: ManualTaskInput
    propertyName: MaxCandidates
    value: 1

  - kind: AutomaticTaskInput
    propertyName: MeetingDuration
    name: Meeting duration
    description: |-
      - Duration of the meeting in minutes.
      - Set to 30 mins unless the request specifically states a length.

  - kind: ManualTaskInput
    propertyName: ActivityDomain
    value: Work

modelDisplayName: Find free meeting rooms
modelDescription: Finds meeting rooms (resource attendees) that are available to book
outputs:
  - propertyName: emptySuggestionsReason

  - propertyName: meetingTimeSuggestions
    name: Resource attendees
    description: This provides the resource attendee's email address and free times.

action:
  kind: InvokeConnectorTaskAction
  connectionReference: crad4_meetingScheduler.shared_office365.shared-office365-c8b25263-769c-4f6e-8c92-51912970a67d
  connectionProperties:
    mode: Invoker

  operationId: FindMeetingTimes_V2

outputMode: All

Get calendar ID

kind: TaskDialog
modelDisplayName: Get calendar ID
modelDescription: This operation lists available calendars of the current user. Its required for creating meetings, to get the current users calendar ID.
outputs:
  - propertyName: value
    description: "There will be multiple calendars, so only return the calendar that has is \"DefaultCalendar\": true"

action:
  kind: InvokeConnectorTaskAction
  connectionReference: crad4_meetingScheduler.shared_office365.shared-office365-6ff43ac4-0a69-40dd-b57b-57459f986591
  connectionProperties:
    mode: Invoker

  operationId: CalendarGetTables_V2

outputMode: All

Create event

kind: TaskDialog
confirmation:
  activity: |-
    **<h3>Please provide approval to send out the following meeting invite</h3>**
    - **Start:** {Text(DateAdd(Topic.Input.start,10,TimeUnit.Hours), "ddd dd/MM/yy hh:mm AM/PM")} - {Text(DateAdd(Topic.Input.end,10,TimeUnit.Hours), "hh:mm AM/PM")}
    - **Meeting Room:** {Left(Topic.Input.resourceAttendees, Find("@", Topic.Input.resourceAttendees) - 1)}
    - **Required Attendees:** {Topic.Input.requiredAttendees}
    - **Meeting Title:** {Topic.Input.subject}


    **<H4>Meeting Invite Message</H4>**

    {Topic.Input.body}
  mode: Strict

response:
  activity:
    attachments:
      - kind: AdaptiveCardTemplate
        cardContent: |-
          ={
            type: "AdaptiveCard",
            '$schema': "https://adaptivecards.io/schemas/adaptive-card.json",
            version: "1.5",
            body: [
              {
                type: "Container",
                items: [
                  {
                    type: "ColumnSet",
                    columns: [
                      {
                        type: "Column",
                        width: "auto",
                        items: [
                          {
                            type: "Icon",
                            name: "calendar"
                          }
                        ]
                      },
                      {
                        type: "Column",
                        width: "stretch",
                        items: [
                          {
                            type: "TextBlock",
                            wrap: true,
                            text: "Meeting Invite Sent",
                            style: "heading"
                          }
                        ]
                      }
                    ]
                  }
                ],
                verticalContentAlignment: "Center"
              },
              {
                type: "ColumnSet",
                columns: [
                  {
                    type: "Column",
                    width: "100px",
                    items: [
                      {
                        type: "TextBlock",
                        text: "Title",
                        wrap: true,
                        weight: "Bolder"
                      }
                    ]
                  },
                  {
                    type: "Column",
                    width: "stretch",
                    items: [
                      {
                        type: "TextBlock",
                        text: Topic.Output.subject,
                        wrap: true
                      }
                    ]
                  }
                ]
              },
              {
                type: "ColumnSet",
                columns: [
                  {
                    type: "Column",
                    width: "100px",
                    items: [
                      {
                        type: "TextBlock",
                        text: "Attendees",
                        wrap: true,
                        weight: "Bolder"
                      }
                    ]
                  },
                  {
                    type: "Column",
                    width: "stretch",
                    items: [
                      {
                        type: "TextBlock",
                        text: Topic.Output.requiredAttendees,
                        wrap: true
                      }
                    ]
                  }
                ]
              },
              {
                type: "ColumnSet",
                columns: [
                  {
                    type: "Column",
                    width: "100px",
                    items: [
                      {
                        type: "TextBlock",
                        wrap: true,
                        weight: "Bolder",
                        text: "Date & Time"
                      }
                    ]
                  },
                  {
                    type: "Column",
                    width: "stretch",
                    items: [
                      {
                        type: "TextBlock",
                        text: Concatenate(Text(DateAdd(Topic.Input.start,10,TimeUnit.Hours), "ddd dd/MM/yy hh:mm AM/PM"), "-", Text(DateAdd(Topic.Input.end,10,TimeUnit.Hours), "hh:mm AM/PM")),
                        wrap: true
                      }
                    ]
                  }
                ]
              },
              {
                type: "ColumnSet",
                columns: [
                  {
                    type: "Column",
                    width: "100px",
                    items: [
                      {
                        type: "TextBlock",
                        wrap: true,
                        text: "Room",
                        weight: "Bolder"
                      }
                    ]
                  },
                  {
                    type: "Column",
                    width: "stretch",
                    items: [
                      {
                        type: "TextBlock",
                        text: Left(Topic.Input.resourceAttendees, Find("@", Topic.Input.resourceAttendees) - 1),
                        wrap: true
                      }
                    ]
                  }
                ]
              },
              {
                type: "Container",
                items: [
                  {
                    type: "TextBlock",
                    text: "Message (In plain text)",
                    wrap: true,
                    weight: "Bolder",
                    horizontalAlignment: "Center"
                  },
                  {
                    type: "TextBlock",
                    text: PlainText(Topic.Output.body),
                    wrap: true,
                    separator: true
                  }
                ],
                showBorder: true,
                roundedCorners: true
              }
            ]
          }

  mode: Strict

inputs:
  - kind: AutomaticTaskInput
    propertyName: start
    name: Start time
    description: "- Get the start time from the Find free meeting rooms tool"

  - kind: AutomaticTaskInput
    propertyName: end
    name: End time
    description: "- Get the end time from the Find free meeting rooms tool"

  - kind: AutomaticTaskInput
    propertyName: requiredAttendees
    name: Required attendees
    description: Required attendees for the event separated by semicolons

  - kind: AutomaticTaskInput
    propertyName: table
    description: Populate this with the ID from the Get calendar ID tool.

  - kind: AutomaticTaskInput
    propertyName: body
    name: Body
    description: |
      If a meeting topic is mentioned, separate products, services, or platforms from activities (e.g. roadmap, demo), and research only the named tools or concepts based on known capabilities. Do not speculate or assume features.

      - Write in the first person, I not we.
      - Start by greeting the meeting attendees.
      - Describe the purpose of the meeting, expand on theme of the conversation if you can.
      - If the request doesn't provide a topic, ask for further details.
      - Always reword to sound professional.
      - Create an agenda, with time in mins for each item.
      - Use formatting such as headings and bullet points.
      - Don't include meeting date and times in the message body.
      - Add an "AI Knowledge" section (Provide details of what your research uncovered, format with smaller subheadings for each product, service, etc).
      - Finish the message with the current user's full name.
      - Format in HTML for Outlook.

  - kind: ManualTaskInput
    propertyName: showAs
    value: busy

  - kind: ManualTaskInput
    propertyName: timeZone
    value: (UTC) Coordinated Universal Time

  - kind: AutomaticTaskInput
    propertyName: subject
    name: Meeting Title
    description: |-
      - This is the title of the meeting, try to extract it from the request to setup the meeting.
      - If the conversation only contains date and attendee info, you'll need to ask the user for more details about the meeting.
      - Meeting titles should have real substance, not just "meeting with bob" etc.

  - kind: AutomaticTaskInput
    propertyName: resourceAttendees
    name: Resource attendee
    description: This is resource email address, same as you used for the location.
    entity: StringPrebuiltEntity

  - kind: ManualTaskInput
    propertyName: importance
    value: normal

modelDisplayName: Create event
modelDescription: |-
  - This operation creates a new event in a calendar. 
  - The find free meeting rooms must be run before attempting to use this tool.
outputs:
  - propertyName: body

  - propertyName: categories

  - propertyName: createdDateTime

  - propertyName: end

  - propertyName: endWithTimeZone

  - propertyName: iCalUId

  - propertyName: id

  - propertyName: importance

  - propertyName: isAllDay

  - propertyName: isHtml

  - propertyName: isReminderOn

  - propertyName: lastModifiedDateTime

  - propertyName: location

  - propertyName: numberOfOccurences

  - propertyName: optionalAttendees

  - propertyName: organizer

  - propertyName: recurrence

  - propertyName: recurrenceEnd

  - propertyName: reminderMinutesBeforeStart

  - propertyName: requiredAttendees

  - propertyName: resourceAttendees

  - propertyName: responseRequested

  - propertyName: responseTime

  - propertyName: responseType

  - propertyName: sensitivity

  - propertyName: seriesMasterId

  - propertyName: showAs

  - propertyName: start

  - propertyName: startWithTimeZone

  - propertyName: subject

  - propertyName: timeZone

  - propertyName: webLink

action:
  kind: InvokeConnectorTaskAction
  connectionReference: crad4_meetingScheduler.shared_office365.shared-office365-6ff43ac4-0a69-40dd-b57b-57459f986591
  connectionProperties:
    mode: Maker

  operationId: V4CalendarPostItem

outputMode: All
Categories:
I have the same question (0)
  • Suggested answer
    SwatiSTW Profile Picture
    741 Super User 2025 Season 2 on at

    1. Start with Microsoft Copilot Studio and create a new custom copilot. Use the Office 365 connectors for users, calendar, and meeting rooms. Make sure the environment has access to Office 365 Outlook and Office 365 Users connectors.
    2. Configure resource mailboxes for meeting rooms. Use PowerShell to set the correct time zone and working hours. For example
    Set-MailboxCalendarConfiguration -Identity room1@yourtenant.onmicrosoft.com -WorkingHoursTimeZone "E. Australia Standard Time"
    3. Create a tool to search for users. Use the Office 365 Users Search for Users (V2) connector. Configure it to accept a search term like a first name or nickname. Sample YAML
    kind: TaskDialog
    inputs:
      - kind: AutomaticTaskInput
        propertyName: searchTerm
        name: Search term
        description: Search string e.g first name or nickname
    action:
      kind: InvokeConnectorTaskAction
      connectionReference: shared_office365users
      operationId: SearchUserV2
    outputMode: All

    4. Create a tool to find meeting times. Use Office 365 Outlook Find Meeting Times (V2). Configure it to interpret natural language times like “tomorrow morning” and convert them to UTC. For example
    Morning = 9:00 AM AEST -> 11:00 PM UTC
    Afternoon = 1:00 PM AEST -> 3:00 AM UTC
    YAML example
    kind: TaskDialog
    inputs:
      - kind: AutomaticTaskInput
        propertyName: RequiredAttendees
        description: List of attendee emails
      - kind: AutomaticTaskInput
        propertyName: Start
        name: Start time
        description: Start time in UTC
      - kind: AutomaticTaskInput
        propertyName: End
        name: End time
        description: End time in UTC
      - kind: AutomaticTaskInput
        propertyName: MeetingDuration
        name: Meeting duration
        description: Default 30 minutes
    action:
      kind: InvokeConnectorTaskAction
      connectionReference: shared_office365
      operationId: FindMeetingTimes_V2
    outputMode: All
    5. Create a tool to find free meeting rooms. Use Office 365 Outlook Find Meeting Times (V2) but set resource attendees to your meeting room emails. Example
    ResourceAttendees = room1@yourtenant.onmicrosoft.com;room2@yourtenant.onmicrosoft.com
    MeetingDuration = 30
    6. Create a tool to get the organizer calendar ID. Use Office 365 Outlook Get Calendars (V2). Filter for the calendar where DefaultCalendar is true.
    7. Create a tool to create events. Use Office 365 Outlook Create Event (V4). Configure inputs for start, end, attendees, room, and body. Generate the body dynamically with HTML including disclaimer and agenda. For example
    <h3>Disclaimer: This meeting invite was generated by AI</h3>
    <p>Hello,</p>
    <p>This meeting is about energy saving strategies...</p>
    <strong>Agenda</strong>
    <ul>
    <li>Introduction - 5 mins</li>
    <li>Discussion - 10 mins</li>
    <li>Next steps - 5 mins</li>
    </ul>
    Before sending, set confirmation mode so the user approves the draft invite.
    8. Chain the tools in the correct order. First search for users, then find meeting times, then find free meeting rooms, then get calendar ID, and finally create the event.
    9. Test with a natural prompt like
    Schedule a meeting with Nigel and Lewis on Wednesday arvo to discuss energy saving.
    The copilot should resolve “Wednesday arvo” to 1 PM – 5 PM Wednesday, check availability, pick a free room, create a structured invite, and ask for approval.
    10. Improve orchestration by adjusting tool descriptions so the AI understands the dependencies. For example, describe in the meeting times tool that it must resolve natural language dates to AEST then convert to UTC.
    11. Once verified, export your solution and share it. You can reuse the YAML configuration for each tool as described above.
     
  • SPCloudguy Profile Picture
    113 on at
    Rather than a ChatGPT summary, here's the guide in 2 steps:
     
    1. Import the solution attached in the first post and enter at least resource mailbox email address into the environment variable.
    2. Publish both the Meeting Maestro and Office Assistant agents.

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Forum hierarchy changes are complete!

In our never-ending quest to improve we are simplifying the forum hierarchy…

Ajay Kumar Gannamaneni – Community Spotlight

We are honored to recognize Ajay Kumar Gannamaneni as our Community Spotlight for December…

Leaderboard > Copilot Studio

#1
Michael E. Gernaey Profile Picture

Michael E. Gernaey 270 Super User 2025 Season 2

#2
Romain The Low-Code Bearded Bear Profile Picture

Romain The Low-Code... 181 Super User 2025 Season 2

#3
S-Venkadesh Profile Picture

S-Venkadesh 93 Moderator

Last 30 days Overall leaderboard