You can build an AI-powered Slack bot that responds to mentions, maintains conversation history, and calls tools autonomously, without storing a long-lived Slack bot token or signing secret in your environment. Chat SDK handles the platform integration (e.g., webhooks and message formatting) and AI SDK's ToolLoopAgent runs the reasoning loop that lets your agent call tools and act on results. Vercel Connect issues a user-authorized Slack token at runtime and forwards Slack events to your project, keeping credentials scoped to the environments that need them.
You'll scaffold the bot with the create-chat-sdk CLI, which generates the Connect wiring for you, then create a Slack connector, link it to your project, and add an agent with tools. Along the way, you'll enable streaming responses, tool calling, and more using Redis and the Vercel AI Gateway.
Vercel Connect is in beta and available on all plans. Features and behavior, including available connectors and trigger forwarding, may change before general availability. Usage is subject to the Beta Agreement and Vercel Connect terms.
Before you begin, make sure you have:
- Node.js 20+ and a package manager (e.g., pnpm)
- Access to a Vercel team and project with Vercel Connect enabled
- Vercel CLI installed (
npm i -g vercel) - A Slack workspace where you can install an app
Chat SDK is a unified TypeScript SDK for building chatbots across Slack, Teams, Discord, and other platforms. You register event handlers (like onNewMention and onSubscribedMessage), and the SDK routes incoming webhooks to them. The Slack adapter handles message parsing and Slack API interactions. The Redis state adapter tracks which threads your bot has subscribed to and manages distributed locking to handle concurrent messages.
The create-chat-sdk CLI scaffolds a minimal, webhook-only Next.js app:
- Your
Chatconfiguration insrc/lib/bot.ts - The dynamic webhook route at
/api/webhooks/[platform] .env.examplefile- The adapter dependencies
Passing --connect adds the Vercel Connect authentication pieces.
AI SDK's ToolLoopAgent wraps a language model with tools and runs an autonomous loop: the model generates text or calls a tool, the SDK executes the tool, feeds the result back, and repeats until the model finishes. When you pass a model string like "anthropic/claude-opus-4.8", and host your application on Vercel, the AI SDK will route the request through the AI Gateway automatically.
Chat SDK accepts any AsyncIterable<string> as a message, so you can pass the agent's fullStream directly to thread.post() for real-time streaming in Slack.
Vercel Connect provides Slack credentials at runtime, so you don't have to manage a static bot token. You register a Slack connector once, link it to your project, and the generated bot spreads the connectSlackAdapter helper from @vercel/connect/chat into the Slack adapter. Connect also forwards inbound Slack events to a trigger destination you register on the connector. For this agent, that destination is your project at /api/webhooks/slack.
The helper wires authentication in both directions:
- For outbound calls to the Slack API, it supplies a
botTokenresolver that fetches a fresh, short-lived Slack token per request via Connect'sgetToken. - For inbound events, Connect verifies them with Slack and forwards them to your app. The helper's
webhookVerifierthen confirms each forwarded event carries a valid Vercel OIDC token, replacing Slack's native signature and timestamp check.
Because OIDC verification replaces Slack's signed timestamp check, request freshness relies on the short-lived OIDC token's expiry, and there is no built-in delivery de-duplication. Keep your webhook handlers idempotent. Note that Connect trigger forwarding is HTTP-only, so it doesn't apply to the Slack adapter's Socket Mode.
The create-chat-sdk CLI generates the entire Chat SDK skeleton with a single command. Pass the Slack platform adapter, the Redis state adapter, and the --connect flag to authenticate with Vercel Connect:
If you use npm, add the -- separator so npm forwards the flags to the CLI instead of consuming them itself:
The CLI generates:
src/lib/bot.tswith theChatconfiguration. Because you passed--connect, it spreads theconnectSlackAdapterhelper from@vercel/connect/chatinto the Slack adapter factory, and@vercel/connectis added to your dependencies.src/app/api/webhooks/[platform]/route.ts, the dynamic webhook route that becomes yourPOST /api/webhooks/slackendpoint..env.example, which lists aSLACK_CONNECTORvariable for your connector UID in place ofSLACK_BOT_TOKENandSLACK_SIGNING_SECRET.- Starter handlers for mentions and subscribed thread replies, which you'll extend with an agent in step five.
The scaffolded app is webhook-only, with no pages or client UI. You'll add the AI SDK and Zod yourself, since the agent loop is your code rather than part of the scaffold:
The ai package is the AI SDK, and includes the AI Gateway provider. zod is used to define tool input schemas.
The Vercel Plugin turns your AI coding agent (e.g., OpenAI Codex, Claude Code, or Cursor) into a Vercel expert. It adds skills, slash commands, and current knowledge of the tools this template uses, including Vercel Connect, Chat SDK, and AI SDK.
The plugin is optional; it isn't required to build your Slackbot or to follow this guide. Note that create-chat-sdk detects when it's run by a coding agent and runs non-interactively, so agents should pass adapters with --adapter as shown above.
Vercel Connect creates and manages the Slack app for you. You don't register an app at api.slack.com, write a manifest, or copy any credentials. You create a connector in the Vercel dashboard, set its scopes and events there, and install it in your workspace.
Open the Connect page on your team's dashboard, then click Create Connector to start the Add Connection flow. Once you've done that:
- Choose Slack as the provider.
- Select your Slack workspace and name the app (e.g.,
acme-slack). If you haven't connected a Slack workspace yet, connect and authorize one first, then return to the Connect page. Keep Triggers enabled so Slack events reach your project. - Open Advanced and set:
- Bot Scopes the agent needs:
chat:write,channels:history,channels:read,groups:history,im:history,mpim:history,reactions:write, andusers:read. - Trigger Event Types to forward:
app_mention, plus the message events for the surfaces your agent supports (message.channels,message.groups,message.im,message.mpim), so both new mentions and follow-up replies work.
- Bot Scopes the agent needs:
- Click Create Slack Connector, then Install to your Slack Workspace.
- In the connector's settings, link it to your project, select the environments it applies to (e.g., production), and register your project as a trigger destination with the path
/api/webhooks/slack, the route the CLI generated in step one.
You can also do this from the CLI. Create the connector with trigger forwarding enabled, then attach your project and register the trigger destination:
Because Connect manages the Slack app, Slack delivers events to Connect's intake URL (shown in the connector settings), not directly to your deployment's /api/webhooks/slack. Connect verifies Slack, then forwards each event to the trigger destination you registered.
Copy the generated .env.example file and add your connector UID:
Replace slack/acme-slack with your connector UID from the Connect dashboard or vercel connect list. Add the same variable to your Vercel project's environment variables so deployments can resolve the connector.
Your agent uses Redis for thread subscriptions and distributed locking. Provision Upstash Redis and connect it to your project with the Vercel CLI:
Other providers are also supported in the Vercel Marketplace, including Redis.
vercel integration add installs the Upstash integration if it isn't already, provisions a database, connects it to your project, and pulls its connection environment variables into .env.local. Follow the prompts to pick the Redis product and a plan.
To use the dashboard instead, open the Storage page for your project, click Create Database, and follow the flow to add Upstash Redis. Then sync the variables locally:
vercel env pull also adds a VERCEL_OIDC_TOKEN, which AI SDK uses to authenticate requests to the AI Gateway, so there's no API key to generate or store. @vercel/connect reads the same token automatically, both locally and on deployments.
The OIDC token expires after 12 hours, so re-run vercel env pull to refresh it, or start the dev server with vercel dev to refresh it automatically. Linking the project also lets Vercel Connect resolve the connector when the adapter requests a token.
You should see REDIS_URL (from Upstash), VERCEL_OIDC_TOKEN (for AI Gateway and @vercel/connect), and the SLACK_CONNECTOR value you set earlier. You should not add SLACK_BOT_TOKEN or SLACK_SIGNING_SECRET.
Create src/lib/tools.ts with the tools your agent can call. This example defines a weather tool and a docs tool, but you can add any tools your use case requires:
Each tool has a description (which tells the model when to use it), an inputSchema (a Zod schema that the model fills in), and an execute function that runs when the tool is called. If a tool calls another provider that Vercel Connect supports (e.g., GitHub), it can request a scoped token for itself.
The CLI already generated src/lib/bot.ts with the Connect wiring and starter handlers. Update it to create a ToolLoopAgent and stream its responses from the mention and thread handlers:
The connectSlackAdapter spread is the part the CLI generated for you.
It returns two fields:
- A
botTokenresolver in function form. The adapter calls it on each Slack API request, and it resolves a fresh, short-lived token via Connect'sgetToken. The token's subject is pinned to{ type: "app" }, so the agent acts as the application itself, and the bot scopes you set on the connector determine what the token can do. - A
webhookVerifierthat validates the Vercel OIDC token Connect attaches to each trigger-forwarded event. The default verifier matches the deployment's project and environment automatically (projectIddefaults toVERCEL_PROJECT_ID, andenvironmenttoVERCEL_TARGET_ENV, thenVERCEL_ENV), so production, preview, and development each accept only their own tokens. Verification fails closed: if those values are absent, every request is rejected, and the issuer is pinned tohttps://oidc.vercel.com.
Omit signingSecret and don't set SLACK_SIGNING_SECRET. The OIDC webhookVerifier is the freshness boundary, and the adapter skips Slack's 5-minute timestamp check when it's set. Leaving SLACK_SIGNING_SECRET unset avoids mixing direct-Slack and Connect modes.
The helper also accepts optional getToken parameters as a second argument (everything except subject), such as scopes or validityBufferMs, if you need to narrow or tune token requests.
When someone tags the bot, onNewMention fires. The handler subscribes to the thread (to track future messages in that thread) and streams the agent's response.
For follow-up messages, onSubscribedMessage retrieves the full thread history using thread.allMessages, converts it to the AI SDK message format with toAiMessages, and passes it to the agent so it has complete conversation context.
Using fullStream is preferred over textStream because it preserves paragraph breaks between tool-calling steps. Chat SDK auto-detects the stream type and handles Slack's native streaming API for real-time updates.
The CLI also generated the webhook route at src/app/api/webhooks/[platform]/route.ts, which exposes POST /api/webhooks/slack. It routes each request to the matching adapter's handler and uses waitUntil so your event handlers finish processing after the HTTP response is sent, which is required on serverless platforms where the function would otherwise terminate early. You don't need to modify it. With trigger forwarding enabled, Connect POSTs verified Slack payloads to this route, and the helper's webhookVerifier runs before the adapter parses the body.
The default verifier accepts only the deployment's own environment. If you forward events to more than one environment, build a verifier with createConnectWebhookVerifier and override the field:
Avoid hardcoding environment: "production" unless you only forward to production, since that would reject preview and development deployments.
Slack sends events to Connect, which forwards them to a deployed Vercel project rather than to your machine. You test the full round trip against a preview or development deployment, with no local tunnel to spin up. Your app rejects direct Slack POSTs unless you add a separate direct-webhook path with SLACK_SIGNING_SECRET.
- Deploy a preview build to receive the Slack events:
- In the connector's settings, make sure that deployment's environment is linked and registered as the trigger destination at
/api/webhooks/slack. The default verifier only accepts tokens for the deployment's own environment, so the connector must forward to the environment you're testing. - Invite the bot to a channel (
/invite @AI Agent). - Tag the bot and ask it, "What's the weather in San Francisco?". You should see a streaming response appear in the thread.
Once you've tested your agent, deploy it to production:
Your Slack AI agent is now live and will respond to mentions in your workspace.
Check that your Slack connector has trigger forwarding enabled and that your project is registered as a trigger destination with the correct path (/api/webhooks/slack). Confirm the connector is installed in your workspace and that its Trigger Event Types include the events your agent needs (app_mention and the relevant message events). You can review all of this in the connector's settings. Verify production/preview deployment logs on /api/webhooks/slack for 401s before debugging token issues.
Make sure the project is linked (vercel link) and that the connector is linked to it for the current environment. Confirm SLACK_CONNECTOR is set to the correct connector UID in the environment, the connector is installed in your workspace, and the bot scopes the agent uses are enabled on the connector. You can check the connector's link, environments, and scopes in its settings. For local development, run vercel env pull to refresh VERCEL_OIDC_TOKEN, since it expires after 12 hours.
- Confirm
connectSlackAdapteris spread intocreateSlackAdapter, so the helper'swebhookVerifieris active. - Confirm OIDC Federation is enabled on the project. The verifier fails closed, so if
VERCEL_PROJECT_IDor the other required environment variables are missing, then every request is rejected. - Remove
SLACK_SIGNING_SECRETfrom the project if set (it can force the wrong verification path in some setups). - Confirm the request is coming from Connect (trigger destination configured), not from Slack hitting your app directly.
- If you forward to multiple environments, the default verifier rejects tokens from environments other than the deployment's own. Override it with
createConnectWebhookVerifier({ environment: [...] })as shown above.
- The event reached your app without Connect forwarding (wrong Events URL on the Slack side, or trigger destination not registered).
- Fix the trigger path to
/api/webhooks/slackand link the correct environment.
OIDC verification replaces Slack's native signature and timestamp check, and Connect has no built-in delivery de-duplication. If a forwarded event is delivered more than once, your handlers run multiple times. Keep webhook handlers idempotent, for example by tracking processed event IDs in Redis.
Chat SDK uses Slack's native streaming API for smooth updates. If you're seeing issues, check that your Redis connection is stable, as the SDK uses distributed locks to manage concurrent messages.
If the agent calls a tool but no result appears, check for errors in your tool's execute function. AI SDK surfaces tool execution errors back to the model, which may attempt to recover. Add error handling in your tools and check your server logs for details.
For long-running threads, the conversation history can exceed the model's context window. Consider limiting the number of messages you pass to the agent by slicing the history array or by using a summarization step for older messages.