Connect FlutterFlow to Slack by either posting to an Incoming Webhook URL (simplest — one channel, no auth header) or calling the Slack Web API with a bot token for rich Block Kit messages and threads. Both the webhook URL and the bot token are secrets — never store them in FlutterFlow directly. Route all Slack calls through a Firebase Cloud Function that holds the credentials server-side.
| Fact | Value |
|---|---|
| Tool | Slack API |
| Category | Communication |
| Method | FlutterFlow API Call |
| Difficulty | Intermediate |
| Time required | 45 minutes |
| Last updated | July 2026 |
Send Slack Notifications and Messages from Your FlutterFlow App
Slack's API gives your FlutterFlow app two paths to post messages. The simplest is an Incoming Webhook URL — a single URL you generate for one specific channel that accepts a plain `POST {"text":"..."}` with no Authorization header. It takes about five minutes to set up and is ideal for notification-style use cases like "alert my team when a user submits a form." The trade-off is that each webhook URL is locked to one channel, so if you need to post to multiple channels or include interactive Block Kit attachments, you want the full Slack Web API (`chat.postMessage`) with a bot token.
The Slack Web API authenticates with a Bearer token in the format `xoxb-...`. The bot token grants the ability to post messages to any channel the bot has been invited to, upload files, create threads, and react to messages — far richer than a webhook. The Slack API is free to use; the only cost is your Slack workspace plan (Free plan, Pro at $8.75+/user/month, etc.).
Here is the critical security constraint for FlutterFlow: both the Incoming Webhook URL and the bot token are secrets. FlutterFlow compiles your project to Dart code that runs on the user's device (iOS, Android, or web browser). Any value you put in an API Call header or hardcode in a Dart custom action ships inside the compiled app and can be extracted with basic tooling. The solution is a Firebase Cloud Function that holds the secret at runtime and proxies the Slack call — FlutterFlow calls your function URL (a plain HTTPS endpoint), and the function does the actual Slack API request. Rate limits for the Web API are tiered per method — `chat.postMessage` allows roughly one message per second per channel before you see 429 responses, so avoid firing a burst of messages from a Repeating Group client-side.
Integration method
FlutterFlow calls a Firebase Cloud Function that holds your Slack bot token or Incoming Webhook URL. The Cloud Function forwards the request to the Slack Web API (`chat.postMessage`) or the webhook endpoint and returns a result. This proxy pattern is mandatory because FlutterFlow compiles to client Dart — any secret stored in an API Call header ships inside the app and can be extracted.
Prerequisites
- A Slack workspace where you have admin access or permission to install apps
- A Firebase project with Blaze (pay-as-you-go) plan enabled for Cloud Functions
- A FlutterFlow project with an API Calls panel available
- Node.js installed locally to deploy the Firebase Cloud Function (or use the Firebase Console inline editor)
- Basic familiarity with Firebase Cloud Functions (the proxy step is covered below)
Step-by-step guide
Create a Slack app and add the bot token or Incoming Webhook
Go to api.slack.com/apps and click 'Create New App'. Choose 'From scratch', name the app (e.g., 'FlutterFlow Notifier'), and select your workspace. Once created, navigate to 'Incoming Webhooks' in the left sidebar and toggle 'Activate Incoming Webhooks' on. Click 'Add New Webhook to Workspace', choose a target channel (e.g., `#notifications`), and click Allow. Copy the webhook URL — it looks like `https://hooks.slack.com/services/T00.../B00.../XXX`. For the full Web API, instead go to 'OAuth & Permissions' in the left sidebar, scroll to 'Scopes', and under 'Bot Token Scopes' add `chat:write`. Click 'Install to Workspace', confirm, and copy the 'Bot User OAuth Token' that starts with `xoxb-...`. Both the webhook URL and the bot token are secrets — do not paste them anywhere in FlutterFlow yet. Store them somewhere temporarily (a local text file or password manager) until you deploy the Cloud Function in the next step.
Pro tip: The Incoming Webhook is locked to one channel; if you later need to post to multiple channels, the bot token + `chat.postMessage` approach is more flexible. Start with the webhook for a quick demo, then graduate to the bot token.
Expected result: You have either an Incoming Webhook URL (`hooks.slack.com/services/...`) or a bot token (`xoxb-...`) and know which channel you're posting to.
Deploy a Firebase Cloud Function to proxy Slack calls
Because the bot token and webhook URL are secrets, they must live in a server environment, not in your Flutter app. Firebase Cloud Functions run Node.js on Google's servers — your FlutterFlow app calls the function's HTTPS URL, and the function makes the actual Slack request using the secret it holds in its runtime config. In the Firebase Console, open your project and go to Functions → Get started. If you prefer the CLI, set up Firebase CLI locally and run `firebase init functions`. In your `index.js` (or `index.ts`) file, add an HTTPS function that reads your Slack token from `process.env.SLACK_BOT_TOKEN` (or `functions.config().slack.token` if using the older SDK), builds the Slack request body with the incoming `channel` and `text` parameters, and POSTs to `https://slack.com/api/chat.postMessage`. Parse the JSON response and return `{ ok, ts, error }` back to FlutterFlow. Add CORS headers so the function is callable from the FlutterFlow web preview. Set the `SLACK_BOT_TOKEN` environment variable in the Firebase Console under Functions → select your function → Edit → Environment Variables, or use the CLI: `firebase functions:config:set slack.token='xoxb-...'`. Deploy with `firebase deploy --only functions`. Copy the deployed HTTPS URL — it looks like `https://us-central1-your-project.cloudfunctions.net/sendSlackMessage`.
1// functions/index.js (Firebase Cloud Function — Node.js 18)2const functions = require('firebase-functions');3const fetch = require('node-fetch');45exports.sendSlackMessage = functions.https.onRequest(async (req, res) => {6 // Allow CORS for FlutterFlow web preview7 res.set('Access-Control-Allow-Origin', '*');8 res.set('Access-Control-Allow-Headers', 'Content-Type');9 if (req.method === 'OPTIONS') { res.status(204).send(''); return; }1011 const { channel, text } = req.body;12 if (!channel || !text) {13 res.status(400).json({ ok: false, error: 'missing_channel_or_text' });14 return;15 }1617 const token = process.env.SLACK_BOT_TOKEN; // set in Firebase env vars18 const response = await fetch('https://slack.com/api/chat.postMessage', {19 method: 'POST',20 headers: {21 'Authorization': `Bearer ${token}`,22 'Content-Type': 'application/json'23 },24 body: JSON.stringify({ channel, text })25 });2627 const data = await response.json();28 // IMPORTANT: Slack returns HTTP 200 even on failure — always check data.ok29 res.status(200).json({ ok: data.ok, ts: data.ts, error: data.error || '' });30});Pro tip: If you're using the Incoming Webhook instead of the bot token, replace the Slack call with a POST to the webhook URL and body `{ text }`. No Authorization header needed.
Expected result: Your Firebase Cloud Function is deployed and accessible at an HTTPS URL. You can test it with a POST request using a tool like Postman or the Firebase Console shell.
Create a Slack API Group in FlutterFlow pointing to your Cloud Function
Open your FlutterFlow project. In the left navigation, click API Calls. Click the + Add button and select Create API Group. Name the group 'Slack Proxy'. In the Base URL field, paste your Firebase Cloud Function's base HTTPS URL — for example `https://us-central1-your-project.cloudfunctions.net`. You do not need any shared headers here because authentication is handled inside the Cloud Function, not in FlutterFlow. Click Save to create the group. Next, inside the Slack Proxy group, click + Add → Create API Call. Name the call 'Send Message'. Set the Method to POST and the Endpoint to `/sendSlackMessage` (the function name). Go to the Body tab and set the Body Type to 'JSON'. Add two body fields: `channel` mapped to a Variable, and `text` mapped to a Variable. Click the Variables tab and create two variables: `channel` (String, no default) and `text` (String, no default). These variables will be populated at runtime from your widget. Go to the Response & Test tab. Expand Test Values, enter a real channel name (e.g., `#test`) and a text string, and click Test API Call. You should see a 200 response with `{ "ok": true, "ts": "..." }`. If you see `ok: false`, check the error field — a common one is `not_in_channel`, which means the bot hasn't been invited to that channel yet (do `/invite @YourBotName` in Slack).
1// API Call configuration reference (for documentation)2{3 "method": "POST",4 "base_url": "https://us-central1-your-project.cloudfunctions.net",5 "endpoint": "/sendSlackMessage",6 "body_type": "JSON",7 "body": {8 "channel": "{{ channel }}",9 "text": "{{ text }}"10 },11 "variables": [12 { "name": "channel", "type": "String" },13 { "name": "text", "type": "String" }14 ]15}Pro tip: Never add an Authorization header in FlutterFlow for this call — the Cloud Function handles authentication. If you add the bot token here, it will ship inside your compiled app.
Expected result: The API Call test returns `{ "ok": true }` and you see the test message appear in your Slack channel.
Parse the API response and bind it to your widget action
Now that the API Call works in the Test tab, go back to the Response & Test tab and click Generate JSON Paths from the successful response. FlutterFlow will parse the response body and create JSON Path entries automatically. You should see paths like `$.ok` (Boolean), `$.ts` (String), and `$.error` (String). These paths let you read the response values in your Action Flow. Next, go to the widget that should trigger the Slack message — for example a Button on your screen. Open the widget's Actions panel (click the widget → go to the Actions tab in the right sidebar). Click + Add Action. Under Integrations, select API Call. Choose the Slack Proxy group and the 'Send Message' call. In the variable bindings section, set `channel` to a widget value (e.g., a TextField state variable or a hardcoded channel string like `#alerts`) and `text` to another widget value or an App State variable. After the API Call action, add a Conditional Action: check if the response JSON path `$.ok` equals `true`. If true, navigate to a success screen or show a SnackBar with 'Message sent!' If false, show a SnackBar reading the `$.error` path (e.g., `not_in_channel`, `channel_not_found`). This is critical — Slack returns HTTP 200 even when the call fails, so the only reliable signal is `ok: false` in the JSON body.
Pro tip: Add a loading state (disable the button and show a CircularProgressIndicator) while the API Call is in flight. Wire the action: Set Loading true → API Call → Set Loading false → conditional on ok.
Expected result: Tapping the button in Test/Run mode fires the API Call and you see the Slack message arrive in your channel. The conditional check correctly shows success or error feedback in the app.
Invite the bot to channels and handle edge cases
Before your integration is production-ready, make sure the Slack bot has access to every channel you plan to post to. In Slack, open the target channel, type `/invite @YourBotName`, and press Enter. Do this for each channel in your list. If your app has a dynamic channel selector (e.g., a DropDown widget with a list of channel names or IDs), remember that the bot must be invited to each of those channels manually — there is no auto-join for bots without additional scopes. Also decide whether you want to post to a channel by its display name (`#general`) or by channel ID (`C01234ABC`). Using the channel ID is more robust — display names can change, but IDs never do. You can find a channel's ID by clicking the channel name in the Slack desktop app and scrolling to the bottom of the About section. If you need to post to a private channel, the bot must be explicitly invited there too — private channels require `chat:write` scope AND membership. For rate limiting: the `chat.postMessage` endpoint allows roughly one message per second per channel on the standard rate tier. If your app might burst multiple posts (e.g., sending to 10 users simultaneously), add server-side batching in the Cloud Function rather than calling the FlutterFlow API Call in a loop on the client. Finally, if you want users to see a real Slack thread inside your Flutter app, you'll need a separate `GET /conversations.replies` or `GET /conversations.history` call — those read operations also require the `channels:history` scope on the bot token.
Pro tip: If you'd rather skip the Cloud Function setup entirely, RapidDev's team builds FlutterFlow integrations like this every week — free scoping call at rapidevelopers.com/contact.
Expected result: Your bot is invited to all target channels, you've tested posting from the FlutterFlow app to at least one channel, and you understand how to handle the `ok: false` error case in your action flow.
Common use cases
Team notification app that alerts Slack when a user completes an in-app action
Build a FlutterFlow app where submitting a form, completing a purchase, or hitting a milestone automatically sends a formatted Slack message to your team's `#alerts` channel. The Cloud Function formats a Block Kit attachment with the user's name, action, and timestamp. Your team gets a rich, actionable notification without leaving Slack.
When the user taps 'Submit Order', send a Slack message to #orders-team with the order total, customer name, and a link to the admin dashboard.
Copy this prompt to try it in FlutterFlow
Internal support app where agents can send Slack updates from mobile
A FlutterFlow mobile app for field agents or support staff where tapping a button posts a status update to a Slack channel. The app presents a text field and a channel selector; on submit it calls the Cloud Function which invokes `chat.postMessage` with the selected channel and message text. The function validates the response and surfaces any errors back to the app.
Build a screen with a channel dropdown (populated from a Firestore list) and a message text field. Tapping Send posts to the selected Slack channel via the proxy function.
Copy this prompt to try it in FlutterFlow
Customer-triggered escalation flow that pings the engineering Slack channel
A FlutterFlow customer app includes a 'Report a Problem' screen. When submitted, the app calls the Cloud Function which posts a detailed message to `#engineering` including the error description, device info, and user ID. A second Cloud Function endpoint (registered as a Slack Events URL) can listen for emoji reactions from engineers and update a Firestore field that the app polls to show resolution status.
Add an 'Escalate to Engineering' button that sends a Slack message to #engineering with the bug description and a link to the user's session log.
Copy this prompt to try it in FlutterFlow
Troubleshooting
`ok: false` with `error: 'not_in_channel'` even though the bot token is correct
Cause: The Slack bot must be explicitly invited to the target channel before it can post there. Simply having the `chat:write` scope does not grant channel membership.
Solution: In Slack, open the target channel and type `/invite @YourBotName`. Repeat for every channel you want to post to. For private channels, membership is required and the invitation must be done by a channel member.
The Cloud Function returns a 200 but the FlutterFlow API Call shows an error or the Slack message never appears
Cause: Slack's API always returns HTTP 200 — even for failures. FlutterFlow sees a 200 and marks the call 'successful', but the actual result is in the JSON body field `ok: false`. The error string in `error` reveals the true problem (e.g., `invalid_auth`, `channel_not_found`, `msg_too_long`).
Solution: In the API Call's Response & Test tab, generate JSON Paths and include `$.ok` and `$.error`. In your Action Flow, add a Conditional Action after the API call: if `$.ok` is `false`, read `$.error` and surface it in a SnackBar or error widget. Never rely solely on HTTP status code for Slack calls.
429 Too Many Requests response when posting messages rapidly
Cause: `chat.postMessage` has a rate limit of approximately one message per second per channel on the standard tier. Calling it in a client-side loop (e.g., iterating a Repeating Group) causes 429 responses with a `Retry-After` header.
Solution: Move any batch-sending logic to the Cloud Function. The function can queue messages, add delays between posts, or use Slack's Block Kit to combine multiple updates into a single message with sections. Never loop API calls from the FlutterFlow client.
The test works in the API Calls Test tab but fails in the FlutterFlow web preview with `XMLHttpRequest error`
Cause: The API Calls Test tab runs on FlutterFlow's servers and is not subject to browser CORS restrictions. The web preview runs in a browser and requires the server to send CORS headers. If your Cloud Function is missing `Access-Control-Allow-Origin: *`, web builds will fail.
Solution: In your Cloud Function, add CORS headers at the top of the handler: `res.set('Access-Control-Allow-Origin', '*')` and handle preflight `OPTIONS` requests by returning 204. The code example in Step 2 above already includes this pattern.
Best practices
- Always proxy Slack credentials (bot token, webhook URL) through a Firebase Cloud Function — never store them in FlutterFlow App State, Dart custom actions, or API Call headers, as they will ship in the compiled app.
- Check `ok` in the JSON response, not just the HTTP status code — Slack returns HTTP 200 for failures, and only `ok: false` + `error` reveal what went wrong.
- Invite the bot to every target channel before going to production — `not_in_channel` is the most common first-time error and requires a Slack-side fix, not a code fix.
- Use channel IDs (e.g., `C01234ABC`) rather than display names (`#general`) in your API calls — display names can be changed by workspace admins, but IDs are permanent.
- Rate-limit awareness: keep message sends to one per second per channel on the client side, and move any burst logic (batch alerts, bulk notifications) to the Cloud Function to avoid 429 responses.
- For read operations (fetching Slack history or threads into the app), add the `channels:history` and `channels:read` scopes to the bot token in addition to `chat:write`.
- Test on a private non-production channel before wiring to team-facing channels — it prevents accidental spam to your `#general` or `#announcements` during development.
Alternatives
Telegram's Bot API is simpler to set up (single bot token, no workspace) and has very generous rate limits, making it a better choice for consumer-facing notification apps where Slack workspace access isn't available.
Discord Webhooks are free, support rich embeds, and require no OAuth setup — a faster alternative if your team or community is already on Discord rather than Slack.
Microsoft Teams is the right choice if your organization is in the Microsoft 365 ecosystem and already uses Teams for internal communication, offering similar Incoming Webhook support.
Frequently asked questions
Can I receive Slack messages or slash commands inside my FlutterFlow app?
Not natively — Slack pushes incoming events (messages, slash commands, interactivity payloads) to an HTTP endpoint you register in the Slack app settings. FlutterFlow has no public HTTP endpoint. The solution is to register a Firebase Cloud Function URL as the Slack Request URL and have that function write incoming events to Firestore. Your FlutterFlow app then listens to the Firestore document via a Backend Query and displays new messages in real time.
Why can't I put the bot token in an API Call header in FlutterFlow?
FlutterFlow compiles your project to Dart code that runs on the user's device. Values placed in API Call headers or Dart custom actions are included in the compiled app binary. Anyone who downloads your app can extract the token using tools like `strings` or a reverse engineering app. A leaked bot token gives full access to your Slack workspace. A Firebase Cloud Function holds the token in a server environment that users never see.
Is the Slack API free to use for FlutterFlow apps?
The Slack API itself is free — there are no per-call fees. Your cost is your Slack workspace plan (the Free plan limits message history and app integrations; Pro starts at $8.75/user/month) and Firebase Cloud Functions (which have a generous free tier but require a Blaze plan to deploy outbound HTTP calls). For most small apps, the combined cost is effectively zero or very low.
Can I use an Incoming Webhook instead of the full Web API?
Yes, and it's simpler. An Incoming Webhook URL accepts a plain `POST {"text":"..."}` with no Authorization header — you just include the URL in your Cloud Function and POST to it. The limitations are that each webhook is locked to one specific channel, and you cannot post Block Kit attachments, files, or thread replies through a webhook. For anything beyond a basic notification, the Web API with a bot token is more capable.
My Slack messages look plain. Can I send formatted Block Kit messages?
Yes. Instead of a plain `text` field, use the `blocks` array in the request body to the `chat.postMessage` endpoint. Block Kit supports sections, dividers, images, buttons, and more. Use the Slack Block Kit Builder at app.slack.com/block-kit-builder to design your layout, then copy the `blocks` JSON into your Cloud Function's request body. You can pass the block content as a JSON string variable from FlutterFlow to the function.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation