runtime.socket-mode-ts
purpose
Socket Mode setup, connection lifecycle, and production patterns for Slack Bolt TypeScript apps using @slack/socket-mode.
rules
- Enable Socket Mode by setting
socketMode: trueand providingappTokenin theAppconstructor. The app token must be an app-level token (prefixxapp-) with theconnections:writescope, not a bot token. api.slack.com/apis/connections/socket - Socket Mode uses WebSocket connections instead of HTTP endpoints. No public URL, no
signingSecret, and no request signature verification are needed. This makes it ideal for local development and firewall-restricted environments. slack.dev/bolt-js/concepts/socket-mode - Install
@slack/socket-modeas a dependency alongside@slack/bolt. Bolt'sSocketModeReceiverwraps theSocketModeClientfrom this package. github.com/slackapi/node-slack-sdk - Call
await app.start()to open the WebSocket connection. Unlike HTTP mode,start()returns anAppsConnectionsOpenResponseobject, not an HTTP server. slack.dev/bolt-js/concepts/socket-mode - Auto-reconnect is enabled by default. The
SocketModeClienthandles connection drops, ping/pong heartbeats, and reconnection automatically. Override withautoReconnectEnabled: falseonly for testing. github.com/slackapi/node-slack-sdk/tree/main/packages/socket-mode - To add OAuth install routes or custom HTTP endpoints alongside Socket Mode, pass
customRoutesto theSocketModeReceiver. This spins up an HTTP server on port 3000 (default) in addition to the WebSocket connection. slack.dev/bolt-js/concepts/custom-routes - For multi-workspace apps using Socket Mode with OAuth, provide
clientId,clientSecret,stateSecret, andinstallationStorein the receiver options. The receiver creates an HTTP server for OAuth flows while using WebSocket for events. slack.dev/bolt-js/concepts/authenticating-oauth - Use
processEventErrorHandleron the receiver to control retry behavior. Returntrueto acknowledge the event (stops Slack retries). Returnfalseto let Slack retry.AuthorizationErrorreturnstrueby default (retrying won't fix bad tokens). bolt-js source: SocketModeReceiver.ts - Access the underlying
SocketModeClientviareceiver.clientto listen for low-level events likeconnected,connecting,disconnected, andunable_to_socket_mode_start. github.com/slackapi/node-slack-sdk/tree/main/packages/socket-mode - Generate the app-level token in the Slack app dashboard under Settings → Basic Information → App-Level Tokens. Create a token with the
connections:writescope. Store it asSLACK_APP_TOKENin your environment. api.slack.com/apis/connections/socket#token - Socket Mode supports all Bolt listener types —
app.message(),app.command(),app.action(),app.shortcut(),app.view(),app.event(), andapp.options()— with no code changes versus HTTP mode. The transport is transparent to handlers. slack.dev/bolt-js/concepts/socket-mode - Do not set
signingSecretwhen using Socket Mode withsocketMode: true. Bolt will throw if both are set with conflicting receiver configurations. If you need to switch between Socket Mode (dev) and HTTP (prod), use environment variables to toggle theAppconstructor options. bolt-js source: App.ts
patterns
Minimal Socket Mode setup
import { App } from "@slack/bolt";
const app = new App({
token: process.env.SLACK_BOT_TOKEN!,
appToken: process.env.SLACK_APP_TOKEN!,
socketMode: true,
});
app.message("hello", async ({ message, say }) => {
await say(`Hey there <@${message.user}>!`);
});
(async () => {
await app.start();
console.log("⚡️ Bolt app is running in Socket Mode");
})();Environment-based transport switching (dev vs prod)
import { App } from "@slack/bolt";
const useSocketMode = process.env.SOCKET_MODE === "true";
const app = new App({
token: process.env.SLACK_BOT_TOKEN!,
// Socket Mode options — only when enabled
...(useSocketMode && {
socketMode: true,
appToken: process.env.SLACK_APP_TOKEN!,
}),
// HTTP options — only when Socket Mode is disabled
...(!useSocketMode && {
signingSecret: process.env.SLACK_SIGNING_SECRET!,
}),
});
(async () => {
const port = useSocketMode ? undefined : Number(process.env.PORT || 3000);
await app.start(port!);
console.log(
`⚡️ Bolt app running in ${useSocketMode ? "Socket" : "HTTP"} mode`
);
})();Socket Mode with OAuth and custom routes
import { App } from "@slack/bolt";
import { FileInstallationStore } from "@slack/oauth";
const app = new App({
socketMode: true,
appToken: process.env.SLACK_APP_TOKEN!,
clientId: process.env.SLACK_CLIENT_ID!,
clientSecret: process.env.SLACK_CLIENT_SECRET!,
stateSecret: process.env.SLACK_STATE_SECRET!,
installationStore: new FileInstallationStore(),
scopes: ["chat:write", "commands", "app_mentions:read"],
customRoutes: [
{
path: "/health",
method: "GET",
handler: (_req, res) => {
res.writeHead(200);
res.end("OK");
},
},
],
});
(async () => {
await app.start(3000);
// WebSocket for events + HTTP on :3000 for OAuth and /health
console.log("⚡️ App running: Socket Mode + OAuth on port 3000");
})();Monitoring connection state
import { App, SocketModeReceiver } from "@slack/bolt";
const receiver = new SocketModeReceiver({
appToken: process.env.SLACK_APP_TOKEN!,
clientPingTimeout: 30_000,
serverPingTimeout: 30_000,
pingPongLoggingEnabled: true,
});
const app = new App({
token: process.env.SLACK_BOT_TOKEN!,
receiver,
});
// Access the underlying SocketModeClient for lifecycle events
receiver.client.on("connected", () => {
console.log("Socket Mode connected");
});
receiver.client.on("disconnected", () => {
console.warn("Socket Mode disconnected — auto-reconnect will retry");
});
receiver.client.on("unable_to_socket_mode_start", (error) => {
console.error("Socket Mode failed to start:", error);
});
(async () => {
await app.start();
})();pitfalls
- Using
signingSecretwith Socket Mode: Setting bothsocketMode: trueandsigningSecretcreates conflicting receiver configurations. Socket Mode does not use HTTP request verification. RemovesigningSecretwhen using Socket Mode. - Wrong token type for
appToken: TheappTokenmust be an app-level token (xapp-prefix) withconnections:writescope, not a bot token (xoxb-) or user token (xoxp-). Using the wrong token gives a cryptic connection error. - Assuming HTTP endpoints exist: In pure Socket Mode (no
customRoutes, no OAuth), there is no HTTP server. Health check endpoints, webhook receivers, and OAuth callback URLs will not work unless you explicitly configurecustomRoutesor OAuth options. - Port conflicts with OAuth: When Socket Mode is used with OAuth, the receiver starts an HTTP server on port 3000 by default. If another service uses that port, pass a different port to
app.start(port). - Missing
@slack/socket-modedependency:@slack/boltdoes not bundle@slack/socket-mode. You must install it separately:npm install @slack/socket-mode. Bolt will throw at startup if the package is missing.
references
- https://api.slack.com/apis/connections/socket
- https://slack.dev/bolt-js/concepts/socket-mode
- https://slack.dev/bolt-js/concepts/custom-routes
- https://github.com/slackapi/bolt-js/blob/main/src/receivers/SocketModeReceiver.ts
- https://github.com/slackapi/node-slack-sdk/tree/main/packages/socket-mode
instructions
This expert covers Socket Mode transport for Slack Bolt TypeScript apps. Use it when: setting up local development without a public URL; configuring socketMode: true and appToken; switching between Socket Mode (dev) and HTTP (prod); adding OAuth or custom HTTP routes alongside Socket Mode; monitoring WebSocket connection lifecycle events; or troubleshooting Socket Mode connection issues.
Pair with: runtime.bolt-foundations-ts.md for App constructor basics. bolt-oauth-distribution-ts.md for multi-workspace OAuth configuration alongside Socket Mode.
research
Deep Research prompt:
"Write a micro expert on Slack Bolt Socket Mode in TypeScript. Cover SocketModeReceiver configuration (appToken, socketMode flag, auto-reconnect, ping/pong), app-level token generation (connections:write scope, xapp- prefix), connection lifecycle events (connected, disconnected, unable_to_socket_mode_start), environment-based transport switching (Socket Mode for dev vs HTTP for prod), combining Socket Mode with OAuth install flows and custom HTTP routes, processEventErrorHandler for retry control, and common pitfalls (signingSecret conflicts, missing @slack/socket-mode package, wrong token type). Source from @slack/bolt SocketModeReceiver.ts, @slack/socket-mode SocketModeClient, and Slack API docs."