---
title: Build a 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) 

# Build a 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-4280)
* [  wrangler.toml ](#tab-panel-4281)

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-05-08",

  "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 date

compatibility_date = "2026-05-08"

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_ID

npx wrangler secret put SLACK_CLIENT_SECRET

npx wrangler secret put SLACK_SIGNING_SECRET

npx wrangler secret put OPENAI_API_KEY

npx 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/api-reference/mcp-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/api-reference/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":"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/guides/","name":"Guides"}},{"@type":"ListItem","position":4,"item":{"@id":"/agents/guides/slack-agent/","name":"Build a Slack Agent"}}]}
```
