Stop losing action items between your communication channels and your task list. This lesson walks you through building four production-ready Power Automate flows that capture tasks from Outlook, Teams, and Microsoft Forms automatically — and close the loop when work is done. You'll finish with a complete task automation system that spans Microsoft 365.

Here's a situation you've probably lived through: you're in the middle of a focused work session when a Teams message arrives asking you to "follow up on the Q3 budget numbers." You mentally flag it, keep working, and three days later realize it never made it onto your task list. Or the reverse: you receive an action item buried in a long email thread, copy it into Microsoft To Do manually, and then discover the task completion never gets communicated back to the person who assigned it. The task lifecycle — capture, track, close the loop — keeps breaking down because the hand-offs between communication tools and task management are all manual.
Power Automate changes this. By connecting Microsoft To Do's connector to the triggers that already live in your workflow — emails landing in Outlook, messages appearing in Teams channels, responses submitted through Microsoft Forms — you can build flows that create tasks automatically, assign them intelligently, and report back when work is done. This isn't about replacing human judgment; it's about making sure nothing slips through the gaps between your communication channels.
By the end of this lesson, you'll have built three production-ready flows that cover the most common real-world task-capture scenarios, and you'll understand how to track completion status back to the source so the whole loop actually closes.
What you'll learn:
You should already be comfortable with the fundamentals of Power Automate cloud flows. Specifically, you should know how to open the designer, add actions, and test a flow manually. If you need a refresher on how triggers work and what differentiates automated from instant flows, the lesson on Power Automate flow types is a good place to start. You'll also benefit from reading about working with conditions, loops, and variables before tackling the conditional logic in the third flow.
You'll need:
Before building anything, you need a clear mental model of how To Do structures its data, because it maps differently than you might expect coming from other task tools.
To Do organizes work into lists, each of which contains tasks. A task has a handful of standard fields that the Power Automate connector can read and write:
notStarted or completedThe connector also surfaces a task ID and a list ID, which you'll use frequently when updating or reading back tasks after creation.
Note
Microsoft To Do tasks created via Power Automate land in whichever list you specify at the time of creation. There is no concept of creating a task "without a list" — you must always target a named list. Plan your list structure before building flows, because renaming lists later breaks any dynamic references that relied on the list name.
The connector actions you'll use most are:
The connector trigger you'll use for completion tracking is:
completedNow let's build something real.
The first flow handles the email scenario. When you flag an email in Outlook using the built-in flag feature, Power Automate detects it and creates a To Do task with the email subject as the title, the sender as a note, and a due date derived from the email's received time.
In Power Automate, create a new Automated cloud flow. Search for the trigger "When an email is flagged (V3)" from the Office 365 Outlook connector. This trigger fires whenever a message in your mailbox transitions to a flagged state — it doesn't fire for every incoming email, only for the specific moment you set the flag.
Tip
You can also use "When a new email arrives (V3)" with a subject-line filter if your team uses a specific naming convention like "[ACTION]" in email subjects. This works well for shared mailboxes where you want to capture inbound requests automatically without requiring someone to manually flag each one.
The trigger needs no special configuration — just your Outlook connection. The trigger output gives you the following dynamic content fields you'll use downstream:
Subject — becomes your task titleFrom — the sender's email addressReceivedTime — when the email arrivedBody — full email body (plain text version recommended for task notes)After the trigger, add the action Create a task (V2) from the Microsoft To Do connector. Map it like this:
Task List Id: Select "Action Items" from the dropdown (Power Automate fetches your actual list names dynamically when you're authenticated)
Title: Use the Subject dynamic content from the trigger. This gives you a one-to-one match between what you saw in your inbox and what appears in your task list.
Due Date/Time: Here's where you add intelligence. Rather than just copying the received time, you want a task due two business days after the email arrived. Use an expression:
addDays(triggerOutputs()?['body/ReceivedTime'], 2)
This uses the Power Automate formula language to add two days to the received timestamp. If you want to get more sophisticated with business-day logic, the article on scheduling and managing time-based flows covers that pattern in depth.
Importance: Set this to "High" by default for flagged emails — if you're flagging something, it presumably matters.
Reminder Date/Time: Add a reminder for the following morning:
addDays(startOfDay(triggerOutputs()?['body/ReceivedTime']), 1)
Body Content: Construct a meaningful note. Add a Compose action before the task creation and build this string:
concat('From: ', triggerOutputs()?['body/From'],
'\nReceived: ', triggerOutputs()?['body/ReceivedTime'],
'\n\nOriginal message:\n', triggerOutputs()?['body/Body'])
Then reference the Compose output as the Body Content of the task. This preserves the email context directly inside the task note — invaluable when you return to a task two weeks later and can't remember why it existed.
Save and test the flow using the "manually trigger" option isn't available here since this is an event-driven flow. Instead, go into Outlook, flag any email, and watch the run history. The first run usually takes 30–90 seconds to appear. Check that the task appeared in your "Action Items" list with the correct title and note content.
Warning
The "When an email is flagged" trigger can sometimes fire twice if Outlook sends duplicate events during sync. Add a condition after the trigger that checks whether the task title (subject) already exists in the list before creating it. The pattern for this uses "List tasks" followed by a Filter Array action — a technique covered in detail in the lesson on transforming and mapping data with Compose, Select, and Filter Array.
Teams is where a lot of informal work gets assigned, especially in channels where project discussions happen. This flow watches a specific channel for messages that contain a keyword or @mention, then converts those messages into To Do tasks.
The Teams connector offers several triggers. For this use case, you want "When a new channel message is added." This fires for every message posted in a channel you specify. Because you don't want a task for every single message, you'll add a condition immediately after the trigger to filter intelligently.
If you want to understand the full range of Teams automation options — notifications, approvals, adaptive cards — the article on using Power Automate with Microsoft Teams gives you a comprehensive picture.
Create a new Automated cloud flow with the trigger "When a new channel message is added." Configure it:
After the trigger, add a Condition action. You want to check whether the message body contains the text "[TASK]" — a lightweight convention where team members prefix actionable messages with this tag:
Condition: contains(triggerOutputs()?['body/body/content'], '[TASK]') equals true
Under the Yes branch, add your task creation actions. Under the No branch, add a Terminate action with status "Succeeded" — this cleanly exits the flow for non-task messages without creating a failed run.
Key insight
The convention of a prefix like [TASK] needs team adoption to work, but it's surprisingly effective because it makes intent explicit. The alternative — using AI Builder to classify message intent — is more automatic but requires a Premium license. If your team is already on a Premium plan, the article on building flows that call Azure OpenAI and AI Builder models shows you how to replace the keyword condition with a real intent classifier.
When a message contains [TASK], you need to extract the meaningful content. The Teams message body comes as HTML. You'll want to strip the HTML tags to get readable text.
Add a Compose action with this expression to clean the message:
replace(replace(replace(
triggerOutputs()?['body/body/content'],
'<p>', ''), '</p>', ''), '<br>', ' ')
For production use, a more thorough HTML-stripping approach is worth building — the mastering dynamic expressions article has a pattern for this using multiple nested replace() calls.
Now add the Create a task (V2) action, mapped as follows:
Task List Id: "Team Requests" — this is a separate list from personal action items, because tasks generated from Teams discussions may need to be visible to others or handed off
Title: Use another Compose before this step to extract just the task description. The convention is that the message looks like: [TASK] Review the Q3 budget spreadsheet by Friday. Use:
trim(replace(outputs('Compose_CleanMessage'), '[TASK]', ''))
This removes the [TASK] prefix and trims whitespace, leaving you with "Review the Q3 budget spreadsheet by Friday" as the task title.
Body Content: Build context from the Teams metadata:
concat('Posted by: ', triggerOutputs()?['body/from/user/displayName'],
'\nChannel: ', triggerOutputs()?['body/channelIdentity/channelId'],
'\nPosted at: ', triggerOutputs()?['body/createdDateTime'],
'\nMessage: ', outputs('Compose_CleanMessage'))
Due date/time: If someone wrote "by Friday" in the message, extracting that date programmatically is hard without AI. A practical alternative is to set due date to the end of the current week automatically:
addDays(startOfWeek(utcNow(), 1), 4)
startOfWeek(utcNow(), 1) returns Monday of the current week (1 = Monday as start day), then addDays pushes it to Friday. This is a reasonable default; team members can adjust in To Do if the actual deadline differs.
After creating the task, send a reply into the Teams channel so the poster knows their message was captured. Add the Teams action "Reply with a message in a channel" and compose:
concat('✅ Task created: "', outputs('Compose_TaskTitle'),
'" has been added to the Team Requests list.')
This closes the loop visually in the channel — team members can see their action items are being tracked without needing to open To Do directly.
The third source is Microsoft Forms — specifically, a "Request Tracker" form that team members use to submit formal task requests to a shared workload. This scenario is common on operations and support teams where work intake needs to be structured rather than free-form.
Assume your form has these fields:
The article on automating Microsoft Forms responses covers the broader patterns for Forms automation. Here we'll focus specifically on the To Do integration angle.
Create a new Automated cloud flow with the trigger "When a new response is submitted" from the Microsoft Forms connector. Select your form from the dropdown. This trigger fires immediately when someone submits the form.
The trigger itself only gives you the response ID — not the actual field values. You always need a second action: "Get response details" from the Microsoft Forms connector, passing in:
Response Id dynamic content from the triggerAfter this action, all your form field values are available as dynamic content.
The form has a Priority choice field with values "Low," "Normal," and "High." The To Do connector's Importance field accepts exactly these same values — but only if they match the connector's expected format. This is where beginners often get tripped up.
Add a Switch action (found under Control) branching on the form's Priority answer:
Case "Low": Set a variable varImportance to low
Case "High": Set a variable varImportance to high
Default: Set varImportance to normal
Initialize this variable at the top of the flow (before the trigger actions, in a dedicated Initialize Variable action):
varImportancenormalTip
Always initialize variables at the very top of your flow, before any branching logic. Power Automate requires variable initialization to occur before the variable is referenced anywhere — if you try to initialize inside a condition branch, the flow will error out at runtime when the other branch runs first.
Now create the task:
Task List Id: "Team Requests"
Title: The form's "Task Title" field directly — outputs('Get_response_details')?['body/r4f8a...'] (the actual field ID will differ; Power Automate shows you the field name in the dynamic content panel)
Due Date/Time: The form's "Requested Due Date" field. Note that Forms returns dates in ISO format, which To Do accepts natively — no conversion needed.
Importance: The varImportance variable you set in the Switch
Body Content:
concat('Submitted by: ', outputs('Get_response_details')?['body/responder'],
'\nDescription: ', outputs('Get_response_details')?['body/rDescription'],
'\nPriority: ', outputs('Get_response_details')?['body/rPriority'],
'\nRequested due: ', outputs('Get_response_details')?['body/rDueDate'])
After creating the task, the "Create a task" action returns a task ID. This is important: if you want to track completion and report back to the form submitter, you need to store this ID somewhere persistent.
The most practical approach at this scale is to write the task ID to a SharePoint list row that maps response IDs to task IDs. Add a SharePoint "Create item" action with:
Response Id from the Forms triggerThis SharePoint row becomes the bridge for your completion-tracking flow. The article on automating SharePoint list item lifecycle management goes deep on this exact pattern.
The three flows above handle task creation. This fourth flow handles the other half of the problem: what happens when a task is actually completed?
Create a new Automated cloud flow with the trigger "When a task is completed" from the Microsoft To Do connector. Configure it to watch your "Team Requests" list.
This trigger fires whenever any task in that list is marked complete — it gives you the task ID, title, and completion timestamp.
With the completed task's ID, query your SharePoint "Task Tracker" list to find the matching row. Add the SharePoint action "Get items" with a filter:
Filter Query: ToDoTaskId eq 'dynamic task ID from trigger'
This returns the row you created in Flow 3, including the submitter's email address and the original form response ID.
Warning
The "Get items" filter query uses OData syntax. String values must be wrapped in single quotes. If the task ID contains special characters, the filter may fail silently and return zero results. Always add a condition after "Get items" that checks length(body('Get_items')?['value']) is greater than 0 before proceeding, and handle the empty-result case with a Terminate action so you have a clear diagnostic in run history.
Using the submitter email from the SharePoint lookup, send an Outlook email confirming the task is done:
Add the Office 365 Outlook "Send an email (V2)" action:
first(body('Get_items')?['value'])?['SubmitterEmail']concat('✅ Completed: ', triggerOutputs()?['body/title'])<p>Your request "<strong>@{triggerOutputs()?['body/title']}</strong>" has been completed.</p>
<p>Completed at: @{triggerOutputs()?['body/completedDateTime']}</p>
<p>If you have follow-up questions, reply to this email.</p>
Simultaneously, update the SharePoint row's Status column to "Completed" using the SharePoint "Update item" action, referencing the row ID from the Get items result.
Notice what this fourth flow does: it reads from To Do, looks up data in SharePoint, sends email via Outlook, and could easily be extended to post a Teams notification in the project channel. This is a classic multi-connector pattern. The article on chaining Microsoft 365 connectors in a single flow has detailed guidance on keeping data consistent when you're passing values across multiple service boundaries.
Key insight
The real power of this four-flow system isn't any individual flow — it's the fact that they share a common data structure (the SharePoint Task Tracker list) that ties them together. The task ID is the thread connecting creation to completion. Any time you build a multi-flow system, identify that connecting key early and make sure every flow that touches the system reads and writes it consistently.
The flows above use two lists deliberately: "Action Items" (personal) and "Team Requests" (shared/visible).
Microsoft To Do's sharing model works through task list sharing, which is a feature in the To Do app itself — you invite others to a list, and they can see and update tasks in it. Power Automate operates on whatever lists are accessible to the authenticated user. If your flow runs as your account and you've shared "Team Requests" with your team, anyone you've shared with can see tasks that flow creates.
For true team-scale task management where multiple people need to create, assign, and track tasks across a project, Microsoft Planner is a better fit than To Do. Power Automate has a full Planner connector with richer assignment and board-management capabilities. If you're building task automation for team projects rather than personal capture, the article on automating Planner task creation, assignment, and status updates covers that path in depth.
The To Do approach works best for:
Planner works better for:
Build a complete version of the Forms-to-To Do flow, then extend it with a completion notification. Here's the full exercise:
Step 1: Create a Microsoft Form called "Work Request Tracker" with these fields:
Step 2: Create a SharePoint list called "Request Log" with these columns:
Step 3: Build Flow A (Forms → To Do → SharePoint):
varImportance = "normal"varImportance appropriately in each caseStep 4: Build Flow B (To Do completion → Email + SharePoint update):
Step 5: Submit a test form response, verify the task appears in To Do, then complete the task in To Do and verify the email arrives and the SharePoint row updates.
The entire cycle — form submission to completion email — should take under five minutes of actual automation time once the flows are live.
The task gets created but the due date is wrong by several hours.
This is a time zone issue. Power Automate internally uses UTC, and To Do displays dates in the user's local time. If you're calculating due dates using utcNow() without a time zone conversion, tasks will appear due at an unexpected hour. Use the convertTimeZone() function to localize before passing to the task:
convertTimeZone(utcNow(), 'UTC', 'Eastern Standard Time', 'yyyy-MM-ddTHH:mm:ss')
The "When a task is completed" trigger fires but my Flow B never runs. Check that the list you selected in the trigger matches exactly where tasks are being created in Flow A. If you renamed a list in To Do after setting up the trigger, the connection breaks silently. Delete and re-add the trigger with the correct list name.
The Teams message body contains HTML tags in the task note.
The Teams connector returns message content as HTML, not plain text. Use nested replace() calls to strip common tags, or accept that notes will contain some markup. For production flows, consider using the decodeUriComponent() and regex-like patterns available in Power Automate expressions — the mastering dynamic expressions article has a thorough treatment.
The "Get items" SharePoint filter returns zero results even though the row exists.
OData filter queries are case-sensitive on column internal names, not display names. Open the SharePoint list settings and check the actual internal name of your ToDoTaskId column — it may be ToDoTaskId0 or similar if SharePoint auto-renamed it. Use that exact internal name in the filter query.
The flagged-email flow creates duplicate tasks. The Outlook flagged-email trigger can fire multiple times for a single flagging event due to sync behavior. To prevent duplicates, add a "List tasks" action before "Create a task," then use Filter Array to check if any existing task title matches the email subject:
equals(item()?['title'], triggerOutputs()?['body/Subject'])
If the Filter Array result has length greater than 0, skip creation. This is idempotency protection — an important pattern for any event-driven automation.
Warning
Don't use the To Do "List tasks" action on large lists for deduplication — it returns all tasks and the Filter Array runs in memory, which degrades as the list grows. If your Action Items list regularly has hundreds of open tasks, build a SharePoint index as shown in the Forms flow and query that instead. It's faster and doesn't depend on To Do's pagination behavior.
My flow fails with "The provided list does not exist" intermittently. The Microsoft To Do connector occasionally returns this error when the service is temporarily inconsistent after a task list update. This is a transient failure, not a configuration problem. Add retry logic to your task creation action by configuring the action's settings (the three-dot menu on the action) to retry up to 3 times with a 60-second interval. The article on error handling and retry patterns covers this configuration in detail.
You've now built a complete task automation system across Microsoft 365: emails become tasks when you flag them, Teams messages with a [TASK] prefix land in a shared list, and Forms submissions create structured work items with priority mapping. The completion-tracking flow closes the loop by updating your request log and notifying the original submitter automatically.
The patterns here — trigger → extract → transform → create → track — are the foundation of almost every workflow automation you'll build. What changes between use cases is the trigger source and the shape of the data; the underlying logic stays consistent.
For your next steps:
Extend the Teams flow to parse the @mention of a specific person and assign the task to them. This requires reading the mention entity from the Teams message body — a more advanced parsing exercise.
Add approval before task creation for the Forms-based flow if your team needs a manager to approve work requests before they enter the queue. The pattern for this is covered in building approval workflows with Power Automate.
Explore Planner if your team task management is outgrowing the To Do sharing model. The Planner automation article shows you how to migrate this same trigger pattern to Planner's richer task model with buckets, assignments, and progress tracking.
Build a daily digest flow that queries your "Action Items" list every morning, filters for tasks due today, and emails you a summary. This uses the To Do "List tasks" action combined with a recurrence trigger — a satisfying capstone to the system you've just built.
The task capture problem is solvable. Once your flows are live, the mental overhead of "did I write that down?" drops dramatically — and that cognitive space is better spent on the actual work.