Microsoft 365 Connectors
Seven Microsoft 365 connectors run locally through Microsoft Graph: Office 365 Outlook, Office 365 Users, Office 365 Groups, Microsoft Teams, Word Online (Business), Excel Online (Business) and OneDrive for Business. They all take a delegated Microsoft Graph token and act as the signed-in user.
These connectors let a flow run end to end on your machine, but they are thinner emulations than SharePoint and Dataverse: their outputs are mostly Microsoft Graph's own objects, which do not always match what the cloud connector returns. When a later step reads specific fields, check the local output in the trace, or mock the action with the shape you expect.
Tokens
| Flag | Covers |
|---|---|
--graph-token <token> | All seven connectors. |
--word-token <token> | Word Online only (overrides --graph-token). |
--excel-token <token> | Excel Online only (overrides --graph-token). |
--onedrive-token <token> | OneDrive for Business only (overrides --graph-token). |
--auth | One Graph token for all seven, with the permissions the flow's operations need. |
# Automatic: signs in once, then renews silently
flowforger run flow.ff.ts --auth
# Explicit token
az account get-access-token --resource https://graph.microsoft.com --query accessToken -o tsv
flowforger run flow.ff.ts --graph-token <token>
All tokens are for https://graph.microsoft.com. An explicit flag wins over --auth for the connectors it covers, and those connectors are left out of the --auth sign-in.
With --auth, the CLI looks at every Microsoft 365 action in the flow and requests only the permissions those operations need (table below). Run flowforger scopes flow.ff.ts to see the list for one flow. An operation the scope map does not know falls back to User.Read (User.Read.All for Office 365 Users). HttpRequest actions are not inspected: add what they call to auth.additionalScopes.graph in flowforger.config.json.
{
"auth": {
"clientId": "<client-id>",
"tenantId": "<tenant-id>",
"additionalScopes": {
"graph": ["Sites.ReadWrite.All"]
}
}
}
Word, Excel and OneDrive request Files.ReadWrite, which covers the signed-in user's own files. For templates and workbooks in a SharePoint library, add a broader Graph permission such as Sites.ReadWrite.All this way, and to the app registration.
Permissions
All permissions are delegated Microsoft Graph permissions (App ID 00000003-0000-0000-c000-000000000000). Add them to your app registration (see App registration) and grant admin consent. flowforger scopes --all prints the full set:
| Connector | Permissions |
|---|---|
| Office 365 Outlook | Mail.Read, Mail.ReadWrite, Mail.Send, Calendars.Read, Calendars.ReadWrite, Contacts.Read, Contacts.ReadWrite, User.Read |
| Office 365 Users | User.Read, User.Read.All, User.ReadWrite, People.Read, Sites.Read.All |
| Office 365 Groups | Group.Read.All, Group.ReadWrite.All, GroupMember.Read.All, GroupMember.ReadWrite.All, User.Read |
| Microsoft Teams | Team.ReadBasic.All, Team.Create, TeamMember.ReadWrite.All, Channel.ReadBasic.All, Channel.Create, ChannelMessage.Read.All, ChannelMessage.Send, Chat.Create, Chat.Read, Chat.ReadWrite, ChatMember.Read, TeamsActivity.Send, TeamworkTag.ReadWrite, OnlineMeetings.ReadWrite, Calendars.ReadWrite, User.Read, User.Read.All |
| Word Online (Business) | Files.ReadWrite |
| Excel Online (Business) | Files.ReadWrite |
| OneDrive for Business | Files.ReadWrite |
Entra ID consents per permission, so add both the Read and ReadWrite variants where both are listed: a read-only flow requests the narrower one. The per-operation mapping is in each connector's section below.
Operation names
Call ctx.connectors.<namespace>.<Operation>(name, params, connectionReferenceName?). The tables list the operation names the local connector accepts. A local alias (the "Local aliases" column) is written to the deployed flow as its designer operation id, with its parameters renamed to the designer's keys where they differ; a designer id is kept as written. Local names without a designer counterpart (for example Excel GetRange or Outlook GetMailFolders) are emitted unchanged and fail in the cloud, so check such an action against a flow built in the Power Automate designer, or one you pulled with flowforger pull. Outlook, Users, Groups and Teams also accept lower-camel-case aliases (sendEmail, getEmails, ...), which are not listed.
An operation the local connector does not recognise fails with "unknown operation". Several operation ids the designer writes today are in that group; they are listed per connector under Not supported locally. Mock those actions (--mock <Action>=<outputs.json>).
Connector triggers (new email, file changed, ...) are written into the compiled flow but never fire locally; pass a sample trigger body with --in. See Connector triggers.
Office 365 Outlook
Namespace: ctx.connectors.office365. Connection reference: shared_office365.
| Area | Operations | Permission |
|---|---|---|
| Send | SendEmailV2 (SendEmail), ReplyToEmailV2, ForwardEmailV2 | Mail.Send |
| Read mail | GetEmailsV2 (GetEmails), GetEmailV2 (GetEmail), GetAttachmentV2, ExportEmailV2, GetMailFolders | Mail.Read |
| Change mail | DeleteEmailV2, MoveEmailV2, MarkAsReadV3, Flag | Mail.ReadWrite |
| Read calendar | GetEventsV4, GetEventV4, GetCalendars, CalendarGetTables, FindMeetingTimesV2 | Calendars.Read |
| Change calendar | CreateEventV4, UpdateEventV4, DeleteEventV4, RespondToEventV2 | Calendars.ReadWrite |
| Read contacts | GetContactsV2, GetContactV2, GetContactFolders | Contacts.Read |
| Change contacts | CreateContactV2, UpdateContactV2, DeleteContactV2 | Contacts.ReadWrite |
| Other | GetMyProfile, HttpRequest (a Graph request: method, uri, body, headers) | User.Read |
await ctx.connectors.office365.SendEmailV2('NotifyOwner', {
'emailMessage/To': 'owner@contoso.com',
'emailMessage/Subject': 'Weekly report',
'emailMessage/Body': '<p>The report is ready.</p>',
});
Local behaviour:
SendEmailV2accepts the designer'semailMessage/To,/Cc,/Bcc,/Subject,/Body,/Importance,/ReplyTo,/From(shared mailbox),/IsHtmland/Attachmentskeys. Recipients are a;- or,-separated string or an array. The mail is saved to Sent Items.- The other operations read plain parameter names:
messageId,folderPath(inbox,sentitems, ... or a folder id),top,filter,searchQuery,fetchOnlyUnread,mailboxAddress,eventId,calendarId,contactId, and so on. - Outputs are Microsoft Graph's objects. List operations (
GetEmailsV2,GetEventsV4,GetContactsV2,GetMailFolders, ...) return a plain array, not{ value: [...] }. Send, reply, forward, delete and respond return{ success: true }. - Calendar operations work on the signed-in user's calendars only.
Designer operation ids such as GetEmailsV3, ReplyToV3, ForwardEmail_V2, DeleteEmail_V2, MoveV2, MarkAsRead_V3, GetAttachment_V2, V4CalendarPostItem, V4CalendarPatchItem and SharedMailboxSendEmailV2 run locally with the designer's flattened keys (replyParameters/Body, body/ToRecipients, item/subject, emailMessage/MailboxAddress, ...), so a pulled flow debugs as is. The typed local names (ReplyToEmailV2, MoveEmailV2, CreateEventV4, ...) are deployed as those ids with their parameters renamed to match. Triggers such as OnNewEmailV3 are emitted only.
Office 365 Users
Namespace: ctx.connectors.office365users. Connection reference: shared_office365users.
| Operations | Permission |
|---|---|
MyProfile_V2, HttpRequest | User.Read |
UserProfile_V2, Manager_V2, DirectReports_V2, SearchUserV2, UserPhoto_V2, UserPhotoMetadata | User.Read.All |
RelevantPeople | People.Read |
MyTrendingDocuments, TrendingDocuments | Sites.Read.All |
UpdateMyProfile, UpdateMyPhoto | User.ReadWrite |
await ctx.connectors.office365users.Manager_V2('GetManager', { id: 'anna@contoso.com' });
await ctx.compose('ManagerName', ctx.body('GetManager')?.['displayName']);
Local behaviour:
idis a user principal name or object id.$selectnarrows the fields.- The
_V2profile operations return Graph's camelCase user (displayName,mail,jobTitle, ...). The olderMyProfile,UserProfile,ManagerandDirectReportsreturn the PascalCaseUsershape (DisplayName,Mail, ...) with the camelCase fields kept alongside. Manager_V2for a user without a manager fails with404"No manager found for the specified user."SearchUserV2matchessearchTermas a prefix of the display name, given name, surname, mail, mail nickname or user principal name, and returns{ value: [...] }in the PascalCase shape. With an emptysearchTerm(andisSearchTermRequirednotfalse) it returns no users.UserPhoto_V2returns{ content: <base64>, contentType }, not binary content{ "$content-type", "$content" }.UserPhotoMetadatareturns{ HasPhoto, Width, Height, ContentType, ImageFileExtension }, withHasPhoto: falsewhen the user has no photo.HttpRequesttakes the designer'sUri,Method,Body,ContentTypeandCustomHeader1toCustomHeader5(Name: value).
Office 365 Groups
Namespace: ctx.connectors.office365groups. Connection reference: shared_office365groups.
| Operations | Permission |
|---|---|
ListGroups, GetGroup, ListGroupEvents, GetGroupEvent | Group.Read.All |
CreateGroup, UpdateGroup, DeleteGroup, AddOwnerToGroup, RemoveOwnerFromGroup, CreateGroupEvent, UpdateGroupEvent, DeleteGroupEvent | Group.ReadWrite.All |
ListGroupMembers, ListGroupOwners | GroupMember.Read.All |
AddMemberToGroup, RemoveMemberFromGroup | GroupMember.ReadWrite.All |
IsMemberOfGroup, HttpRequest | User.Read |
Local behaviour:
- Groups and members are addressed by
groupIdanduserId. - List operations return Graph's
{ value: [...] }, soctx.body('X')?.['value']works as in the cloud. CreateGroupcreates a Microsoft 365 (unified) group unlessgroupTypessays otherwise;mailNicknamedefaults to the display name without spaces.IsMemberOfGroupchecks the signed-in user's membership.ListGroupEventswithstartDateTimeandendDateTimereads the calendar view (recurring events expanded).HttpRequesttakesUri(a full Graph URL or a path),Method,Body,ContentTypeandCustomHeader1toCustomHeader5.
Not supported locally: HttpRequestV2.
Microsoft Teams
Namespace: ctx.connectors.teams. Connection reference: shared_teams.
| Area | Operations | Permission |
|---|---|---|
| Teams | GetAllTeams, GetAllAssociatedTeams, GetTeam | Team.ReadBasic.All |
CreateATeam | Team.Create | |
AddMemberToTeam | TeamMember.ReadWrite.All | |
| Channels | GetChannelsForGroup, GetAllChannelsForTeam, GetChannel | Channel.ReadBasic.All |
CreateChannel | Channel.Create | |
AddMemberToChannel, RemoveMemberFromChannel | TeamMember.ReadWrite.All | |
| Chats | CreateChat | Chat.Create |
GetChats, GetMessagesFromChat | Chat.Read | |
ListMembers | ChatMember.Read | |
| Messages | PostMessageToConversation, PostCardToConversation, UpdateCardInConversation | ChannelMessage.Send, Chat.Create, Chat.ReadWrite, User.Read |
ReplyWithMessageToConversation, ReplyWithCardToConversation | ChannelMessage.Send | |
GetMessagesFromChannel, ListRepliesToMessage | ChannelMessage.Read.All | |
GetMessageDetails | ChannelMessage.Read.All, Chat.Read | |
PostFeedNotification | TeamsActivity.Send | |
| Tags | CreateTag, GetTags, DeleteTag, AddMemberToTag, DeleteTagMember, GetTagMembers | TeamworkTag.ReadWrite |
| Mentions | AtMentionUser | User.Read.All |
AtMentionTag | TeamworkTag.ReadWrite, User.Read.All | |
| Meetings | CreateTeamsMeeting | Calendars.ReadWrite, OnlineMeetings.ReadWrite |
| Graph | HttpRequest | User.Read |
await ctx.connectors.teams.PostMessageToConversation('PostToChannel', {
poster: 'User',
location: 'Channel',
body: {
recipient: { groupId: '0b6f2a3c-1111-4222-8333-944455556666', channelId: '19:abc123@thread.tacv2' },
messageBody: '<p>The weekly report is ready.</p>',
},
});
Local behaviour:
- The post operations take the designer's parameters:
location(Channel,Group chatorChat with Flow bot),body/recipient/groupId+body/recipient/channelIdfor a channel,body/recipient(a chat id) for a group chat,body/recipient(an email, UPN or object id; several separated by;) for the Flow bot, andbody/messageBody. A nestedbody: { ... }object is flattened to these keys. - Messages are posted as the signed-in user, whatever
postersays: a delegated Graph token cannot post as the Flow bot. - Chat with Flow bot is emulated: locally the message goes to the one-on-one chat between you and the recipient, created if needed. Deployed, it goes to the recipient's Workflows (Flow bot) chat. A recipient who is you fails locally (Graph cannot create a chat with yourself); use another recipient or a channel while testing.
- Cards are posted as an Adaptive Card attachment.
Not supported locally: PostCardAndWaitForResponse ("Post adaptive card and wait for a response") and SubscribeUserMessageWithOptions. These wait for a person to answer, so they only work deployed. Call them with ctx.connectorWebhook and mock them locally; with a Graph token loaded they fail locally.
Word Online (Business)
Namespace: ctx.connectors.wordonlinebusiness (or its alias ctx.connectors.wordonline; see the note below). Connection reference: shared_wordonlinebusiness. Permission: Files.ReadWrite.
| Operation | Designer name | Local aliases |
|---|---|---|
CreateFileItem | Populate a Microsoft Word template | PopulateWordTemplate |
GetFilePDF | Convert Word Document to PDF | ConvertToPdf, ConvertWordDocumentToPdf |
await ctx.connectors.wordonlinebusiness.CreateFileItem('FillContract', {
source: 'me',
drive: 'b!AbCdEf',
file: '/Templates/Contract.docx',
dynamicFileSchema: { '1325480521': 'Fabrikam', '1873251098': '2026-11-07' },
});
Local behaviour:
sourceisme(your OneDrive),users/<upn>,groups/<id>,sites/<id>, or a SharePoint site URL (resolved to the site).driveis the document library's drive id,filean item id or a path. A path that starts with the library name (/Shared Documents/...) is made library-relative.- Populate a template downloads the template and fills its content controls locally: each
dynamicFileSchema/<id>value replaces the text of the content control with that numeric id, keeping the first run's formatting. Values are written as plain text: picture, repeating-section and other non-text controls are not emulated. The result is binary content (.docx), ready for a create-file action. - Convert to PDF uses Graph's PDF conversion and returns binary content (
application/pdf). PopulateWordTemplateandPopulateAWordTemplateare local aliases ofCreateFileItem, andConvertToPdf/ConvertWordDocumentToPdfofGetFilePDF; all of them run locally and are deployed under the cloud id.
Excel Online (Business)
Namespace: ctx.connectors.excelonlinebusiness (or its alias ctx.connectors.excelonline; see the note below). Connection reference: shared_excelonlinebusiness. Permission: Files.ReadWrite.
| Operation | Designer name | Local aliases |
|---|---|---|
GetItems | List rows present in a table | ListRows |
GetItem | Get a row | GetRow |
AddRowV2 | Add a row into a table | AddRow |
PatchItem | Update a row | UpdateRow |
DeleteItem | Delete a row | DeleteRow |
GetTables | Get tables | |
GetAllWorksheets | Get worksheets | GetWorksheets |
CreateTable | Create table | |
CreateWorksheet | Create worksheet | |
CreateIdColumn | Add a key column to a table | AddKeyColumn |
FlowForger also accepts GetRange, UpdateRange, GetColumn, AddColumn, DeleteColumn, DeleteTable and DeleteWorksheet locally; they are Graph workbook calls without a designer counterpart.
await ctx.connectors.excelonlinebusiness.AddRowV2('AddOrder', {
source: 'me',
drive: 'b!AbCdEf',
file: '01ABCDEF2GHIJKLMNOPQRSTUVWXYZ',
table: 'Orders',
item: { OrderId: 'SO-1001', Status: 'Open' },
});
Local behaviour:
source,driveandfilework as for Word.tableis the table name;idColumnandidselect the row for get, update and delete;itemholds the row's values by column name.- Rows differ from the cloud's. Locally a row is
{ _rowIndex, <column>: <value>, ... }with the values as Graph returns them (numbers stay numbers). The cloud connector's rows carry@odata.etagandItemInternalIdand format their values differently. MockGetItemswith a real cloud output when later steps depend on the exact shape. $filterand$orderbyrun on your machine, after the rows are read:$filterunderstands one condition (Column eq 'x',ne,contains(...),startswith(...),endswith(...)); an expression it does not recognise returns all rows.$topand$skipare applied before filtering.- Get, update and delete find the row by reading the whole table and comparing the key column as text.
AddRowV2andPatchItemanswer with the row as written; delete answers{ ok: true, status: 204 }.CreateIdColumnadds the column but does not fill it with key values.RunScriptProdandRunScriptProdV2(Run script) are accepted but are not expected to work locally: Office Scripts cannot be run through Microsoft Graph. Mock them.
OneDrive for Business
Namespace: ctx.connectors.onedriveforbusiness. Connection reference: shared_onedriveforbusiness. Permission: Files.ReadWrite.
| Area | Operations |
|---|---|
| Files | CreateFile, UpdateFile, DeleteFile, GetFileContent, GetFileContentByPath, GetFileMetadata, GetFileMetadataByPath |
| Copy and move | CopyDriveFile, CopyDriveFileByPath, MoveFile, MoveFileByPath, CopyFile (upload from URL) |
| Convert | ConvertFile, ConvertFileByPath |
| Folders and search | ListFolderV2 (ListFolder), ListRootFolder, FindFiles, FindFilesByPath |
| Sharing | CreateShareLinkV2 (CreateShareLink), CreateShareLinkByPathV2 (CreateShareLinkByPath) |
| Other | GetFileThumbnail |
Local behaviour:
- Everything runs against the signed-in user's own drive (
/me/drive), whichever connection the deployed flow uses. - Metadata answers use the connector's
{ Id, Name, DisplayName, Path, LastModified, Size, MediaType, IsFolder, ETag, FileLocator, ... }fields with the original Graph properties kept alongside. Folder listings return a plain array. GetFileContent,GetFileContentByPath,ConvertFileandConvertFileByPathreturn binary content{ "$content-type", "$content" }for anything buttext/*(which is returned as text) and JSON (parsed), as in the cloud.CreateFileandUpdateFileaccept text, a base64 string, or binary content from another action (for example a populated Word template).CopyFiledownloads the source URL without authentication and uploads it.CreateShareLinkV2defaults to aviewlink withanonymousscope.
Not supported locally: ExtractFolderV2 (extract an archive). Mock it.
Namespace names and connection references
The typed namespaces ctx.connectors.wordonline, ctx.connectors.excelonline and ctx.connectors.onedrive are aliases of ctx.connectors.wordonlinebusiness, ctx.connectors.excelonlinebusiness and ctx.connectors.onedriveforbusiness (the names flowforger pull generates): both spellings run locally and both are deployed as shared_wordonlinebusiness, shared_excelonlinebusiness and shared_onedriveforbusiness. A connections entry in flowforger.config.json may be keyed by either name. Declare the connection references in the constructor:
constructor(ctx: FlowContext) {
ctx.flow.connectionReferences = {
shared_wordonlinebusiness: {
apiId: '/providers/Microsoft.PowerApps/apis/shared_wordonlinebusiness',
connectionReferenceLogicalName: 'contoso_wordonline',
},
};
}
Failures
A Graph error fails the action with outputs { statusCode, body }, where the body is Graph's own error object ({ error: { code, message } }), so a catch scope can read ctx.outputs('X')?.['statusCode']. A missing token fails the action with "Connector '...' not provided" and no outputs. See Outputs and failures.
These connectors do not follow @odata.nextLink for a pagination policy: locally a list operation returns its first page.
Packages
@flowforger/connectors-office365, @flowforger/connectors-office365users, @flowforger/connectors-office365groups, @flowforger/connectors-teams, @flowforger/connectors-wordonline, @flowforger/connectors-excelonline, @flowforger/connectors-onedrive, sharing @flowforger/connectors-shared.