Slack
SLACK · MESSAGING
Connect Slack to your AI app with scoped access.
Connect supported Slack workflows to your AI app through Fabriq Developer. Make the token type, approved scopes and available actions explicit before your agent calls a tool.
Connect Slack with Fabriq Developer
Your user consents at Slack. The credential lands in a vault your agent cannot read, and the tool list it gets back is the intersection of that user’s grant and the agent’s own scopes.
Provider names are surfaces, not vendors, and the authoritative list is the capabilities endpoint rather than any page including this one — an unknown name returns 400 with the valid list. Fabriq Developer is free to start and needs no provider paperwork. Start free or read the quickstart.
Ship the Slack integration
Every route, header, field name and error string below is the gateway’s own, and every tool id and scope comes from the generated schema behind the action list further down this page.
Prerequisites
Before the redirect above can do anything, four things have to be in place on the Fabriq side and one or two on the Slack side.
- A Fabriq Developer app, and its AF_APP_ID and AF_APP_SECRET. They ride on every app-scoped call as X-App-Id and X-App-Secret.
- A Redirect URL saved on the agent’s Connect & webhooks page. It is checked before anything else, so leaving it empty fails every connect with “B2B2C OAuth callback URL not configured” and hides whatever else is wrong.
- An external user id of your own, one per real person, created with POST /api/v1/apps/{app_id}/external-users. The call is idempotent and a 409 means the id already exists.
- Grants on the agent itself. The tool list a user gets back is the intersection of that user’s grant and the agent’s own scopes, so an agent with no overlapping grant sees nothing even after a clean consent.
- A Slack workspace your user can authorize in. The connector’s channel, private-channel and DM reads are documented in its own schema as bot scopes, and Slack describes a bot token (xoxb-) as tied to your app rather than to a user’s identity, while a user token (xoxp-) acts on behalf of a member.
- The bot added to anything private you expect to read. slack_get_group_messages requires the bot to be invited to the private channel first, and slack_get_dm_messages only sees DM conversations the bot is part of.
Connect an account
The route is /integrations/slack-connector but the gateway provider id is slack. Use slack in the path; the slug is a URL, not an identifier.
Post the initiate, redirect the user to oauth_url, and let them authorize in their workspace. The scopes this connector declares are channels:history, channels:read, chat:write, groups:history, groups:read, im:history, im:read and users:read.
Make one tool call
slack_list_channels is the first call worth making: a GET at /api/v1/tools/slack/channels, no required arguments, one scope (channels:read), and it returns the channel ids every other Slack tool wants as input.
slack_get_connection_info is cheaper still and declares no scope at all — it answers which workspace this connection points at, and whether the connection is live, before you spend a scope on anything.
Inspect the result
Scope to action, as the connector declares it: channels:read covers slack_list_channels, and channels:read with channels:history covers slack_get_messages. groups:read and groups:history cover the private-channel reads, im:read and im:history the DM reads, users:read slack_list_users and slack_get_user, and chat:write slack_send_message. slack_get_connection_info declares no scope.
That is ten actions, nine of them reads. This connector is not the Slack API; treat the list above as the whole surface rather than a sample of it.
- tools/list has two gates: the connection has to exist, and the agent’s grants have to intersect the connection’s scopes. Connected with zero tools is a real state, so say “connected but no actions live” rather than showing the integration as on.
- A listed connection is not a working one. The initiate writes the row before the user ever reaches the provider: status “configured” means the row exists and nothing else does, and it yields zero tools. Key every user-facing state off status == "connected".
- Do not infer readiness from granted_scopes being non-empty. Some providers legitimately grant none.
- The callback query parameters — status, tool, connection_id, external_user_id — are not signed. Treat them as a UI hint, resolve the user from your own session, and confirm by re-reading the connections list. The signed channel is the connection.completed webhook.
The payload above shows the shape of a response using the field names the gateway documents. It is illustrative: it was not captured from a live Slack account, so treat the values as placeholders and read your own response.
Disconnect
Deleting the connection is one app-scoped call, and it is the only way to take Slack away from an agent without taking the agent down.
- The delete removes the stored credential and the connection row on the Fabriq side, so the user has to authorize again to reconnect.
- Whether the authorization is still listed in the provider’s own connected-apps screen is the provider’s state, not Fabriq’s. If your flow needs the grant gone there too, send the user to the provider and check.
- Drop any cached user token when you disconnect, and on a connection.needs_reauth webhook.
Troubleshooting
- Private-channel or DM reads return nothing even though the scopes are granted: the bot has to be in the conversation. slack_get_group_messages needs the bot invited to the channel, and slack_get_dm_messages shows only DMs involving the bot, not the user’s DMs with other people.
- The message posted as the app rather than as the person: slack_send_message sends as the bot by default, and sending as the authenticated user means passing as_user=true, which the tool’s own schema says requires user scopes.
- Decide the token type before you write the copy on your connect button. Slack’s own token documentation separates bot tokens, which are tied only to your app, from user tokens, which take actions on behalf of a member — and the two produce different author names on the same message.
- Consent completed but tools/list comes back empty: diff the authorize URL’s scope= parameter against the connection row’s granted_scopes before blaming the console. The URL tells you what was actually requested, and a scope never requested can never be granted.
- Every connect fails with “B2B2C OAuth callback URL not configured”: the Redirect URL on the agent’s Connect & webhooks page is empty. It is checked first, so it masks every other misconfiguration.
- Calls return 200 and nothing happens: an external-user token was posted to /mcp instead of /mcp/external. Both answer 200, so this one fails silently — pin the route with a test.
- An unknown provider name returns 400 with the list of valid names. Read that error; it is accurate. Provider names are surfaces rather than vendors.
Slack-side requirements checked against Slack token types (bot vs user tokens) (2026-10-29).
Connecting Slack in practice
Slack’s Marketplace guidelines put the scopes an autonomous agent most wants, search:read and the admin.* family, on the list a non-partner app is unlikely to be approved for, and route files:read and every *:history scope into an enhanced review that is separate from the normal app review. With token rotation enabled, access tokens last twelve hours and each refresh token is single use, so the token store has to be transactional rather than best effort.
Scopes that draw scrutiny
search:readListed among the scopes a non-partner Marketplace app is unlikely to be approved for, which removes the obvious way to answer "find where we discussed this".admin"Administer a workspace". admin.* scopes are generally prohibited for Marketplace apps.channels:historyEvery *:history scope triggers Slack’s enhanced review, and message history is what a retrieval agent is actually after.files:readEnhanced review, because it reaches every file shared in the conversations the app can see.groups:writeAs a user token it lets the bot add itself to private conversations, so it is called out for enhanced review.users:read.emailExposes member email addresses, which is the join key most integrations want and most security teams query.auditlogs:readReads events across all workspaces, channels and users in the org.
Rate limits
- Web API methods are tiered: Tier 1 is 1+ per minute, Tier 2 20+, Tier 3 50+, Tier 4 100+.
- chat.postMessage sits in a special tier at roughly one message per second per channel, with a separate workspace-wide limit Slack does not put a number on.
- Slack states the tier limits are subject to change, so treating a tier number as a contract is a mistake.
Who has to approve
By default members can install apps without approval from a Workspace Owner. Once an owner turns on "Only allow pre-approved apps" in App Management Settings, every install becomes a request. On Enterprise Grid an admin resolves those with admin.apps.approve, which needs the admin.apps:write scope and targets either a team_id or an enterprise_id but not both, and an app approved at the org level has to be re-approved there on any scope change.
Worth knowing
- With token rotation enabled, access tokens come back with expires_in of 43,200 seconds, that is twelve hours.
- Refresh tokens are single use and are revoked after a short grace period once redeemed through oauth.v2.access. Slack does not publish the length of that grace period, so a retry that reuses a refresh token can lock the connection out.
- At most two tokens are active in a rotation chain at once, and older ones are revoked automatically, so a blue-green deploy that keeps a stale token will lose it.
- identity.*, triggers:* and workflow.steps:execute are also on the prohibited list, so an agent design that leans on Slack workflow triggers will not clear review.
Checked against Slack Marketplace requirements (2026-09-29), Slack OAuth scopes (2026-09-29), Web API rate limits (2026-09-29), Token rotation (2026-09-29), Manage app approval (2026-09-29), admin.apps.approve (2026-09-29)
What an agent can do
Each action is granted on its own. An agent allowed to read is not thereby allowed to write, and the scope beside each row is what the acting user must have connected for it to run at all.
Often connected alongside
Put Slack behind one governed endpoint.
Same permissions, same audit trail, whatever else you connect next.