Source: https://cli.nylas.com/guides/email-to-teams-notifications

# 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](https://cli.nylas.com/authors/nick-barraclough) Product Manager

Reviewed by [Qasim Muhammad](https://cli.nylas.com/authors/qasim-muhammad)

Updated June 9, 2026

> **TL;DR:** Pull matching mail with `nylas email search "*" --json`, wrap each message in an Adaptive Card with `jq`, and `curl` it to a Teams Workflows webhook. The old connector URLs died in May 2026, and the replacement is picky about payload shape — the exact card format that posts cleanly is below.

Command references used in this guide: [`nylas email search`](https://cli.nylas.com/docs/commands/email-search), [`nylas email mark read`](https://cli.nylas.com/docs/commands/email-mark-read), and [`nylas webhook create`](https://cli.nylas.com/docs/commands/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](https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/) 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](https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498), 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](https://cli.nylas.com/guides/getting-started)). 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.

```bash
# Unread alerts from one sender, as a JSON array
nylas email search "*" \
  --from "alerts@yourcompany.com" \
  --unread \
  --json --limit 25 > teams-inbox.json
```

## What 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](https://adaptivecards.microsoft.com/). 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.

```bash
#!/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"
done
```

## How 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](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/rate-limit) 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.

```bash
# 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](https://cli.nylas.com/guides/email-to-slack-notifications) — the same pipeline aimed at a Slack channel
- [Email to Mattermost notifications](https://cli.nylas.com/guides/email-to-mattermost-notifications) — self-hosted chat instead of Teams
- [VIP sender email alerts](https://cli.nylas.com/guides/vip-sender-email-alerts-cli) — alert only on mail from people who matter
- [Extract email data with jq](https://cli.nylas.com/guides/extract-email-data-jq) — more recipes for shaping message JSON
- [EmailEngine vs Nylas](https://cli.nylas.com/guides/emailengine-vs-nylas) — compare self-hosted email API options
- [Unified.to vs Nylas](https://cli.nylas.com/guides/unified-to-vs-nylas) — unified-API alternatives for mailbox access
- [Full command reference](https://cli.nylas.com/docs/commands) — 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](https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/), [create incoming webhooks with Workflows](https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498), and the [Teams cards reference](https://learn.microsoft.com/en-us/microsoftteams/platform/task-modules-and-cards/cards/cards-reference).
