A framework for building Slack apps, fast.
—
Slack Bolt provides advanced functionality for AI Assistant integration, workflow steps, custom functions, and conversation state management for complex Slack application scenarios.
Build AI-powered Slack assistants with thread context management and user interaction handling.
/**
* AI Assistant class for building intelligent Slack applications
* @param config - Assistant configuration with event handlers
*/
class Assistant {
constructor(config: AssistantConfig);
}
interface AssistantConfig {
/** Custom thread context store (defaults to in-memory store) */
threadContextStore?: AssistantThreadContextStore;
/** Handler(s) for when assistant threads are started */
threadStarted: AssistantThreadStartedMiddleware | AssistantThreadStartedMiddleware[];
/** Handler(s) for when thread context changes (optional) */
threadContextChanged?: AssistantThreadContextChangedMiddleware | AssistantThreadContextChangedMiddleware[];
/** Handler(s) for user messages in assistant threads */
userMessage: AssistantUserMessageMiddleware | AssistantUserMessageMiddleware[];
}
type AssistantThreadStartedMiddleware = (
args: AssistantUtilityArgs & AllMiddlewareArgs
) => Promise<void>;
type AssistantThreadContextChangedMiddleware = (
args: AssistantUtilityArgs & AllMiddlewareArgs & {
previousThreadContext?: AssistantThreadContext;
}
) => Promise<void>;
type AssistantUserMessageMiddleware = (
args: AssistantUtilityArgs & SlackEventMiddlewareArgs<'message'> & {
message: AssistantMessage;
}
) => Promise<void>;Usage Examples:
import { App, Assistant } from "@slack/bolt";
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET
});
// Configure assistant
const assistant = new Assistant({
threadStarted: async ({ say, setStatus, setSuggestedPrompts }) => {
await say("Hello! I'm your AI assistant. How can I help you today?");
await setStatus("Ready to assist");
await setSuggestedPrompts({
prompts: [
{ title: "Help me write code", message: "Can you help me write some code?" },
{ title: "Explain a concept", message: "Can you explain how webhooks work?" },
{ title: "Review my work", message: "Can you review this document?" }
],
title: "Popular requests"
});
},
userMessage: async ({
message,
say,
getThreadContext,
saveThreadContext,
setStatus
}) => {
await setStatus("Thinking...");
// Get conversation context
const context = await getThreadContext();
// Process user message with AI
const response = await processWithAI(message.text, context);
// Update context with new information
context.conversationHistory.push({
user: message.text,
assistant: response
});
// Save updated context
await saveThreadContext();
await say(response);
await setStatus("Ready");
}
});
// Register assistant with app
app.assistant(assistant);Manage conversation state and context across assistant interactions.
interface AssistantThreadContext {
/** Channel ID where the thread exists */
channelId: string;
/** Thread timestamp identifier */
threadTs: string;
/** Custom context data */
[key: string]: any;
}
interface AssistantThreadContextStore {
/** Retrieve context for a specific thread */
get(channelId: string, threadTs: string): Promise<AssistantThreadContext>;
/** Save context for a specific thread */
save(context: AssistantThreadContext): Promise<void>;
/** Remove context for a specific thread */
remove(channelId: string, threadTs: string): Promise<void>;
}
class DefaultThreadContextStore implements AssistantThreadContextStore {
constructor();
async get(channelId: string, threadTs: string): Promise<AssistantThreadContext>;
async save(context: AssistantThreadContext): Promise<void>;
async remove(channelId: string, threadTs: string): Promise<void>;
}Utility functions available to assistant middleware for managing thread state and interactions.
interface AssistantUtilityArgs {
/** Get current thread context */
getThreadContext: GetThreadContextUtilFn;
/** Save current thread context */
saveThreadContext: SaveThreadContextUtilFn;
/** Send message in current thread */
say: SayFn;
/** Set assistant status */
setStatus: SetStatusFn;
/** Set suggested prompts for the thread */
setSuggestedPrompts: SetSuggestedPromptsFn;
/** Set thread title */
setTitle: SetTitleFn;
}
type GetThreadContextUtilFn = () => Promise<AssistantThreadContext>;
type SaveThreadContextUtilFn = () => Promise<void>;
type SetStatusFn = (status: string) => Promise<AssistantThreadsSetStatusResponse>;
type SetSuggestedPromptsFn = (
params: SetSuggestedPromptsArguments
) => Promise<AssistantThreadsSetSuggestedPromptsResponse>;
type SetTitleFn = (title: string) => Promise<AssistantThreadsSetTitleResponse>;
interface SetSuggestedPromptsArguments {
/** Prompt suggestions that appear when opening assistant thread */
prompts: [AssistantPrompt, ...AssistantPrompt[]];
/** Title for the prompts */
title: string;
}
interface AssistantPrompt {
/** Display title for the prompt */
title: string;
/** Message text when prompt is selected */
message: string;
}Legacy workflow step functionality for workflow apps (deprecated in favor of custom functions).
/**
* @deprecated Steps from Apps are no longer supported and support will be removed in next major version
* Workflow step builder for creating custom workflow steps
*/
class WorkflowStep {
constructor(config: WorkflowStepConfig);
}
/**
* @deprecated Configuration for workflow steps
*/
interface WorkflowStepConfig {
/** Callback ID for the workflow step */
callback_id: string;
/** Handler for step edit action */
edit: WorkflowStepEditMiddleware | WorkflowStepEditMiddleware[];
/** Handler for step save action */
save: WorkflowStepSaveMiddleware | WorkflowStepSaveMiddleware[];
/** Handler for step execution */
execute: WorkflowStepExecuteMiddleware | WorkflowStepExecuteMiddleware[];
}
type WorkflowStepEditMiddleware = (
args: SlackActionMiddlewareArgs<WorkflowStepEdit> & {
step: WorkflowStepEdit;
configure: (config: StepConfigureArguments) => Promise<ViewsOpenResponse>;
}
) => Promise<void>;
type WorkflowStepSaveMiddleware = (
args: SlackViewMiddlewareArgs & {
step: WorkflowStepEdit;
update: (config: StepUpdateArguments) => Promise<WorkflowsUpdateStepResponse>;
}
) => Promise<void>;
type WorkflowStepExecuteMiddleware = (
args: SlackEventMiddlewareArgs<'workflow_step_execute'> & {
step: WorkflowStepExecuteEvent;
complete: (outputs?: StepCompletionArguments) => Promise<WorkflowsStepCompletedResponse>;
fail: (error: StepFailureArguments) => Promise<WorkflowsStepFailedResponse>;
}
) => Promise<void>;Create custom functions for Slack workflows with input validation and execution handling.
/**
* Custom function builder for Slack workflows
*/
class CustomFunction {
constructor(callbackId: string, config: CustomFunctionConfig);
}
interface CustomFunctionConfig {
/** Handler for function execution */
execute: CustomFunctionExecuteMiddleware | CustomFunctionExecuteMiddleware[];
}
type CustomFunctionExecuteMiddleware = (
args: SlackCustomFunctionMiddlewareArgs & {
inputs: FunctionInputs;
complete: FunctionCompleteFn;
fail: FunctionFailFn;
}
) => Promise<void>;
type FunctionCompleteFn = (outputs: { [key: string]: any }) => Promise<void>;
type FunctionFailFn = (error: string) => Promise<void>;
interface SlackCustomFunctionMiddlewareArgs extends AllMiddlewareArgs {
/** Function execution payload */
payload: {
type: 'function_executed';
function: {
callback_id: string;
type: 'app';
};
inputs: FunctionInputs;
function_execution_id: string;
workflow: {
id: string;
};
event: {
type: 'function_executed';
};
};
}Usage Examples:
import { App, CustomFunction } from "@slack/bolt";
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET
});
// Define custom function
const processDataFunction = new CustomFunction("process_data", {
execute: async ({ inputs, complete, fail, client }) => {
try {
const { data_source, format } = inputs;
// Validate inputs
if (!data_source) {
await fail("data_source is required");
return;
}
// Process the data
const result = await processData(data_source, format);
// Return outputs
await complete({
processed_data: result.data,
record_count: result.count,
status: "success"
});
} catch (error) {
await fail(`Processing failed: ${error.message}`);
}
}
});
// Register function with app
app.function("process_data", processDataFunction);Manage conversation state and context across multiple interactions.
/**
* Interface for storing conversation state
*/
interface ConversationStore {
/** Store conversation data */
set(conversationId: string, value: any, expiresAt?: number): Promise<void>;
/** Retrieve conversation data */
get<T = any>(conversationId: string): Promise<T | undefined>;
/** Remove conversation data */
delete(conversationId: string): Promise<void>;
}
/**
* In-memory implementation of ConversationStore
*/
class MemoryStore implements ConversationStore {
constructor();
async set(conversationId: string, value: any, expiresAt?: number): Promise<void>;
async get<T = any>(conversationId: string): Promise<T | undefined>;
async delete(conversationId: string): Promise<void>;
}
/**
* Middleware for adding conversation context to requests
* @param store - Conversation store instance
* @param options - Context options
*/
function conversationContext(
store: ConversationStore,
options?: ConversationContextOptions
): Middleware<AnyMiddlewareArgs>;
interface ConversationContextOptions {
/** Custom function to extract conversation ID */
getConversationId?: (args: AnyMiddlewareArgs) => string | undefined;
/** Context property name (defaults to 'conversation') */
contextKey?: string;
}Usage Examples:
import { App, MemoryStore, conversationContext } from "@slack/bolt";
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET
});
// Create conversation store
const convoStore = new MemoryStore();
// Add conversation context middleware
app.use(conversationContext(convoStore));
// Use conversation context in listeners
app.message("start survey", async ({ message, context, say }) => {
// Initialize conversation state
await context.conversation.set("survey_step", 1);
await context.conversation.set("responses", {});
await say("Let's start the survey! What's your name?");
});
app.message(async ({ message, context, say }) => {
const step = await context.conversation.get("survey_step");
const responses = await context.conversation.get("responses") || {};
if (step === 1) {
responses.name = message.text;
await context.conversation.set("survey_step", 2);
await context.conversation.set("responses", responses);
await say("Thanks! What's your favorite color?");
} else if (step === 2) {
responses.color = message.text;
await context.conversation.set("survey_step", 3);
await context.conversation.set("responses", responses);
await say(`Great! So your name is ${responses.name} and your favorite color is ${responses.color}. Survey complete!`);
// Clear conversation state
await context.conversation.delete("survey_step");
await context.conversation.delete("responses");
}
});Install with Tessl CLI
npx tessl i tessl/npm-slack--bolt