Microsoft 365 actions
Who this is for: people and AI assistants who write workflow steps that read or change Microsoft Entra ID, send email from a shared mailbox, or move files to and from SharePoint and OneDrive, for example to check leavers, offboard someone or file a monthly report.
Before you start: you need a Microsoft 365 connection. See Connect Microsoft 365.
These actions are part of our curated platform library. Use them in any workflow as uses: library/ms365.<name>@v1. The actions in the first table only read. They never change anything in your tenant. The actions in the second table change one thing each, see Actions that change data. The third table lists the actions for email and files. Each action uses one connection slot, graph, which takes a connection made from the Microsoft 365 (Microsoft Graph) template. You find the same actions in the app under Library, and your AI assistant reads their full contracts with the MCP tool get_action.
| Action | What it reads | Microsoft Graph application permission | Licence |
|---|---|---|---|
library/ms365.get-user@v1 |
One user | User.Read.All |
none |
library/ms365.list-users@v1 |
Users, optionally filtered | User.Read.All |
none |
library/ms365.list-user-groups@v1 |
The groups a user belongs to | Directory.Read.All (direct) or User.Read.All (also nested) |
none |
library/ms365.list-group-members@v1 |
The members of a group | GroupMember.Read.All, and User.Read.All for user details |
none |
library/ms365.list-sign-in-activity@v1 |
When users last signed in | AuditLog.Read.All and User.Read.All |
Entra ID P1 or P2 |
library/ms365.list-sign-ins@v1 |
Sign-in log records in a time window | AuditLog.Read.All |
Entra ID P1 or P2 |
| Action | What it changes | Microsoft Graph application permission |
|---|---|---|
library/ms365.disable-user@v1 |
Turns off one account’s sign-in | User.EnableDisableAccount.All and User.Read.All, and maybe an admin role |
library/ms365.revoke-sign-in-sessions@v1 |
Signs one user out everywhere | User.RevokeSessions.All |
library/ms365.remove-group-member@v1 |
Removes one member from one group | GroupMember.ReadWrite.All |
| Action | What it does | Microsoft Graph application permission |
|---|---|---|
library/ms365.send-mail@v1 |
Sends one email from a mailbox, with files of the run attached | Mail.Send, limited to the mailbox with RBAC for Applications |
library/ms365.list-files@v1 |
Lists one folder in SharePoint or OneDrive (only reads) | Files.Read.All |
library/ms365.download-file@v1 |
Saves one SharePoint or OneDrive file as a file of the run (only reads) | Files.Read.All |
library/ms365.upload-file@v1 |
Uploads one file of the run to SharePoint or OneDrive | Files.ReadWrite.All, and Sites.ReadWrite.All for files over 4 MiB |
Grant only the permissions for the actions you use, under API permissions in your app registration, and select Grant admin consent.
What every action does the same way
Section titled “What every action does the same way”- Only the data you ask for. Each action asks Microsoft for a short list of properties by default, such as the user’s ID, name, user principal name and whether the account is enabled. Use
select(orfieldsfor sign-ins) to choose others. Ask only for what the workflow needs: it keeps personal data out of your runs and makes Microsoft slow you down less. - Pages, with a limit. Microsoft returns long lists a page at a time. The actions read the pages for you, up to
max_pagespages andmax_itemsitems. When more was left, the output saystruncated: true. Narrow thefilter, or raise the limits. - Only Microsoft’s address. The actions follow a next-page link only when it points to
https://graph.microsoft.com/v1.0. Any other link stops the step, and Liitora never sends your connection’s sign-in to it. - Filters stay filters. A
filteris an OData filter, such asaccountEnabled eq false. It is sent only as the filter value, so it can’t change what the action calls. - When Microsoft slows you down. If Microsoft answers
429 Too Many Requestsor503 Service Unavailablewith a wait time of up to a minute, Liitora waits and tries again, within the step’s timeout. If it keeps happening, the step fails and says so. Addretry:to the step, or run the workflow at a quieter time. - Errors. A failed step says what it tried to read, the HTTP status and Microsoft’s error code, and what to check, such as a missing permission. Microsoft’s own error text is left out, because it can contain details of the request. For the actions that read, a user or group that doesn’t exist is not an error: the output says
found: false. For the actions that change data, it is an error, so a mistyped ID never looks like success.
ms365.get-user
Section titled “ms365.get-user”Reads one user by object ID or user principal name.
| Input | Required | What it is |
|---|---|---|
user |
yes | The user’s object ID or user principal name, like ada@contoso.com. |
select |
no | The properties to return. Default: id, displayName, userPrincipalName and accountEnabled. id is always returned. |
Outputs: found (true or false) and user (the properties, or null).
Microsoft lists User.EnableDisableAccount.All together with User.Read.All as the least privileged way to read accountEnabled. If accountEnabled is missing from the output, grant that pair.
- id: leaver name: Read the leaver's account uses: library/ms365.get-user@v1 connections: graph: m365 with: user: ${{ trigger.body.upn }}ms365.list-users
Section titled “ms365.list-users”Lists users, optionally with a filter.
| Input | Required | What it is |
|---|---|---|
filter |
no | An OData filter, like accountEnabled eq false. |
advanced |
no | Use Microsoft’s advanced query mode, needed for ne, not, endsWith and similar. Also returns the total count. |
select |
no | The properties to return. Default: id, displayName, userPrincipalName and accountEnabled. |
page_size |
no | Users per page, 1 to 999 (default 100). |
max_pages |
no | Pages to read at most, 1 to 100 (default 10). |
max_items |
no | Users to return at most, 1 to 10000 (default 1000). |
Outputs: users, count (the total in advanced mode, otherwise null), pages and truncated.
signInActivity can’t be selected here, because it needs a licence and another permission. Use ms365.list-sign-in-activity.
ms365.list-user-groups
Section titled “ms365.list-user-groups”Lists the groups a user belongs to.
| Input | Required | What it is |
|---|---|---|
user |
yes | The user’s object ID or user principal name. |
transitive |
no | Also include groups the user belongs to through nested groups (default false). |
groups_only |
no | Return groups only (default true). With false, directory roles and administrative units come back too. |
filter |
no | An OData filter on the groups, like startswith(displayName,'Sales'). |
select |
no | The properties to return. Default: id and displayName. |
page_size |
no | Groups per page with transitive, 1 to 999 (default 100). |
max_pages |
no | Pages to read at most, 1 to 100 (default 10). |
max_items |
no | Groups to return at most, 1 to 10000 (default 1000). |
Outputs: found, groups, count, pages and truncated. Each item has a type, such as group.
Direct memberships need Directory.Read.All. With transitive: true, User.Read.All is enough. Groups with hidden membership also need Member.Read.Hidden.
ms365.list-group-members
Section titled “ms365.list-group-members”Lists the members of a group.
| Input | Required | What it is |
|---|---|---|
group |
yes | The group’s object ID. |
transitive |
no | Also include members of nested groups (default false). |
users_only |
no | Return users only (default true). With false, groups, devices and other members come back too. |
filter |
no | An OData filter on the members, like accountEnabled eq true. |
select |
no | The properties to return. Default: id, displayName and userPrincipalName. |
page_size |
no | Members per page, 1 to 999 (default 100). |
max_pages |
no | Pages to read at most, 1 to 100 (default 10). |
max_items |
no | Members to return at most, 1 to 10000 (default 1000). |
Outputs: found, members, count, pages and truncated.
Members your app may not read come back with only their ID and type. To read user details, grant User.Read.All too. Microsoft’s pages also name GroupMember.ReadBasic.All. We list GroupMember.Read.All because it is the one we know can be granted to an app.
- id: admins name: List the members of the admin group uses: library/ms365.list-group-members@v1 connections: graph: m365 with: group: 00000000-0000-0000-0000-000000000000 transitive: truems365.list-sign-in-activity
Section titled “ms365.list-sign-in-activity”Reads when users last signed in, for one user or many.
| Input | Required | What it is |
|---|---|---|
user |
no | One user’s object ID or user principal name. Leave it out to list many users. |
filter |
no | An OData filter on sign-in activity, like signInActivity/lastSignInDateTime le 2026-07-01T00:00:00Z. Microsoft doesn’t allow combining it with filters on other user properties. |
advanced |
no | Use Microsoft’s advanced query mode, needed for ne and not. |
page_size |
no | Users per page, 1 to 500 (default 500). |
max_pages |
no | Pages to read at most, 1 to 40 (default 5). |
max_items |
no | Users to return at most, 1 to 20000 (default 2500). |
Outputs: found, users (each with id, displayName, userPrincipalName, accountEnabled and signInActivity), pages and truncated.
Microsoft limits these requests to about 10 a minute for your tenant, so run this on a schedule rather than for every event.
ms365.list-sign-ins
Section titled “ms365.list-sign-ins”Lists sign-in log records in a time window, optionally for one user.
| Input | Required | What it is |
|---|---|---|
from |
yes | Start of the window, a UTC date and time like 2026-10-01T00:00:00Z. |
to |
no | End of the window, later than from. |
user_id |
no | Only this user’s sign-ins, by object ID. |
user_principal_name |
no | Only this user’s sign-ins, by user principal name. Give user_id or this, not both. |
fields |
no | The fields of each sign-in to return. Default: id, createdDateTime, userId, userPrincipalName, appDisplayName and status. |
page_size |
no | Sign-ins per page, 1 to 1000 (default 1000). |
max_pages |
no | Pages to read at most, 1 to 40 (default 5). |
max_items |
no | Sign-ins to return at most, 1 to 20000 (default 1000). |
Outputs: sign_ins, pages and truncated.
Sign-in records can hold IP addresses, locations and device details. They are personal data, so they are returned only when you name them in fields. Microsoft keeps sign-ins for 7 days, or 30 days with Entra ID P1 or P2, so a window that starts earlier returns nothing older.
Actions that change data
Section titled “Actions that change data”These actions each change one thing in your tenant: the three Entra ID actions below, and ms365.send-mail and ms365.upload-file further down. They share these rules:
- One at a time. Each one changes one user or one membership, named by its ID. There is no wildcard and no list, so a mistake can’t reach more than one account.
- Its own permission. Each one needs its own write permission, listed with it. Grant only the ones your workflow uses. A workflow that only uses the reading actions never needs any of them.
- Safe to run again. For the three Entra ID actions, when the change is already done, the step succeeds and the output says so with
changed: false, for examplestatus: already_disabled. It doesn’t fail. Sending an email is different: each run sends a new email. An upload with the defaultconflict: failfails when the file is already there. - Dry runs and test runs change nothing. In a dry run, Liitora holds back the change and logs it as “would have sent”. The step succeeds with
dry_run: true(and, for the Entra ID actions,changed: falseandstatus: dry_run). A test run that uses a production connection holds it back the same way. Reading steps, such as the account check before disabling, still run for real. - A person approves it first. Like every workflow, a version that uses these actions runs for real only after an admin approves it. The approval page shows each step and the connection it changes. See Approve a workflow.
- Errors. If Microsoft refuses, the step fails and names the permission or admin role to check. A user or group that doesn’t exist fails the step too.
ms365.disable-user
Section titled “ms365.disable-user”Turns off sign-in for one account (accountEnabled set to false). It reads the account first. If the account is already disabled, it changes nothing.
| Input | Required | What it is |
|---|---|---|
user |
yes | The user’s object ID or user principal name, like ada@contoso.com. |
Outputs: changed (true when this step disabled the account), status (disabled, already_disabled or dry_run), user_id (the user’s object ID) and dry_run.
Needs User.EnableDisableAccount.All and User.Read.All. Disabling an account is a sensitive action: Microsoft may also require a Microsoft Entra admin role for the app, such as User Administrator. Only a Privileged Authentication Administrator or a Global Administrator can disable an administrator who has a privileged role, so a step that tries to disable an admin may fail with a missing permission even when the permissions above are granted.
Disabling stops new sign-ins. It doesn’t end sessions that are already open. Add ms365.revoke-sign-in-sessions after it to sign the user out everywhere.
ms365.revoke-sign-in-sessions
Section titled “ms365.revoke-sign-in-sessions”Signs one user out of every app and device, by making their refresh tokens and session cookies invalid. They have to sign in again.
| Input | Required | What it is |
|---|---|---|
user |
yes | The user’s object ID or user principal name, like ada@contoso.com. |
Outputs: changed (true when Microsoft revoked the sessions), status (revoked or dry_run) and dry_run.
Needs User.RevokeSessions.All. Running it again is safe: Microsoft revokes again and the step succeeds. It can take a few minutes before every app notices. It doesn’t apply to external (guest) users from other organisations.
ms365.remove-group-member
Section titled “ms365.remove-group-member”Removes one member from one security group or Microsoft 365 group. It only removes the membership. It never deletes the user or the group.
| Input | Required | What it is |
|---|---|---|
group |
yes | The group’s object ID. |
member |
yes | The member’s object ID, for example the user’s id from ms365.get-user. A user principal name is not accepted here. |
Outputs: changed (true when this step removed the member), status (removed, not_a_member or dry_run) and dry_run.
Needs GroupMember.ReadWrite.All. A group that can be assigned to Microsoft Entra roles also needs RoleManagement.ReadWrite.Directory. Groups with dynamic membership can’t be changed this way: change the group’s membership rule instead. Only security groups and Microsoft 365 groups can be changed through Microsoft Graph.
When Microsoft says the member isn’t there, the action reads the group to tell a missing member from a missing group. That read needs a permission that can read the group, such as GroupMember.Read.All. If the group exists, the output says not_a_member. If it doesn’t, the step fails.
For a whole offboarding workflow, see Offboard a leaver in Microsoft 365.
Email and files
Section titled “Email and files”These actions send email and move files between a run and SharePoint or OneDrive. A file of the run is one a step saved, such as a report made with ctx.artifacts.put or a download. See Work with files in a run.
- Naming the document library. The file actions take exactly one of
drive_id(a document library’s drive ID, likeb!abc...),site(a SharePoint site’s default library) oruser(that user’s OneDrive).siteis the site ID, a host name likecontoso.sharepoint.com, or a host name and site path likecontoso.sharepoint.com:/sites/Finance. Then name the folder or file by its item ID or by its path from the top of the library, likeReports/2026. - Paths stay in the library. Each part of a path must be a SharePoint name: no
.., no/inside a name, and none of" * : < > ? \ | # %. The step fails before calling Microsoft otherwise, so a path can never reach another library. - Only SharePoint addresses for file content. Microsoft hands file content over through short-lived addresses on
*.sharepoint.com. The actions use such an address only when it ishttpson asharepoint.comhost, and they never send your connection’s sign-in there. Any other address stops the step. - Only the data you need. The file actions don’t return who created or changed a file, because that is personal data most workflows don’t need.
Excel tables in workbooks and posting to Microsoft Teams are not in this library. Microsoft documents changing Excel workbooks only for a signed-in user, not for an app on its own. To post to a Teams channel, add a Workflows webhook to the channel in Teams and send to it with webhook.send.
ms365.send-mail
Section titled “ms365.send-mail”Sends one email from a mailbox, usually a shared mailbox such as invoices@contoso.com, optionally with files of the run attached. It changes data: see Actions that change data.
| Input | Required | What it is |
|---|---|---|
mailbox |
yes | The mailbox the email comes from: its address or user ID. |
to |
no | Recipient email addresses. |
cc |
no | Copy recipients. |
bcc |
no | Hidden copy recipients. |
reply_to |
no | Addresses replies go to, if not the mailbox. Up to 10. |
subject |
yes | The subject, at most 255 characters on one line. |
body |
no | The message text. |
body_type |
no | text (the default) or html. |
attachments |
no | Files of this run to attach: their names, or ${{ artifacts.<name> }}. Up to 20. |
save_to_sent_items |
no | Keep a copy in the mailbox’s Sent Items (default true). |
Give at least one recipient in to, cc or bcc, and at most 500 together. Outputs: accepted (true when Microsoft accepted the email), dry_run, recipients (how many) and attachments (how many).
Microsoft accepts the email for delivery and answers at once. It does not confirm that the email was delivered, so a step that succeeds doesn’t prove that the recipient got it.
Each attachment must be under 3,000,000 bytes, and all of them together at most 4,000,000 bytes. The step checks the sizes before it sends anything, and a larger file fails the step. To share a larger file, upload it with ms365.upload-file and send its link (webUrl).
Needs Mail.Send. Granted in Entra, this permission lets the app send as any mailbox in your organization. Grant it with RBAC for Applications in Exchange Online instead, limited to the mailbox your workflow uses. See Limit which mailboxes the app can send from.
- id: send name: Send the invoice uses: library/ms365.send-mail@v1 connections: graph: m365 with: mailbox: invoices@contoso.com to: ["${{ trigger.body.email }}"] subject: Your invoice body: The invoice is attached. attachments: ["${{ artifacts['invoice.pdf'] }}"]ms365.list-files
Section titled “ms365.list-files”Lists the files and folders in one folder of a SharePoint document library or a OneDrive. It only reads.
| Input | Required | What it is |
|---|---|---|
drive_id |
one of three | The document library’s drive ID. |
site |
one of three | A SharePoint site, for its default document library. |
user |
one of three | A user ID or user principal name, for that user’s OneDrive. |
folder_id |
no | The folder’s item ID. |
folder_path |
no | The folder’s path, like Reports/2026. Leave out both for the top folder. |
page_size |
no | Items per page, 1 to 999 (default 200). |
max_pages |
no | Pages to read at most, 1 to 100 (default 10). |
max_items |
no | Items to return at most, 1 to 10000 (default 1000). |
Outputs: found (false when there is no such folder), items, pages and truncated. Each item has id, name, size, webUrl, lastModifiedDateTime and kind (file or folder), plus mimeType for a file and childCount for a folder.
Needs Files.Read.All (Sites.Read.All also works).
ms365.download-file
Section titled “ms365.download-file”Downloads one file of up to 8,000,000 bytes from a SharePoint document library or a OneDrive and saves it as a file of the run. It only reads.
| Input | Required | What it is |
|---|---|---|
drive_id |
one of three | The document library’s drive ID. |
site |
one of three | A SharePoint site, for its default document library. |
user |
one of three | A user ID or user principal name, for that user’s OneDrive. |
item_id |
one of two | The file’s item ID. |
path |
one of two | The file’s path, like Reports/2026/March.xlsx. |
save_as |
no | The run file’s name. Default: the file’s name, with characters other than letters, digits, dots, dashes and underscores replaced by _. |
Outputs: file (the run file, as ${{ artifacts.<name> }} gives it), item_id, name (the name in SharePoint or OneDrive) and size (bytes).
A larger file fails the step before anything is downloaded. Needs Files.Read.All (Sites.Read.All also works).
ms365.upload-file
Section titled “ms365.upload-file”Uploads one file of the run, up to 100 MiB, into a folder of a SharePoint document library or a OneDrive. It changes data: see Actions that change data.
| Input | Required | What it is |
|---|---|---|
drive_id |
one of three | The document library’s drive ID. |
site |
one of three | A SharePoint site, for its default document library. |
user |
one of three | A user ID or user principal name, for that user’s OneDrive. |
folder_id |
no | The folder’s item ID. |
folder_path |
no | The folder’s path, like Reports/2026. Leave out both for the top folder. |
file |
yes | The run file to upload: its name, or ${{ artifacts.<name> }}. |
file_name |
no | The name in SharePoint or OneDrive. Default: the run file’s name. |
conflict |
no | When a file with that name is already there: fail (the default) fails the step, replace replaces the file, and rename saves the new one under a new name. |
Outputs: id, name (the name it was saved under, which differs after a rename), size, webUrl, upload (simple or session), parts (requests that carried the content) and dry_run. In a dry run, only dry_run, upload and parts are set.
Files up to 4 MiB go in one request. Larger files go in an upload session, in parts of 5 MiB, one after another. If a part fails, the step cancels the session, so no half-uploaded file is left behind, and fails. Run it again to start over.
Needs Files.ReadWrite.All. Files over 4 MiB also need Sites.ReadWrite.All, because it is the only permission Microsoft lists for upload sessions. To limit the app to the sites it needs, grant Sites.Selected instead and give the app access to those sites, see Limit SharePoint to chosen sites. Liitora has not tested that setup.
- id: file name: File the report uses: library/ms365.upload-file@v1 connections: graph: m365 with: site: contoso.sharepoint.com:/sites/Finance folder_path: Reports/2026 file: "${{ artifacts['report.csv'] }}"Your data
Section titled “Your data”What these actions return is stored with the run, like every step’s output, and deleted with the run log after your plan’s retention. A downloaded file is a file of the run and is deleted the same way. The run log keeps the email’s subject and recipients as the step’s input, but not the attachments’ content. See Read run logs and Data privacy and GDPR.
Sources
Section titled “Sources”Written from Microsoft’s own documentation:
- Get user and List users
- user resource type and signInActivity resource type
- How to manage inactive user accounts
- Advanced query capabilities on Microsoft Entra ID objects
- List a user’s direct memberships and transitive memberships
- List group members and transitive members
- List signIns and signIn resource type
- Microsoft Entra data retention
- Microsoft Graph throttling guidance and service-specific throttling limits
- Microsoft Graph error responses, Paging Microsoft Graph data and Query parameters
- Update user and Revoke sign-in sessions
- Remove a member from a group and Get group
- user: sendMail, fileAttachment resource type and Role Based Access Control for Applications in Exchange Online
- List children of a driveItem, Get a driveItem and Download driveItem content
- Upload a small file and Upload large files with an upload session
- Get a SharePoint site, Get a drive and Addressing driveItems
- Things to know about sending mail, SharePoint Online limits and Selected permissions
- Microsoft Graph permissions reference