Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Slack By James Joyner IV · · 11 min read

Securing Slack Connect: Shared Channels Without Leaking Your Workspace

Harden Slack Connect shared channels for ops: scope bots correctly, gate external members, and audit cross-org events with AI as a fast junior you review.

  • #slack
  • #chatops
  • #security
  • #slack-connect

The first time a vendor asked to set up a Slack Connect channel with my team, I said yes in about four seconds. It felt like the friendly thing to do, and it was a huge upgrade over the email thread we’d been suffering through. What I did not think about, until our security team did, was that I had just invited an external organization into a space where my deploy bot posted release notes, my alert bot named internal hostnames, and someone occasionally pasted a stack trace with a connection string in it. Slack Connect is fantastic for collaboration and quietly dangerous for ops teams who treat a shared channel like an internal one.

I now reach for AI when I’m building Slack Connect tooling, but only as a fast junior engineer. It will happily generate a bot that listens to every channel including shared ones, and it will not notice that you just exfiltrated internal telemetry to a partner org. You review before that bot touches a workspace. The model is fast; you own the blast radius.

What changes when a channel is shared

A normal channel lives entirely inside your workspace. A Slack Connect channel is co-owned: members from another org are real participants, and your bot’s events now fire for messages authored by people you don’t administer. The critical field is on every event and every conversations.info response:

{
  "channel": {
    "id": "C0EXTERNAL",
    "is_shared": true,
    "is_ext_shared": true,
    "is_pending_ext_shared": false,
    "connected_team_ids": ["T0THEIRTEAM"]
  }
}

is_ext_shared: true is your tripwire. Any bot that posts sensitive content needs to check this before it speaks, not after.

Gate your bot on channel sharedness

Treat external sharedness as a hard boundary in your event handler. Here is the guard I put at the top of every Bolt listener that might emit internal data:

async function isExternallyShared(client, channelId) {
  const { channel } = await client.conversations.info({ channel: channelId });
  return Boolean(channel.is_ext_shared || channel.is_pending_ext_shared);
}

app.event('app_mention', async ({ event, client, say }) => {
  if (await isExternallyShared(client, event.channel)) {
    await say("I don't post internal deploy or alert details in shared channels.");
    return;
  }
  // ...normal internal behavior
});

Cache the result with a short TTL so you’re not calling conversations.info on every message and burning rate limits.

Pro Tip: Pending shared channels (is_pending_ext_shared) are the sneaky case — the invite is out but not yet accepted. Treat pending exactly like accepted, because the moment someone clicks the link, history may become visible.

Scope the bot, not just the channel

Channel-level guards are necessary but not sufficient. The deeper fix is OAuth scopes. A bot that only needs to post status updates does not need channels:history, files:read, or users:read.email. Every scope you grant is a scope an external member can indirectly benefit from if your bot misbehaves in a shared channel.

I keep a minimal manifest and let AI draft the scope list — then I delete half of what it suggests:

oauth_config:
  scopes:
    bot:
      - chat:write
      - commands
      - conversations.connect:read   # see shared-channel state

conversations.connect:read lets you inspect sharing status without granting broad history access. The model loves to add channels:read and groups:history “to be safe.” Safe for whom? Strip them.

Audit who is actually in the room

In a shared channel, membership drifts. The partner org adds a contractor, that contractor leaves their company, and Slack’s deprovisioning on their side is not your problem until it is. Periodically reconcile membership and flag external users:

async function externalMembers(client, channelId) {
  const out = [];
  for await (const page of client.paginate('conversations.members', { channel: channelId })) {
    for (const userId of page.members) {
      const { user } = await client.users.info({ user: userId });
      if (user.is_stranger || user.team_id !== process.env.HOME_TEAM_ID) {
        out.push({ id: userId, team: user.team_id });
      }
    }
  }
  return out;
}

is_stranger is Slack’s own flag for a user from a connected org you don’t share a workspace with. Pipe this into your alerting so a new external member triggers a review instead of going unnoticed.

Watch the org-level shared-channel events

The Events API emits shared_channel_invite_received, shared_channel_invite_accepted, and related events at the org level when you have the right Enterprise Grid scopes. Route these into the same place you collect audit-log security signals, so “a new external org just joined a channel” is an event a human sees, not a surprise during an incident.

For ops teams, the highest-value alert is simple: a bot that handles incident data was just added to an externally shared channel. That should page someone.

Where AI genuinely helps

The boring, error-prone glue is where the model earns its keep: writing the pagination loop, drafting the manifest, generating the membership-diff report, and turning Slack’s event schemas into typed handlers. Pair it with a real prompt library so you’re not re-explaining your conventions every time — I keep mine in the prompt workspace and lean on curated prompts for the scaffolding.

What AI must not do is decide your trust boundary. It does not know that the partner in T0THEIRTEAM is a competitor on a different product line, or that your compliance scope forbids customer PII crossing org lines. Never hand the model your real bot token to “test” against the live workspace — give it fixtures, review the diff, and deploy yourself. And always verify the request signature on any HTTP endpoint, shared channel or not; a friendly partner org is still an untrusted network path.

Conclusion

Slack Connect is a collaboration superpower and an exfiltration vector wearing the same friendly face. Gate bots on is_ext_shared, scope ruthlessly, reconcile external membership, and feed sharing events into your audit pipeline. Let AI write the plumbing fast, then put a human between that plumbing and any workspace it can speak into. Browse more in the Slack category or grab a ready-made starting point from the prompt packs.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.