---
title: Slack agent
description: Build and deploy an AI-powered Slack bot on Cloudflare Workers using the Agents SDK.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/agents/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Slack agent

## Deploy your first Slack Agent

This guide will show you how to build and deploy an AI-powered Slack bot on Cloudflare Workers that can:

* Respond to direct messages
* Reply when mentioned in channels
* Maintain conversation context in threads
* Use AI to generate intelligent responses

Your Slack Agent will be a multi-tenant application, meaning a single deployment can serve multiple Slack workspaces. Each workspace gets its own isolated agent instance with dedicated storage, powered by the [Agents SDK](https://developers.cloudflare.com/agents/).

You can view the full code for this example [here ↗](https://github.com/cloudflare/awesome-agents/tree/69963298b359ddd66331e8b3b378bb9ae666629f/agents/slack).

## Prerequisites

Before you begin, you will need:

* A [Cloudflare account ↗](https://dash.cloudflare.com/sign-up)
* [Node.js ↗](https://nodejs.org/) installed (v18 or later)
* A [Slack workspace ↗](https://slack.com/create) where you have permission to install apps
* An [OpenAI API key ↗](https://platform.openai.com/api-keys) (or another LLM provider)

## 1\. Create a Slack App

First, create a new Slack App that your agent will use to interact with Slack:

1. Go to [api.slack.com/apps ↗](https://api.slack.com/apps) and select **Create New App**.
2. Select **From scratch**.
3. Give your app a name (for example, "My AI Assistant") and select your workspace.
4. Select **Create App**.

### Configure OAuth & Permissions

In your Slack App settings, go to **OAuth & Permissions** and add the following **Bot Token Scopes**:

* `chat:write` — Send messages as the bot
* `chat:write.public` — Send messages to channels without joining
* `channels:history` — View messages in public channels
* `app_mentions:read` — Receive mentions
* `im:write` — Send direct messages
* `im:history` — View direct message history

### Enable Event Subscriptions

You will later configure the Event Subscriptions URL after deploying your agent. But for now, go to **Event Subscriptions** in your Slack App settings and prepare to enable it.

Subscribe to the following bot events:

* `app_mention` — When the bot is @mentioned
* `message.im` — Direct messages to the bot

Do not enable it yet. You will enable it after deployment.

### Get your Slack credentials

From your Slack App settings, collect these values:

1. **Basic Information** \> **App Credentials**:  
  * **Client ID**
  * **Client Secret**
  * **Signing Secret**

Keep these handy — you will need them in the next step.

## 2\. Create your Slack Agent project

1. Create a new project for your Slack Agent:

 npm  yarn  pnpm 

```
npm create cloudflare@latest -- my-slack-agent
```

```
yarn create cloudflare my-slack-agent
```

```
pnpm create cloudflare@latest my-slack-agent
```

1. Navigate into your project:

Terminal window

```
cd my-slack-agent
```

1. Install the required dependencies:

Terminal window

```
npm install agents openai
```

## 3\. Set up your environment variables

1. Create a `.env` file in your project root for local development secrets:

Terminal window

```
touch .env
```

1. Add your credentials to `.env`:

Terminal window

```
SLACK_CLIENT_ID="your-slack-client-id"SLACK_CLIENT_SECRET="your-slack-client-secret"SLACK_SIGNING_SECRET="your-slack-signing-secret"OPENAI_API_KEY="your-openai-api-key"OPENAI_BASE_URL="https://gateway.ai.cloudflare.com/v1/YOUR_ACCOUNT_ID/YOUR_GATEWAY/openai"
```

Note

The `OPENAI_BASE_URL` is optional but recommended. Using [Cloudflare AI Gateway](https://developers.cloudflare.com/ai-gateway/) gives you caching, rate limiting, and analytics for your AI requests.

1. Update your `wrangler.jsonc` to configure your Agent:

* [  wrangler.jsonc ](#tab-panel-5691)
* [  wrangler.toml ](#tab-panel-5692)

JSONC

```
{  "$schema": "./node_modules/wrangler/config-schema.json",  "name": "my-slack-agent",  "main": "src/index.ts",  // Set this to today's date  "compatibility_date": "2026-06-30",  "compatibility_flags": [    "nodejs_compat"  ],  "durable_objects": {    "bindings": [      {        "name": "MyAgent",        "class_name": "MyAgent",        "script_name": "my-slack-agent"      }    ]  },  "migrations": [    {      "tag": "v1",      "new_classes": [        "MyAgent"      ]    }  ]}
```

TOML

```
"$schema" = "./node_modules/wrangler/config-schema.json"name = "my-slack-agent"main = "src/index.ts"# Set this to today's datecompatibility_date = "2026-06-30"compatibility_flags = [ "nodejs_compat" ]
[[durable_objects.bindings]]name = "MyAgent"class_name = "MyAgent"script_name = "my-slack-agent"
[[migrations]]tag = "v1"new_classes = [ "MyAgent" ]
```

## 4\. Create your Slack Agent

1. First, create the base `SlackAgent` class at `src/slack.ts`. This class handles OAuth, request verification, and event routing. You can view the [full implementation on GitHub ↗](https://github.com/cloudflare/awesome-agents/blob/69963298b359ddd66331e8b3b378bb9ae666629f/agents/slack/src/slack.ts).
2. Now create your agent implementation at `src/index.ts`:

TypeScript

```
import { env } from "cloudflare:workers";import { SlackAgent } from "./slack";import { OpenAI } from "openai";
const openai = new OpenAI({  apiKey: env.OPENAI_API_KEY,  baseURL: env.OPENAI_BASE_URL,});
type SlackMsg = {  user?: string;  text?: string;  ts: string;  thread_ts?: string;  subtype?: string;  bot_id?: string;};
function normalizeForLLM(msgs: SlackMsg[], selfUserId: string) {  return msgs.map((m) => {    const role = m.user && m.user !== selfUserId ? "user" : "assistant";    const text = (m.text ?? "").replace(/<@([A-Z0-9]+)>/g, "@$1");    return { role, content: text };  });}
export class MyAgent extends SlackAgent {  async generateAIReply(conversation: SlackMsg[]) {    const selfId = await this.ensureAppUserId();    const messages = normalizeForLLM(conversation, selfId);
    const system = `You are a helpful AI assistant in Slack.Be brief, specific, and actionable. If you're unsure, ask a single clarifying question.`;
    const input = [{ role: "system", content: system }, ...messages];
    const response = await openai.chat.completions.create({      model: "gpt-4o-mini",      messages: input,    });
    const msg = response.choices[0].message.content;    if (!msg) throw new Error("No message from AI");
    return msg;  }
  async onSlackEvent(event: { type: string } & Record<string, unknown>) {    // Ignore bot messages and subtypes (edits, joins, etc.)    if (event.bot_id || event.subtype) return;
    // Handle direct messages    if (event.type === "message") {      const e = event as unknown as SlackMsg & { channel: string };      const isDM = (e.channel || "").startsWith("D");      const mentioned = (e.text || "").includes(        `<@${await this.ensureAppUserId()}>`,      );
      if (!isDM && !mentioned) return;
      const conversation = await this.fetchConversation(e.channel);      const content = await this.generateAIReply(conversation);      await this.sendMessage(content, { channel: e.channel });      return;    }
    // Handle @mentions in channels    if (event.type === "app_mention") {      const e = event as unknown as SlackMsg & {        channel: string;        text?: string;      };      const thread = await this.fetchThread(e.channel, e.thread_ts || e.ts);      const content = await this.generateAIReply(thread);      await this.sendMessage(content, {        channel: e.channel,        thread_ts: e.thread_ts || e.ts,      });      return;    }  }}
export default MyAgent.listen({  clientId: env.SLACK_CLIENT_ID,  clientSecret: env.SLACK_CLIENT_SECRET,  slackSigningSecret: env.SLACK_SIGNING_SECRET,  scopes: [    "chat:write",    "chat:write.public",    "channels:history",    "app_mentions:read",    "im:write",    "im:history",  ],});
```

## 5\. Test locally

Start your development server:

Terminal window

```
npm run dev
```

Your agent is now running at `http://localhost:8787`.

### Configure Slack Event Subscriptions

Now that your agent is running locally, you need to expose it to Slack. Use [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/trycloudflare/) to create a secure tunnel:

Terminal window

```
npx cloudflared tunnel --url http://localhost:8787
```

This will output a public URL like `https://random-subdomain.trycloudflare.com`.

Go back to your Slack App settings:

1. Go to **Event Subscriptions**.
2. Toggle **Enable Events** to **On**.
3. Enter your Request URL: `https://random-subdomain.trycloudflare.com/slack`.
4. Slack will send a verification request — if your agent is running correctly, it should show **Verified**.
5. Under **Subscribe to bot events**, add:

  * `app_mention`
  * `message.im`
6. Select **Save Changes**.

Note

Cloudflare Tunnel URLs are temporary. When testing locally, you will need to update the Request URL each time you restart the tunnel.

### Install your app to Slack

Visit `http://localhost:8787/install` in your browser. This will redirect you to Slack's authorization page. Select **Allow** to install the app to your workspace.

After authorization, you should see "Successfully registered!" in your browser.

### Test your agent

Open Slack. Then:

1. Send a DM to your bot — it should respond with an AI-generated message.
2. Mention your bot in a channel (e.g., `@My AI Assistant hello`) — it should reply in a thread.

If everything works, you're ready to deploy to production!

## 6\. Deploy to production

1. Before deploying, add your secrets to Cloudflare:

Terminal window

```
npx wrangler secret put SLACK_CLIENT_IDnpx wrangler secret put SLACK_CLIENT_SECRETnpx wrangler secret put SLACK_SIGNING_SECRETnpx wrangler secret put OPENAI_API_KEYnpx wrangler secret put OPENAI_BASE_URL
```

Note

You can skip `OPENAI_BASE_URL` if you're not using AI Gateway.

1. Deploy your agent:

Terminal window

```
npx wrangler deploy
```

After deploying, you will get a production URL like:

```
https://my-slack-agent.your-account.workers.dev
```

### Update Slack Event Subscriptions

Go back to your Slack App settings:

1. Go to **Event Subscriptions**.
2. Update the Request URL to your production URL: `https://my-slack-agent.your-account.workers.dev/slack`.
3. Select **Save Changes**.

### Distribute your app

Now that your agent is deployed, you can share it with others:

* **Single workspace**: Install it via `https://my-slack-agent.your-account.workers.dev/install`.
* **Public distribution**: Submit your app to the [Slack App Directory ↗](https://api.slack.com/start/distributing).

Each workspace that installs your app will get its own isolated agent instance with dedicated storage.

## How it works

### Multi-tenancy with Durable Objects

Your Slack Agent uses [Durable Objects](https://developers.cloudflare.com/durable-objects/) to provide isolated, stateful instances for each Slack workspace:

* Each workspace's `team_id` is used as the Durable Object ID.
* Each agent instance stores its own Slack access token in KV storage.
* Conversations are fetched on-demand from Slack's API.
* All agent logic runs in an isolated, consistent environment.

### OAuth flow

The agent handles Slack's OAuth 2.0 flow:

1. User visits `/install` \> redirected to Slack authorization.
2. User selects **Allow** \> Slack redirects to `/accept` with an authorization code.
3. Agent exchanges code for access token.
4. Agent stores token in the workspace's Durable Object.

### Event handling

When Slack sends an event:

1. Request arrives at `/slack` endpoint.
2. Agent verifies the request signature using HMAC-SHA256.
3. Agent routes the event to the correct workspace's Durable Object.
4. `onSlackEvent` method processes the event and generates a response.

## Customizing your agent

### Change the AI model

Update the model in `src/index.ts`:

TypeScript

```
const response = await openai.chat.completions.create({  model: "gpt-4o", // or any other model  messages: input,});
```

### Add conversation memory

Store conversation history in Durable Object storage:

TypeScript

```
async storeMessage(channel: string, message: SlackMsg) {  const history = await this.ctx.storage.kv.get(`history:${channel}`) || [];  history.push(message);  await this.ctx.storage.kv.put(`history:${channel}`, history);}
```

### React to specific keywords

Add custom logic in `onSlackEvent`:

TypeScript

```
async onSlackEvent(event: { type: string } & Record<string, unknown>) {  if (event.type === "message") {    const e = event as unknown as SlackMsg & { channel: string };
    if (e.text?.includes("help")) {      await this.sendMessage("Here's how I can help...", {        channel: e.channel      });      return;    }  }
  // ... rest of your event handling}
```

### Use different LLM providers

Replace OpenAI with [Workers AI](https://developers.cloudflare.com/workers-ai/):

TypeScript

```
import { Ai } from "@cloudflare/ai";
export class MyAgent extends SlackAgent {  async generateAIReply(conversation: SlackMsg[]) {    const ai = new Ai(this.ctx.env.AI);    const response = await ai.run("@cf/meta/llama-3-8b-instruct", {      messages: normalizeForLLM(conversation, await this.ensureAppUserId()),    });    return response.response;  }}
```

## Next steps

* Add [Slack Interactive Components ↗](https://api.slack.com/interactivity) (buttons, modals)
* Connect your Agent to an [MCP server](https://developers.cloudflare.com/agents/model-context-protocol/apis/client-api/)
* Add rate limiting to prevent abuse
* Implement conversation state management
* Use [Workers Analytics Engine](https://developers.cloudflare.com/analytics/analytics-engine/) to track usage
* Add [schedules](https://developers.cloudflare.com/agents/runtime/execution/schedule-tasks/) for scheduled tasks

## Related resources

[ Agents documentation ](https://developers.cloudflare.com/agents/) Complete Agents framework documentation. 

[ Durable Objects ](https://developers.cloudflare.com/durable-objects/) Learn about the underlying stateful infrastructure. 

[ Slack API ](https://api.slack.com/) Official Slack API documentation. 

[ OpenAI API ](https://platform.openai.com/docs/) Official OpenAI API documentation.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/agents/examples/slack-agent/#page","headline":"Slack agent · Cloudflare Agents docs","description":"Build and deploy an AI-powered Slack bot on Cloudflare Workers using the Agents SDK.","url":"https://developers.cloudflare.com/agents/examples/slack-agent/","inLanguage":"en","image":"https://developers.cloudflare.com/dev-products-preview.png","dateModified":"2026-06-03","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/agents/","name":"Agents"}},{"@type":"ListItem","position":3,"item":{"@id":"/agents/examples/","name":"Examples"}},{"@type":"ListItem","position":4,"item":{"@id":"/agents/examples/slack-agent/","name":"Slack agent"}}]}
```
