# Microsoft Teams

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 20 minutes
- Last updated: July 2026

## TL;DR

Connect FlutterFlow to Microsoft Teams by creating a Workflows-based channel webhook in Teams and POSTing an Adaptive Card from a FlutterFlow API Call. No authentication header is needed — the webhook URL is the credential. For richer features like DMing users or posting to any channel, use Microsoft Graph via a Cloud Function proxy to keep the client secret off the device.

## Send Real-Time Alerts to Microsoft Teams from Your FlutterFlow App

Microsoft Teams is the central hub for many operations teams, and pushing app events directly into a Teams channel keeps your team informed without anyone having to check a separate dashboard. FlutterFlow makes this surprisingly approachable through Teams' Workflows webhook: a single URL that accepts a structured JSON Adaptive Card payload, no auth headers needed. This is the fastest notification integration you can build in FlutterFlow — a single API Call and you're done.

Teams offers two integration paths and FlutterFlow makes the choice clear. The simple path — the one 90% of FlutterFlow builders actually need — is the channel webhook created via the Teams 'Workflows' app (which replaced the now-deprecated Incoming Webhook connector). The URL itself is the credential, and your FlutterFlow API Call posts a JSON message body directly to it. The rich path is Microsoft Graph, which lets you post to any channel, send direct messages, and manage Teams programmatically. Graph requires Azure AD OAuth with a client secret, which must never live in a compiled Flutter app — that path needs a Firebase Cloud Function or Supabase Edge Function proxy.

Both paths are free with a Microsoft 365 or Teams account. The webhook-based approach is best for operations alerts (new user signup, order placed, error spike) sent to an internal channel. The Graph path is for applications where the teams and recipients are dynamic or user-driven. This page walks through the webhook path completely and outlines the Graph upgrade when you need it.

## Before you start

- A Microsoft Teams workspace with a channel where you want to receive notifications
- Owner or member access to the Teams channel to create a Workflows webhook
- A FlutterFlow project (free tier or above)
- For the Graph path: access to the Azure Portal to register an Azure AD app
- For the Graph path: a Firebase project or Supabase project for the Cloud Function proxy

## Step-by-step guide

### 1. Create a Workflows Webhook in Your Teams Channel

Open Microsoft Teams and navigate to the channel where you want to receive notifications — for example, #ops-alerts or #new-signups. Hover over the channel name, click the three-dot (⋯) menu, and select 'Workflows'. In the Workflows dialog, search for 'Post to a channel when a webhook request is received' and click Add. Give the workflow a name like 'FlutterFlow Notifications', confirm the team and channel, and click Add workflow. Teams will generate a webhook URL — it looks like `https://prod-xx.<region>.logic.azure.com:443/workflows/.../triggers/manual/paths/invoke?...`. Copy this URL and save it somewhere safe. This URL is the only credential you need: whoever knows it can post to your channel. Do not publish it in public repositories or embed it in a consumer-facing app without a proxy layer. Note: the older 'Incoming Webhook' connectors are being phased out by Microsoft — always use the Workflows-based webhook for new integrations. If you see a dead webhook URL inherited from an old setup, recreate it through the Workflows app to get a fresh, supported URL.

**Expected result:** A webhook URL is generated and visible in the Teams Workflows dialog. A test message appears in the target channel.

### 2. Add a Microsoft Teams API Group in FlutterFlow

In your FlutterFlow project, click 'API Calls' in the left navigation panel. Click '+ Add' and select 'Create API Group'. Name the group 'MicrosoftTeams'. For the base URL, enter the first part of your webhook URL up through the domain — however, because Teams Workflows webhook URLs are full paths (not base + path), the cleanest approach for a single webhook is to leave the base URL as `https://prod-xx.<region>.logic.azure.com` and put the rest of the path in the individual API Call below. Alternatively, paste the entire webhook URL as the base URL and leave the endpoint path empty. Either approach works; keep it simple by treating the full webhook URL as the single endpoint. In the Headers section, you do not need to add any Authorization header — the webhook URL itself is the credential. Set the 'Content-Type' header to `application/json` so Teams parses the JSON body correctly. Click 'Save' to create the group. You now have an API Group ready to accept calls pointing at your Teams channel.

```
// API Group config (reference, not a file to copy)
// Group name: MicrosoftTeams
// Base URL: https://prod-xx.<region>.logic.azure.com
// Headers:
//   Content-Type: application/json
// (No Authorization header needed)
```

**Expected result:** The MicrosoftTeams API Group appears in your API Calls panel with Content-Type set and no auth header.

### 3. Create the 'Send Teams Message' API Call with an Adaptive Card Body

Inside the MicrosoftTeams API Group, click '+ Add API Call'. Name it 'sendChannelMessage'. Set the method to POST. In the endpoint path field, paste the full webhook URL path (everything after the domain). Open the Variables tab and add any variables you want to inject into the message — for example: `message_title` (String), `message_body` (String), `user_email` (String). These become `{{ variable_name }}` placeholders in your request body. Switch to the Body tab, set the body type to 'JSON', and paste an Adaptive Card payload. A simple MessageCard body works for most channel webhooks. Click 'Response & Test', paste a sample response (Teams returns `1` on success), and click 'Test API Call' to confirm the message arrives in your Teams channel. If you receive a 400 Bad Request, the most common cause is a malformed JSON body — verify your Adaptive Card structure is valid. Once the test succeeds, save the API Call.

```
{
  "@type": "MessageCard",
  "@context": "http://schema.org/extensions",
  "themeColor": "0076D7",
  "summary": "{{ message_title }}",
  "sections": [
    {
      "activityTitle": "{{ message_title }}",
      "activityText": "{{ message_body }}",
      "facts": [
        {
          "name": "User",
          "value": "{{ user_email }}"
        }
      ],
      "markdown": true
    }
  ]
}
```

**Expected result:** The test API Call sends a formatted card to the Teams channel. The channel shows the message with the title, body text, and user email fact row.

### 4. Trigger the Teams Notification from an App Action

Now connect the API Call to an action in your FlutterFlow app. Find the widget that should trigger the notification — for example, a 'Sign Up' button, a form submission confirmation widget, or a backend trigger. Click the widget to open its properties, then click '+ Add Action' in the Actions panel on the right side. Choose 'Backend/API Call' from the action types. Select your 'MicrosoftTeams' group and the 'sendChannelMessage' call. Map the variables: set `message_title` to a static string like 'New User Registered', set `message_body` to a text field value or a combination of widget state values, and set `user_email` to the current user's email from your auth provider (e.g., `currentUserEmail` from Supabase or Firebase Auth). You can also trigger this notification from a backend trigger or from the Action Flow Editor after a Supabase/Firestore write succeeds — chain it after the data-write action so the notification only fires when the save succeeds. If you want to include dynamic data like a formatted timestamp or a Firestore document field, use a Custom Function to build the string before passing it to the API Call variable.

**Expected result:** Tapping the trigger widget in Run Mode or on a real device sends the Adaptive Card to the Teams channel within a few seconds.

### 5. (Optional) Upgrade to Microsoft Graph for Rich Teams Access

If you need to post to channels dynamically (not a fixed webhook), send direct messages to specific users, or manage Teams programmatically, you must use the Microsoft Graph API. This requires Azure AD app registration and a client secret — credentials that cannot live in a FlutterFlow client app because they compile into the app bundle and can be extracted. The correct pattern is a Firebase Cloud Function that performs the client-credentials OAuth flow, mints a short-lived access token, and makes the Graph call. Here is how to set it up: In the Azure Portal, go to Azure Active Directory → App registrations → New registration. Give it a name, select 'Accounts in this organizational directory only', and register. In the app's API permissions, add 'ChannelMessage.Send' (Application permission) and grant admin consent. Copy the Application (client) ID, Tenant ID, and create a Client Secret under Certificates & secrets. Store all three in your Firebase Cloud Function's environment config or Secret Manager — never in FlutterFlow. Deploy the Cloud Function below. In FlutterFlow, add a new API Call to your MicrosoftTeams group pointing at your Cloud Function URL, passing message params as body variables. FlutterFlow never touches Azure credentials directly.

```
// Firebase Cloud Function — Microsoft Graph proxy (index.js)
const functions = require('firebase-functions');
const fetch = require('node-fetch');

const TENANT_ID = functions.config().teams.tenant_id;
const CLIENT_ID = functions.config().teams.client_id;
const CLIENT_SECRET = functions.config().teams.client_secret;
const TEAM_ID = functions.config().teams.team_id;
const CHANNEL_ID = functions.config().teams.channel_id;

async function getGraphToken() {
  const res = await fetch(
    `https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/token`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        grant_type: 'client_credentials',
        client_id: CLIENT_ID,
        client_secret: CLIENT_SECRET,
        scope: 'https://graph.microsoft.com/.default',
      }),
    }
  );
  const data = await res.json();
  return data.access_token;
}

exports.sendTeamsMessage = functions.https.onRequest(async (req, res) => {
  res.set('Access-Control-Allow-Origin', '*');
  if (req.method === 'OPTIONS') { res.status(204).send(''); return; }
  try {
    const { content } = req.body;
    const token = await getGraphToken();
    const graphRes = await fetch(
      `https://graph.microsoft.com/v1.0/teams/${TEAM_ID}/channels/${CHANNEL_ID}/messages`,
      {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ body: { content } }),
      }
    );
    const data = await graphRes.json();
    res.status(200).json({ success: true, id: data.id });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});
```

**Expected result:** The Cloud Function is deployed. A FlutterFlow API Call to the function URL posts a message to the Teams channel via Graph without any Azure credentials in the client app.

## Best practices

- Use the Workflows-based webhook (not the deprecated Incoming Webhook connector) for all new Teams integrations — the legacy connector is being retired by Microsoft.
- Never embed the webhook URL in a public consumer-facing app — proxy it through a Firebase Cloud Function so the URL cannot be scraped and abused to spam your channel.
- Store the webhook URL in FlutterFlow's App Settings → App Values (constants) rather than hardcoding it in the API Call endpoint field, so you can update it without republishing.
- Build your Adaptive Card JSON in the Adaptive Card Designer (adaptivecards.io) and validate it before pasting into FlutterFlow — malformed JSON is the most common source of 400 errors.
- For the Microsoft Graph path, cache the access token in Cloud Function memory (invalidate after 55 minutes) rather than re-minting it on every call — token exchange counts against Azure AD rate limits.
- Use FlutterFlow's Conditional Action to guard the Teams notification so it fires only on meaningful events, not on every tap — noisy channels reduce team attention.
- Test your API Call using FlutterFlow's 'Response & Test' panel with a live webhook URL before wiring it to a widget action — this isolates configuration issues from UI logic issues.

## Use cases

### SaaS app that notifies the ops channel on new signups

When a new user completes registration in your FlutterFlow app, an API Call fires to the Teams webhook and posts an Adaptive Card showing the user's name, email, and plan tier to the #new-signups channel. The ops team gets instant visibility without checking a backend dashboard.

Prompt example:

```
Post a Teams notification with the new user's name, email, and subscription plan every time a registration is completed in the app.
```

### Field service app that escalates job alerts to a dispatcher channel

A FlutterFlow field-service app lets technicians flag critical job issues. Tapping 'Escalate' triggers an Adaptive Card to the #dispatch Teams channel with the job ID, technician name, location, and issue description, so the dispatcher can act immediately.

Prompt example:

```
When a technician taps the escalate button, send a Teams card to the dispatch channel with the job details, technician name, and GPS location.
```

### E-commerce app that posts order summaries to a fulfillment channel

When an order is placed in a FlutterFlow shopping app, a Teams message appears in the #fulfillment channel showing the order number, items, total, and shipping address. The warehouse team can pick and pack without waiting for email.

Prompt example:

```
Every time an order is confirmed, post an Adaptive Card to Teams showing the order number, product list, total amount, and delivery address.
```

## Troubleshooting

### 400 Bad Request when POSTing to the Teams webhook

Cause: The Adaptive Card or MessageCard JSON is malformed — most commonly a missing `@type` or `@context` field, or an invalid JSON structure in the body.

Solution: Copy the exact MessageCard body from the step above and validate it with a JSON linter. Confirm that `@type` is `"MessageCard"` and `@context` is `"http://schema.org/extensions"`. Test in Postman before using FlutterFlow to isolate whether the issue is the payload or the FlutterFlow variable interpolation.

### Webhook URL returns 404 or the channel stops receiving messages

Cause: The Teams channel webhook was created with the deprecated 'Incoming Webhook' connector, which Microsoft is retiring. The old connector URL no longer functions.

Solution: Recreate the webhook using the Teams 'Workflows' app: channel ⋯ → Workflows → 'Post to a channel when a webhook request is received'. Update the URL in your FlutterFlow API Call to the new Workflows URL.

### Messages arrive in the wrong Teams channel

Cause: The webhook URL was generated for a different channel than intended, or you copied the wrong URL from Teams.

Solution: Open the Teams Workflows app and verify which channel each webhook is bound to. Create a separate webhook per channel and store each URL as a distinct variable in FlutterFlow so you can route messages to the right channel at call time.

### 403 Forbidden when calling Microsoft Graph from the Cloud Function

Cause: The Azure AD app registration is missing the `ChannelMessage.Send` Application permission, or admin consent has not been granted.

Solution: In the Azure Portal, go to your app registration → API permissions → Add permission → Microsoft Graph → Application permissions → search for `ChannelMessage.Send`. Click 'Grant admin consent for [your tenant]'. Redeploy the Cloud Function and retry.

### XMLHttpRequest error when calling the Teams webhook from the FlutterFlow web preview

Cause: Teams webhook endpoints block cross-origin requests from browser origins (CORS), which only affects the web build in FlutterFlow's Run Mode preview.

Solution: This CORS block only impacts the web preview, not native iOS/Android builds. Test the API Call on a real device (use FlutterFlow Test Mode on device) or test the webhook endpoint directly in Postman. Alternatively, route even the simple webhook through a Cloud Function proxy, which removes the CORS issue for web builds.

## Frequently asked questions

### Do I need a paid Microsoft 365 subscription to use Teams webhooks?

Teams is available on a free tier that includes channels and the Workflows app needed to create webhooks. The free Teams plan is sufficient for webhook-based notifications from FlutterFlow. Microsoft 365 business plans add features like unlimited message history and larger meeting sizes, but are not required for this integration.

### Can my FlutterFlow app receive messages FROM Teams?

No — FlutterFlow is a compiled client app and cannot host an HTTP endpoint. To receive Teams messages or events in your FlutterFlow app, you need to deploy a Cloud Function (Firebase or Supabase) that handles Teams' event subscription or webhook callbacks and writes incoming data to Firestore or a Supabase table. Your FlutterFlow app then reads and displays that data.

### Is the Teams Incoming Webhook connector still supported?

Microsoft is deprecating the classic Incoming Webhook connectors and recommending the Workflows-based webhook as the replacement. If your existing webhook URL stops working, recreate it through Teams → channel ⋯ → Workflows → 'Post to a channel when a webhook request is received'. The Workflows webhook URL format is different from the old connector URL.

### What is the difference between posting to a channel webhook vs using Microsoft Graph?

The channel webhook only posts to one specific channel and requires no authentication header beyond knowing the URL. Microsoft Graph lets you post to any channel, send direct messages, list team members, and manage Teams programmatically — but requires Azure AD app registration with a client secret that must be kept in a server-side Cloud Function. Start with the webhook for simple notifications and add Graph when you need dynamic channel selection or DMs.

### Will the Teams message look the same on mobile and desktop Teams apps?

Adaptive Card and MessageCard rendering is consistent across the Teams desktop app and the Teams mobile app. Some advanced Adaptive Card features (like certain input types) have limited mobile support — verify your card layout in the Adaptive Card Designer's mobile preview before deploying.

---

Source: https://www.rapidevelopers.com/flutterflow-integrations/microsoft-teams
© RapidDev — https://www.rapidevelopers.com/flutterflow-integrations/microsoft-teams
