Microsoft 365 mailbox
A Microsoft 365 mailbox automation starts a CoderFlow task for each new email in a watched folder. Microsoft Graph pushes notifications through a webhook or Azure Event Hubs; CoderFlow does not poll the mailbox for new mail.
Replies in the same conversation follow up on its task until that task is approved or rejected, or more than 30 days have passed since the conversation's last email. A later reply starts a new task. By default, at most 10 follow-ups reach one task in a rolling 24 hours; further replies are recorded without starting another turn. Administrators can change that limit.
This is a separate, opt-in environment automation. The Outlook add-in lets a person choose an open email and send it to CoderFlow; it does not monitor mailboxes and does not require this connection.
Mailbox access is read-only: Mail.Read. CoderFlow never replies, marks mail read, moves it, tags it or deletes it. Outlook therefore shows no indication that CoderFlow handled a message. Use the automation's History to find its outcome and task. A person replies from Outlook when needed.
Tenant setup
You need Microsoft Entra and Exchange Online administration access, plus Azure resource administration for Event Hubs delivery.
-
Register a single-tenant Entra application and create a client secret. Use one app registration per CoderFlow server, including a separate one for a test server.
-
In Exchange Online PowerShell, create the service principal. Use the Object ID from Enterprise applications, not the app registration's Object ID:
New-ServicePrincipal -AppId <application-id> -ObjectId <enterprise-application-object-id> -DisplayName "CoderFlow mail" -
Define an Exchange management scope containing only the intake mailboxes. Assign the read role to that scope:
New-ManagementRoleAssignment -App <enterprise-application-object-id> -Role "Application Mail.Read" -CustomResourceScope "CoderFlow intake"An administrative unit can be used instead with
-RecipientAdministrativeUnitScope. -
Do not grant application
Mail.Readin Entra. Remove any existing Entra grant: it adds to Exchange RBAC permissions and would bypass the mailbox restriction. -
Check an allowed mailbox and an out-of-scope mailbox:
Test-ServicePrincipalAuthorization -Identity <enterprise-application-object-id> -Resource support@contoso.com
Allow 30 minutes to 2 hours for permission changes to apply. The test command bypasses the cache, so a successful test can precede working mailbox access. See Microsoft's RBAC for Applications setup for defining and testing scopes.
Choose a delivery mode
Both modes use the same mailbox permissions, filters, limits and task behavior. This integration currently supports the Microsoft public cloud only.
Webhook
Choose Webhook when Microsoft can reach a public HTTPS address for your CoderFlow server. The reverse proxy must allow /api/webhooks/inbound/*, including Microsoft's validation requests, without requiring a CoderFlow sign-in.
You can expose the webhook-only listener and keep the main application port private. It isolates public routes and ports within the same server process. Configure HTTPS termination at the proxy and use its address as Public base URL.
Optionally restrict inbound connections to Microsoft's notification IP ranges; keep that allowlist current using the link in Microsoft's webhook firewall guidance.
CoderFlow creates the webhook for the connection. Do not create or link a generic webhook yourself. In Automations → Webhooks, it appears as Managed by connection followed by the connection's name. See managed mail webhooks for history and token rotation.
Azure Event Hubs
Choose Azure Event Hubs when the server can connect outbound to Azure but cannot accept public inbound requests.
- Create an Event Hubs namespace and an event hub in your Azure subscription.
- Assign Azure Event Hubs Data Sender on the hub to Microsoft Graph Change Tracking. If that service principal is missing, create it in your tenant first. Its application ID is
0bf30f3b-4a52-48df-9a82-234910c4a086; Microsoft's Event Hubs delivery guide includes the creation steps. - Assign Azure Event Hubs Data Receiver on the hub to your CoderFlow app. CoderFlow uses Entra authentication, not SAS keys or connection strings.
- Create a consumer group dedicated to this CoderFlow server.
$Defaultis refused. Do not share the hub and consumer group with another server or reader: it could take notifications intended for this server. - Allow outbound port 5671 for AMQP, or 443 for AMQP over WebSockets. CoderFlow falls back automatically. After three silent 5671 attempts across the server process, it uses 443 until the server restarts.
- Set the hub's retention to cover the longest outage you expect. After an outage, CoderFlow resumes from saved checkpoints; longer gaps need catch-up.
Add the connection in CoderFlow
Open Administration → Environments, select the environment, then open Connections. Select Add Connection and the Microsoft 365 type card.
In General, enter Name and an optional Description. Available For is fixed to Automation: the credentials remain on the server and are never injected into task or deployment containers.
| Field | What to enter |
|---|---|
| Tenant ID | The tenant's GUID or verified domain, such as contoso.onmicrosoft.com. |
| Client ID (application ID) | The single-tenant app's application GUID. |
| Client secret | The secret value, not its ID. |
| Delivery | Webhook or Azure Event Hubs. |
| Public base URL | For Webhook: a public HTTPS base address, with no credentials, query or fragment. Localhost and private IP addresses are refused. |
| Namespace | For Event Hubs: the namespace name, with or without .servicebus.windows.net. The name is 6–50 characters, starts with a letter, ends with a letter or number, and otherwise uses letters, numbers or hyphens. |
| Event hub | The hub name, 1–256 characters. |
| Consumer group | The dedicated group name, 1–50 characters. $Default is not allowed. |
Hub and consumer-group names use letters, numbers, periods, hyphens or underscores and start and end with a letter or number.
The secret is stored encrypted and is never shown again. On edit, leave Client secret blank to keep it. If Tenant ID or Client ID (application ID) changes, enter the secret again; a saved secret cannot be reused for a different identity. After a failed save, a typed secret is cleared and the dialog explains whether you must re-enter it or can retain the saved secret.
Test connection
Select Test connection. Connection checks reports:
| Check | Successful result |
|---|---|
| Microsoft Graph token | Microsoft Graph token acquired. |
| Azure Event Hubs token (Event Hubs only) | Azure Event Hubs token acquired. |
| Azure Event Hubs properties (Event Hubs only) | The partition count and whether properties were read using AMQP/TLS on port 5671 or AMQP over WebSockets on port 443. |
The overall success message is Microsoft 365 connection successful. The test does not read mail, create a subscription, or prove webhook delivery. It also does not verify Graph's Data Sender assignment. Mailbox access and delivery are checked when an automation is enabled.
The notice reads: “A mailbox scope (RBAC for Applications) assigned less than 2 hours ago may not have applied yet. Mailbox access itself is checked when an automation is enabled.” Save the connection after checking it.
Create the email automation
On the environment's Automations tab, select Add Automation. Under Trigger, choose When an email arrives (Microsoft 365).
| Field | What to choose |
|---|---|
| Name, Description | A name for the automation and an optional explanation. |
| Connection | A Microsoft 365 connection in this environment. If none exists, Add one in Connections opens the connection setup. |
| Mailbox | The SMTP address of a user or shared mailbox covered by the app's Exchange scope. |
| Folder | Inbox (the default), or a slash-separated path such as Inbox/Support for mail routed by an Outlook rule. Paths can be up to 512 characters; empty, . and .. segments are refused. |
| Allowed senders | Optional addresses or @domain entries, up to 100. Enter one per line, or separate them with commas or semicolons. Empty means anyone. Matching ignores case; @contoso.com matches that domain, not its subdomains. |
| Run Target | Custom instructions or Task template. Deployment, publishing and import targets are unavailable for email. |
| Agent | The agent and any available model or effort settings for the task. |
| Run Configuration | Complete Task Name Template, then Instructions, or Task Template, its parameters and optional Additional Instructions. Email task names are generated from the subject even though the editor requires Task Name Template. |
Only one automation can watch the same mailbox and folder on a connection. This trigger is available for environment automations only. It has no schedule, SCM poll or Run Now action; test it by sending an allowed email after delivery is verified.
The default Instructions are:
Work on what the email asks within this environment, or summarize and stop.
Replace them with your intake rules, or select a template. An environment skill can hold shared conventions for handling incoming mail.
Untrusted input and Launch tasks pinned are shown checked and locked for this trigger; neither can be turned off. Leave Enabled off while preparing the automation; turn it on and select Save when ready. Enabling establishes a baseline of existing mail, creates the subscription and verifies delivery. Existing mail in the initial baseline does not start tasks.
Limits
Limits apply to each automation. The daily limits use rolling 24-hour windows, not a midnight reset.
| Field | Default | Range | When reached |
|---|---|---|---|
| Running tasks | 2 | 1–10 | Email needing a new task waits until capacity is available. |
| Tasks per day | 20 | 1–100 | Email needing a new task waits until an earlier launch leaves the 24-hour window. |
| Follow-ups per task per day | 10 | 1–50 | Further replies are Recorded only; they do not start a turn or wait to be sent later. |
How emails become tasks
Skipped mail
CoderFlow avoids duplicate work by tracking message IDs, including the internet message ID. Duplicate notifications do not start another task. It also skips recognized auto-replies and bounces, mail sent by the watched mailbox itself, CoderFlow's own notification email, and senders outside Allowed senders. A message deleted before CoderFlow can read it is skipped.
Sender matching uses the email's From address. An allowlist narrows intake; it does not replace your tenant's mail authentication and anti-spoofing controls.
Waiting mail and replies
Mail over the new-task caps stays queued; it is not dropped. A reply also waits while its conversation's task is being created. If the existing task is busy, the follow-up is queued until its current turn ends.
A reply starts a new task after approval, rejection, deletion, or a gap of more than 30 days since the last email. It can also start a replacement if the earlier task is no longer available for follow-up. When available, the earlier task's name and link are included as context. Follow-ups keep the same untrusted-input restrictions.
Task files
The task receives the automation's instructions, the email's sender, recipients, date, subject and readable body, plus these files:
| File | Contents |
|---|---|
email.md | The complete readable email and attachment inclusion notes. |
email-original.html | The original HTML body, or email-original.txt for a plain-text body. |
email.eml | The original message, when it fits the limits. |
| Individual attachments | Supported file attachments, including inline images, with safe filenames. |
The size limits match the Outlook add-in: 20 attachments, 50 MiB per file, a 10 MiB body, and 150 MiB total for the body, original message and included attachments. Oversized attachments or an oversized .eml are omitted with a reason; an oversized body prevents processing. Cloud file links are not downloaded. Attached Outlook items, such as attached emails or calendar items, are not extracted as separate files by mailbox intake.
The prompt includes up to 40,000 characters of the readable body; the rest remains in email.md. Hidden HTML elements are omitted from the readable copy, white text that may be hidden is flagged, and remote image links are removed. The original body remains untrusted.
How the task runs
Tasks and follow-ups run as the automation's creator, with that person's current permissions. New tasks are private, pinned and marked as untrusted input. The creator must retain permission to use automations and create tasks in the environment; follow-ups also require permission to execute tasks. Review the task in CoderFlow, then reply to the sender from Outlook if appropriate.
Moving or deleting mail in Outlook does not remove tasks already started. CoderFlow does not write processing markers back to the mailbox.
Read the status view
Select History on the automation and find Microsoft 365 mail. Select Refresh to update the status.
| Subscription state | Meaning |
|---|---|
| Off | The automation is disabled. Check for remaining subscription details while cleanup finishes. |
| No subscription | An enabled automation has no subscription status yet. |
| Creating subscription | CoderFlow is preparing or creating the subscription. |
| Verifying delivery | CoderFlow is waiting for Microsoft's validation to arrive. |
| Active | Delivery has been verified. |
| Needs attention | Read the accompanying error and check credentials, scope or delivery settings. |
orphaned | A subscription is waiting for credentials or expiry before it can be cleaned up. |
The view shows Delivery, Subscription ID, Expires, Last renewal, Delivery verified and Last notification. Webhook verification means Graph's validation request reached CoderFlow. Event Hubs verification means Graph's validation message arrived through the hub within 60 seconds. A successful connection test alone does not mean delivery is working.
Catch-up shows Requested, the reason and request time while recovery is pending, or None pending. Last catch-up shows the most recent completed or failed catch-up's time, reason and number of emails added to the queue, with a separate Error: line if it failed. It shows None yet until a result is recorded. Counts appear as No emails added, 1 email added, or, for example, 3 emails added. These count queued emails, not tasks started; check Recent emails for their processing outcomes. A failed attempt can have added some emails before the error, and a catch-up in progress can still show the previous result.
The reason labels are:
| Reason shown | What requested recovery |
|---|---|
| after the subscription was created | Subscription creation, including recreation after delivery settings change. |
| after the subscription was recreated | Microsoft removed the subscription and CoderFlow recreated it. |
| after missed notifications | Microsoft reported missed notifications. |
| daily check (Event Hubs) | Renewal in Event Hubs mode without lifecycle delivery. |
| after the event hub's retention was exceeded | Notifications may have fallen outside the hub's retention. |
| after the automation was enabled again | The automation was re-enabled with a saved change position. |
| after a restart | Startup recovery for an enabled mail automation. |
For Event Hubs, Event Hubs consumer shows Stopped, Connecting, Connected or Error, plus Transport, Last connected and Last event. It starts when the first automation using the connection is enabled. A connected consumer and verified subscription are separate checks.
Usage shows Running tasks, Tasks today, Waiting and Follow-ups per task per day. Despite its label, Tasks today counts the preceding 24 hours.
Recent emails lists Time, Outcome, Email and Task, with a reason and task link where available. Subjects and senders are visible only to people allowed to configure the automation.
| Outcome | Meaning |
|---|---|
| New task | A new task was created. |
| Follow-up | The email was delivered to the conversation's task. |
| Recorded only | The follow-up cap was reached; no turn was started. |
| Waiting | The email is waiting for capacity, its conversation's task launch, or a retry. It also covers replies with the reason Queued until the task's current turn ends, Waiting for the conversation's task approval, or automation or environment is disabled. |
| Skipped | No task or follow-up was needed; the reason explains the filter or duplicate. |
| Error | Processing failed; inspect the reason before deciding what to do next. |
Recovery and catch-up
Normal delivery is push-based. CoderFlow renews subscriptions about once a day; renewal is not a poll for mail. Catch-up runs only when recovery is requested for subscription creation, subscription removal, missed notifications, the daily Event Hubs check, exceeded Event Hubs retention, an automation being re-enabled, or a server restart. It does not poll the folder between these requests.
Catch-up reads the folder's changes since the last saved position and queues only new mail that has not already been handled. It ignores deleted entries and mail before the initial baseline, and stays within the 45-day duplicate-memory window. Duplicate tracking prevents overlap with push delivery from starting the same work again. Creation catch-up closes the gap between the initial baseline and subscription creation.
Large backlogs are processed in slices, with progress saved so later slices can resume. The new-task limits still apply: adding mail to the queue does not bypass Running tasks or Tasks per day, and excess mail waits. Failed catch-ups retain their request and retry with increasing delays; Last catch-up shows the error.
If Microsoft expires the saved change position, CoderFlow rebuilds it and considers mail received since the start of the last successful catch-up, with a two-minute overlap. If no successful catch-up is recorded, it uses the initial baseline. Recovery never goes before that baseline or beyond the 45-day duplicate memory; a small safety margin keeps it inside that window. An older email outside this window is not queued by catch-up.
At startup, CoderFlow checks each enabled mail automation's subscription, recreates it if Microsoft reports it missing, and requests catch-up. A failed subscription check is retried. Disabled automations do not start catch-ups.
In the default Event Hubs configuration, a daily catch-up follows renewal because lifecycle events are not delivered through the hub. This is the routine recovery check in that mode; other catch-ups require one of the events above.
Microsoft lists message-notification latency as under one minute on average and up to three minutes; task startup, queue limits and retries add time. See Microsoft's subscription latency table. Webhook delivery retries can continue for up to four hours, including delayed retries after the server returns. See webhook retry behavior.
An Event Hubs setup without lifecycle delivery may not discover missed mail until the daily catch-up. Recovery is not immediate: allow time for reconnects, Microsoft throttling and the task limits. Mail deleted before it can be fetched cannot become a task.
Event Hubs lifecycle setting
Set CODERFLOW_M365_EVENTHUB_LIFECYCLE in the CoderFlow server process environment:
| Value | Behavior |
|---|---|
none (default) | No lifecycle notification URL for Event Hubs. A daily catch-up follows renewal. |
eventhub | Requests lifecycle notifications through the event hub, so missed or removed subscriptions can trigger recovery without the daily fallback. |
Microsoft support for an Event Hubs lifecycle notification URL is pending confirmation. Keep none unless support has been confirmed for your deployment. This setting does not change webhook mode.
Disable mail before renaming an environment
An environment cannot be renamed while it has an enabled Microsoft 365 mail automation, a subscription record (including one pending deletion), or a managed mail webhook.
Disable all its email automations, then wait for subscription removal and managed-webhook cleanup. Use History → Refresh until no subscription remains; the disabled view says “The automation is disabled, so there is no subscription. Enabling it creates one.” An Off badge alone is not proof that cleanup has finished. If cleanup fails, correct the reported access or connection problem and allow the retry to complete before renaming.
The refusal begins “Microsoft 365 mail is active in this environment”, lists the blockers, and ends “Disable the Microsoft 365 mail automations and wait until their status shows no subscription, then rename.” While a rename is in progress, enabling mail or saving a Microsoft 365 connection is refused with “Microsoft 365 mail cannot be changed while this environment is being reset or renamed.”
Security and data handling
- Untrusted input — Always on, including follow-ups. Email is framed as information from its sender. CoderFlow MCP tools remain read-only, apart from renaming the task itself. This does not remove the environment's repository, mount or network access; give the intake environment only what it needs.
- Allowed senders — Restrict who can start work, and retain tenant anti-spoofing controls. Leaving this empty accepts mail from anyone.
- Limits — Bound task launches and reply loops. Start with the defaults and review usage before raising them.
Use an automation owner with only the permissions needed for intake. CoderFlow rechecks that owner's access before launching work or sending a follow-up. The mailbox integration has no outbound-mail permission or reply action.
The client secret is encrypted on the server. Microsoft 365 connection credentials and subscription authentication secrets never reach task containers. Notifications are checked against the subscription, tenant and a subscription-specific secret before they enter the durable queue. Event Hubs roles separate Graph's sending access from CoderFlow's receiving access.
Email content goes to the task's configured AI provider as part of its instructions and files. Subjects, the template title and the start of the automation's instructions can also go to the server's Utility AI to generate task names; the email body is not sent for naming. Ensure the watched mailboxes are appropriate for that data flow.
Troubleshooting
Connection checks fail
Use the message in Connection checks to identify the next step:
| Message | Action |
|---|---|
The client secret is invalid. Enter the secret value, not the secret ID. | Enter the app's secret value. |
The client secret has expired. Create a new secret and enter its value. | Create and save a replacement secret. |
The Microsoft tenant was not found. Check the tenant ID or verified domain. | Correct Tenant ID. |
The app was not found in this tenant. Check the tenant ID and client ID. | Check that the app belongs to the selected tenant. |
Enter the client secret. A saved secret can only be used with its saved tenant and client and a valid credential binding. | Re-enter the secret for the current tenant and application. |
Event Hubs access was denied. Give the CoderFlow app Azure Event Hubs Data Receiver on the hub. | Assign the receiving role to the CoderFlow app on the configured hub. |
The Event Hubs namespace or event hub was not found. Check the namespace and event hub name. | Check Namespace and Event hub. |
Event Hubs could not be reached. Allow outbound AMQP on port 5671 or WebSockets on port 443. | Check outbound firewall, DNS and proxy rules. |
The Microsoft 365 connection test timed out. Try again and check outbound network access. | Restore outbound access and retry Test connection. |
Microsoft authentication failed. Check the tenant, app credentials and outbound network access. | Check credentials and access to Microsoft's sign-in and Graph endpoints. |
Delivery is not verified
“Graph's validation message did not arrive through the event hub within 60 seconds” means CoderFlow did not observe Graph's validation through the configured hub. Check the Microsoft Graph Change Tracking Data Sender assignment, the hub, the dedicated consumer group, and Event Hubs consumer status. A token or properties test does not prove Graph can send.
“Graph created the subscription without a webhook validation handshake” means the webhook handshake was not observed. Check Public base URL, public HTTPS, and proxy access to /api/webhooks/inbound/* on the main server or webhook-only listener.
“Another server uses this app registration” means a conflicting subscription could not be matched to this server's delivery URL. Give each server its own app registration; do not reuse production's app on a test server.
Mailbox access or folder lookup fails
“Microsoft 365 denied access to the mailbox (check RBAC for Applications)” calls for checking the Exchange service principal, Application Mail.Read assignment and mailbox scope with Test-ServicePrincipalAuthorization. Allow up to two hours after changing the scope; do not fix this by granting tenant-wide Entra Mail.Read.
For “Mailbox not found”, check Mailbox. For “Configured mail folder was not found” or “Mail folder path is ambiguous”, check Folder and use an unambiguous path such as Inbox/Support.
“Another automation already watches this mail folder” means the resolved folder is already in use. Keep one watcher for that mailbox and folder on the connection.
Port 5671 does not respond
“Port 5671 did not respond; using WebSockets on 443. Restart the server to retry port 5671.” explains the automatic fallback after three silent native attempts. If the consumer is Connected over 443, it can continue receiving mail. Restore outbound 5671 access before restarting if you want to use that transport again.
Mail arrived but no task started
Check Delivery verified, Recent emails and Usage. Mail may be Skipped, Waiting, a Follow-up on an existing task, or Recorded only after the follow-up cap. The initial baseline deliberately ignores existing mail. Check the automation creator's current access and the selected folder before resending; duplicate tracking prevents a second task for the same message.
For an environment rename refusal, follow Disable mail before renaming an environment.