@vercel/slack-bolt
v1.4.0
Published
A Vercel receiver for building Slack apps with Bolt and deploying them to Vercel
Readme
@vercel/slack-bolt
A custom Slack Bolt receiver built for Vercel's Fluid Compute.
Getting Started
Visit our template to get started building a Slack app.
Installation
npm install @vercel/slack-bolt
# or
yarn add @vercel/slack-bolt
# or
pnpm add @vercel/slack-bolt
# or
bun add @vercel/slack-boltAPI Reference
VercelReceiver
Responsible for handling and parsing any incoming requests from Slack and then forwarding them to your Bolt app for event processing.
This example shows a single-tenant app with a fixed bot token. For multi-tenant apps that need OAuth, see OAuth below.
import { App } from "@slack/bolt";
import { VercelReceiver } from "@vercel/slack-bolt";
const receiver = new VercelReceiver();
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
receiver,
deferInitialization: true,
});
app.message(/^(hi|hello|hey).*/, async ({ say }) => {
await say("Hello, world!");
});
export { app, receiver };Parameters
| Name | Type | Default Value | Required | Description |
| --------------------------- | --------------------------------- | ------------------------------------ | -------------- | ---------------------------------------------------------------------- |
| signingSecret | string | process.env.SLACK_SIGNING_SECRET | No1 | Signing secret for your Slack app used to verify requests. |
| signatureVerification | boolean | true | No | Enable or disable request signature verification. |
| logger | Logger2 | new ConsoleLogger() | No | Logger used for diagnostics. |
| logLevel | LogLevel2 | LogLevel.INFO | No | Minimum log level for the logger. |
| customPropertiesExtractor | (req: Request) => StringIndexed | undefined | No | Return value is merged into Bolt event customProperties2. |
| ackTimeoutMs | number | 3001 | No | Milliseconds to wait for ack() before returning a timeout error. |
| clientId | string | process.env.SLACK_CLIENT_ID | No3 | Your app's client ID (required for OAuth). |
| clientSecret | string | process.env.SLACK_CLIENT_SECRET | No3 | Your app's client secret (required for OAuth). |
| stateSecret | string | process.env.SLACK_STATE_SECRET | No3 | Secret for OAuth CSRF state parameter. |
| scopes | string[] | undefined | No | Bot scopes to request during the OAuth flow. |
| redirectUri | string | undefined | No | Redirect URI registered with your Slack app. |
| installationStore | InstallationStore2 | undefined | No4 | Persistent storage backend for OAuth installations. |
| installerOptions | VercelInstallerOptions | {} | No | Advanced OAuth options (user scopes, direct install, state store, etc). |
1 Optional if process.env.SLACK_SIGNING_SECRET is provided.
2 Provided by the @slack/bolt library. More information here.
3 Required for OAuth. Read from process.env automatically if not provided.
4 Required for OAuth in serverless environments -- the default in-memory store does not persist across cold starts.
createHandler
A function that returns a Vercel-compatible request handler that will initialize and start your Bolt app to process the event.
// An example using Next.js route handlers
import { createHandler } from "@vercel/slack-bolt";
import { app, receiver } from "./app";
export const POST = createHandler(app, receiver);Parameters
| Name | Type | Required | Description |
| ---------- | ----------------- | -------- | ------------------------------------------------------------ |
| app | App1 | Yes | Your Bolt app |
| receiver | VercelReceiver | Yes | The Vercel receiver instance used to process Slack requests. |
1 Provided by the @slack/bolt library. More information here.
OAuth (Multi-Tenant Apps)
VercelReceiver has built-in support for Slack's OAuth v2 flow, allowing your app to be installed by multiple workspaces. For background on how OAuth works with Bolt, see the Bolt OAuth guide.
Environment variables
Set these in your Vercel project (or .env.local for local development):
| Variable | Description |
| --------------------- | -------------------------------------------------------- |
| SLACK_CLIENT_ID | App client ID, found under Basic Information on api.slack.com. |
| SLACK_CLIENT_SECRET | App client secret, found in the same section. |
| SLACK_STATE_SECRET | A random string used to sign the OAuth state parameter. |
| SLACK_SIGNING_SECRET| Signing secret for verifying incoming Slack requests. |
All four are read from process.env automatically.
Receiver setup
When using OAuth, the receiver manages token retrieval via the installationStore -- you no longer pass token or signingSecret to the App constructor.
import { App } from "@slack/bolt";
import { VercelReceiver } from "@vercel/slack-bolt";
const receiver = new VercelReceiver({
scopes: ["chat:write", "commands"],
installationStore: myInstallationStore,
installerOptions: {
directInstall: true,
},
});
const app = new App({
receiver,
deferInitialization: true,
});Route handlers
Expose two GET routes for the OAuth flow. The paths can be anything -- just make sure the callback path matches the Redirect URL in your Slack app settings.
// app/api/slack/install/route.ts
import { receiver } from "@/bolt/app";
export const GET = receiver.handleInstall;// app/api/slack/oauth-redirect/route.ts
import { receiver } from "@/bolt/app";
export const GET = receiver.handleCallback;Installation store
An installationStore is required in serverless environments. The default in-memory store does not survive cold starts, so each new function invocation would lose all installation data.
Your store must implement storeInstallation, fetchInstallation, and optionally deleteInstallation. Any persistent backend works (Redis, PostgreSQL, DynamoDB, etc). See the Bolt InstallationStore docs for the full interface, and the Next.js example for a working Redis implementation.
preview
Programmatic API for creating and configuring a Slack app for a Vercel preview deployment. This is the function that powers the vercel-slack build CLI. Imported from @vercel/slack-bolt/preview.
import { preview } from "@vercel/slack-bolt/preview";
const result = await preview({
branch: "feat/my-feature",
projectId: "prj_...",
deploymentUrl: "my-app-abc123.vercel.app",
manifestPath: "manifest.json",
slackConfigurationToken: process.env.SLACK_CONFIGURATION_TOKEN,
vercelApiToken: process.env.VERCEL_API_TOKEN,
});Parameters (PreviewParams)
| Name | Type | Required | Description |
| ------------------------- | -------- | -------- | ------------------------------------------------------------------ |
| branch | string | Yes | Git branch name for the preview deployment. |
| projectId | string | Yes | Vercel project ID. |
| deploymentUrl | string | Yes | URL of the current deployment. |
| manifestPath | string | Yes | Path to the Slack app manifest.json file. |
| slackConfigurationToken | string | Yes | Slack app configuration token. |
| vercelApiToken | string | Yes | Vercel API token with write access. |
| branchUrl | string | No | Branch-specific URL. Defaults to deploymentUrl. |
| slackAppId | string | No | Existing Slack app ID to update instead of creating a new one. |
| slackServiceToken | string | No | Service token for auto-installing the app. |
| teamId | string | No | Vercel team ID. |
| deploymentId | string | No | Current deployment ID (used for cancel-and-redeploy). |
| automationBypassSecret | string | No | Existing bypass secret. Generated automatically if not provided. |
| commitSha | string | No | Git commit SHA (displayed in the Slack app description). |
| commitMessage | string | No | Git commit message (displayed in the Slack app description). |
| commitAuthor | string | No | Git commit author (displayed in the Slack app description). |
Return value (PreviewResult)
| Name | Type | Description |
| --------------- | --------- | ------------------------------------------------------------- |
| isNew | boolean | true if a new Slack app was created, false if updated. |
| installStatus | string | Installation outcome (e.g. installed, missing_service_token). |
| app | object | Slack API response containing app_id and credentials. |
Preview Deployments
@vercel/slack-bolt can automatically create and manage a dedicated Slack app for each preview branch. On every preview build it will:
- Create a new Slack app (or update the existing one) from your
manifest.json - Rewrite manifest URLs to point to the preview deployment
- Store the app credentials (
SLACK_APP_ID,SLACK_CLIENT_ID,SLACK_CLIENT_SECRET,SLACK_SIGNING_SECRET) as branch-scoped environment variables - Optionally auto-install the app and persist
SLACK_BOT_TOKEN - Cancel and redeploy so the new environment variables take effect
On production and local/development builds, the preview step is skipped automatically.
Setup
1. Add the CLI to your build script
{
"scripts": {
"build": "vercel-slack build --cleanup && next build"
}
}2. Create a manifest.json
Place a Slack app manifest in your project root. URLs can use any placeholder domain — they will be rewritten to your preview deployment URL:
Note: The manifest doesn't have to live in the project root. Set the
MANIFEST_PATHenvironment variable or pass--manifest-path <path>to specify a custom location relative to the working directory.
{
"display_information": {
"name": "My Slack App"
},
"settings": {
"event_subscriptions": {
"request_url": "https://example.com/api/slack/events"
}
}
}3. Configure environment variables
Add the following to your Vercel project:
| Variable | Required | Description |
| ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| SLACK_CONFIGURATION_TOKEN | Yes | App configuration token. Generate at https://api.slack.com/apps. Expires after 12 hours. |
| SLACK_CONFIG_REFRESH_TOKEN | No | Refresh token for automatic rotation of expired configuration tokens. Provided alongside the configuration token. Strongly recommended. |
| VERCEL_API_TOKEN | Yes | Vercel API token with write access. Create at https://vercel.com/account/settings/tokens |
| SLACK_SERVICE_TOKEN | No | Service token for auto-installing the app. Without this, the app must be installed manually. See https://docs.slack.dev/authentication/tokens/#service |
You must also enable Automatically expose System Environment Variables in your Vercel project settings.
How it works
git push → Vercel preview build
└─ vercel-slack build --cleanup
├─ Skips if production, development, or local
├─ Validates Slack and Vercel tokens
│ └─ If expired and SLACK_CONFIG_REFRESH_TOKEN is set → auto-rotates
│ and persists new tokens to Vercel env vars
├─ Cleans up orphaned preview apps (--cleanup)
├─ Reads manifest.json
├─ Creates or updates Slack app via apps.manifest API
├─ Rewrites manifest URLs → preview deployment URL
├─ Stores credentials as branch-scoped env vars
├─ Auto-installs app (if SLACK_SERVICE_TOKEN is set)
└─ Redeploys to pick up new env vars (only needed on first deploy for branch)Cleanup
When --cleanup is passed, the CLI removes Slack apps and branch-scoped environment variables for preview branches that are no longer active in the Vercel project. This runs before the main provisioning step and failures are non-fatal.
CLI Reference
Usage: vercel-slack <command> [options]
Commands:
build Build and configure the Slack app for a Vercel preview deployment
Options:
--cleanup Clean up Slack apps and env vars for inactive preview branches
--help, -h Show help
--version, -v Show versionAll Vercel and Slack environment variables are read automatically. You can override any of them via CLI flags (e.g. --vercel-env, --slack-app-id).
Examples
Starter templates: Next.js, Hono, Nitro.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Support
For issues and questions:
- Check the Slack Bolt documentation
- Review Vercel Functions documentation
- Open an issue in this repository
