Guide
Send Email Alerts to Microsoft Teams
Microsoft disabled Office 365 connectors in May 2026. Route email to Microsoft Teams channels the supported way: a Workflows webhook fed by nylas email search JSON, formatted as an Adaptive Card.
Written by Nick Barraclough Product Manager
Reviewed by Qasim Muhammad
Command references used in this guide: nylas email search, nylas email mark read, and nylas webhook create.
Why did email to Microsoft Teams connectors stop working?
Microsoft retired Office 365 connectors in Teams, so any pipeline that forwarded email to Microsoft Teams through a connector URL no longer posts. The retirement was announced on July 3, 2024, and after several extensions the final shutdown rolled out between May 18 and May 22, 2026. The supported replacement is a Power Automate Workflows webhook.
The Microsoft 365 developer blog spells out two limitations of the new path. Messages post under the default Flow bot identity, so custom webhook icons and display names are gone. And while legacy MessageCard payloads still render, their HttpPost action buttons don't work — interactive elements require Adaptive Cards. The rest of this guide builds the migration target: a script that searches a mailbox, shapes each hit into an Adaptive Card, and posts it to a Workflows webhook.
How do I create a Teams Workflows webhook?
Create a Teams Workflows webhook from the channel itself: open More options on the channel, pick Workflows, and choose the template named Send webhook alerts to a channel. After you authenticate and select the target channel, Teams shows a dialog with the webhook URL. Copy it — that URL is the only credential the forwarder needs.
Per Microsoft's incoming webhooks with Workflows guide, six templates exist (3 for chats, 3 for channels) and the underlying trigger is called When a Teams webhook request is received. Building from scratch in the Workflows app offers 3 authentication options: anyone, any user in your tenant, or specific users. The generated URL points at an Azure Logic Apps host such as prod-XX.westus.logic.azure.com and embeds a signature, so store it in an environment variable rather than in a committed script. Anyone holding the URL can post to your channel if you picked the unauthenticated option.
How do I pull matching email as JSON?
Pull matching mail with nylas email search, which filters server-side by sender, subject, read state, and date before anything is downloaded. The --json flag emits a parseable array, and --limit caps results at 20 by default, auto-paginating past 200 when raised.
Install the CLI with brew install nylas/nylas-cli/nylas (other methods are in the getting started guide). Scope the search tightly: a Teams channel that receives every inbox message becomes noise within a day, while one fed only unread alerts from a monitoring address stays readable. The command below grabs unread messages from one sender and writes them to a file for the next step.
# Unread alerts from one sender, as a JSON array
nylas email search "*" \
--from "alerts@yourcompany.com" \
--unread \
--json --limit 25 > teams-inbox.jsonWhat does the Adaptive Card payload look like?
A Workflows webhook expects a JSON body whose attachments array carries one Adaptive Card with the content type application/vnd.microsoft.card.adaptive. The card body is a list of elements such as TextBlock, defined in the Adaptive Cards documentation. The script below sends one card per POST — a run that finds 25 messages makes 25 webhook calls. The attachments array can batch several cards into a single POST, but per-message posts keep failures isolated: one malformed subject breaks one card, not the whole batch.
The loop below has jq build a version 1.4 card per message — bold subject, subtle sender line, wrapped snippet — so quoting and escaping are handled instead of hand-assembled in the shell. After a successful POST, nylas email mark read drops the message from the next unread query. The trigger acknowledges each POST asynchronously, so an empty response is normal.
#!/bin/bash
# email-to-teams.sh — post each email as an Adaptive Card
TEAMS_WEBHOOK="$TEAMS_WORKFLOW_URL"
jq -c '.[]' teams-inbox.json | while read -r msg; do
card=$(echo "$msg" | jq -c '{
type: "message",
attachments: [{
contentType: "application/vnd.microsoft.card.adaptive",
content: {
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
type: "AdaptiveCard",
version: "1.4",
body: [
{ type: "TextBlock", size: "Medium", weight: "Bolder",
text: (.subject // "(no subject)") },
{ type: "TextBlock", isSubtle: true,
text: ("From: " + (.from[0].email // "unknown")) },
{ type: "TextBlock", wrap: true, text: (.snippet // "") }
]
}
}]
}')
curl -s -X POST "$TEAMS_WEBHOOK" \
-H "Content-Type: application/json" \
-d "$card"
# Mark as read so the next run skips it
id=$(echo "$msg" | jq -r '.id')
nylas email mark read "$id"
doneHow do I schedule the forwarder around Teams rate limits?
Schedule the email-to-Teams script from cron at a 5-minute interval and cap each run with --limit 25. Cards land in the channel under the Flow bot identity, and the Teams rate-limit table allows a bot 7 sends to one conversation per second and 60 per 30-second window, so a sequential 25-card run stays under the ceiling.
The --unread filter plus the mark-read call already de-duplicate between runs, so the scaling question on Teams is routing, not frequency. Every channel issues its own Workflows URL: billing mail and incident mail become two searches against the same mailbox grant, each POSTing to its own URL, and neither channel gets near the per-conversation cap. When the incident channel needs sub-minute latency, replace its cron entry with nylas webhook create --triggers message.created and feed the same jq card builder from your webhook receiver — delivery drops from a 5-minute worst case to about a second.
# crontab -e — poll every 5 minutes, keep a log
*/5 * * * * /usr/local/bin/email-to-teams.sh >> /var/log/teams-email.log 2>&1
# Real-time alternative: push instead of poll
nylas webhook create \
--url "https://your-server.com/email-hook" \
--triggers message.created \
--description "Email to Teams forwarder"Next steps
- Email to Slack notifications — the same pipeline aimed at a Slack channel
- Email to Mattermost notifications — self-hosted chat instead of Teams
- VIP sender email alerts — alert only on mail from people who matter
- Extract email data with jq — more recipes for shaping message JSON
- EmailEngine vs Nylas — compare self-hosted email API options
- Unified.to vs Nylas — unified-API alternatives for mailbox access
- Full command reference — every flag and subcommand documented
Teams-side behavior is described from Microsoft's documentation, not from a verified test in every tenant configuration. References: Office 365 connectors retirement announcement, create incoming webhooks with Workflows, and the Teams cards reference.