Skip to main content
slack-expert Expert-level Slack platform development using Bolt SDK — build Slack apps with slash commands, interactive components (buttons, modals, shortcuts), home tabs, event subscriptions, workflows, Socket Mode, and Block Kit. Use for building Slack bots, workflow automation, and rich interactive Slack experiences.
Jump to install Skills Marketplace Discover and explore AI skills built by the community.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Copy promptShow prompt details A direct command skips the review prompt. Inspect the source before running it.
npx skills add https://github.com/oimiragieo/agent-studio --skill slack-expertThe command stays on one line. Scroll horizontally to inspect it before copying.
Prefer a local copy? Download the files currently available to SkillsMP.
Download Zip Downloading... More from this repository Related occupations SOC
Based on SOC occupation classification
name slack-expert description Expert-level Slack platform development using Bolt SDK — build Slack apps with slash commands, interactive components (buttons, modals, shortcuts), home tabs, event subscriptions, workflows, Socket Mode, and Block Kit. Use for building Slack bots, workflow automation, and rich interactive Slack experiences. version 1.0.0 model sonnet invoked_by both user_invocable true tools ["Bash","Read","Write","Edit","WebFetch"] best_practices ["Always use Bolt SDK (not raw HTTP) for new Slack apps","Prefer Socket Mode for internal tools; HTTP for public apps","Validate request signatures before processing payloads","Use lazy listeners for long-running operations","Store OAuth tokens in a secure database, never env-vars for multi-workspace apps"] error_handling graceful streaming supported verified false lastVerifiedAt "2026-03-14T00:00:00.000Z" source builtin trust_score 100 provenance_sha 5d2e560139ce9700
Slack Expert Skill
Overview
Full-platform Slack development using the Bolt SDK. Covers interactive app development beyond basic messaging — slash commands, modals, shortcuts, home tabs, event subscriptions, workflow steps, and multi-workspace OAuth.
Related skill: slack-notifications — use that skill for simple one-way messaging/alerts. Use slack-expert when building interactive Slack apps or bots.
Requirements
Variable Purpose Required SLACK_BOT_TOKENBot OAuth token (xoxb-...) Yes (single workspace) SLACK_SIGNING_SECRETRequest signature verification Yes SLACK_APP_TOKENSocket Mode app-level token (xapp-...) Socket Mode only SLACK_CLIENT_IDOAuth client ID Multi-workspace only SLACK_CLIENT_SECRETOAuth client secret Multi-workspace only SLACK_STATE_SECRETOAuth state token Multi-workspace only
Quick Start — Bolt App (Node.js)
const { App } = require ('@slack/bolt' );
const app = new App ({
token : process.env .SLACK_BOT_TOKEN ,
signingSecret : process.env .SLACK_SIGNING_SECRET ,
socketMode : true ,
appToken : process.env .SLACK_APP_TOKEN ,
});
app. ( , ({ command, ack, respond }) => {
();
( );
});
app. ( , ({ body, ack, client }) => {
();
client. . ({
: body. ,
: confirmationModal,
});
});
app. ( , ({ event, say }) => {
({ : , : event. });
});
( () => {
app. (process. . || );
. ( );
})();
command
'/hello'
async
await
ack
await
respond
`Hello <@${command.user_id} >!`
action
'approve_button'
async
await
ack
await
views
open
trigger_id
trigger_id
view
event
'app_mention'
async
await
say
text
`Hello <@${event.user} >!`
thread_ts
ts
async
await
start
env
PORT
3000
console
log
'Bolt app running'
Core Concepts
Listeners
app.message (/deploy/ , async ({ message, say }) => {
await say ('Triggering deployment...' );
});
app.command ('/status' , async ({ ack, respond, command }) => {
await ack ();
respond ({ text : 'Checking status...' , response_type : 'ephemeral' });
const status = await fetchStatus ();
await respond ({ text : `Status: ${status} ` });
});
app.shortcut ('create_ticket' , async ({ shortcut, ack, client }) => {
await ack ();
await client.views .open ({
trigger_id : shortcut.trigger_id ,
view : ticketModal,
});
});
Modals (Views) const modal = {
type : 'modal' ,
callback_id : 'ticket_submission' ,
title : { type : 'plain_text' , text : 'Create Ticket' },
submit : { type : 'plain_text' , text : 'Submit' },
close : { type : 'plain_text' , text : 'Cancel' },
blocks : [
{
type : 'input' ,
block_id : 'title_block' ,
element : {
type : 'plain_text_input' ,
action_id : 'title_input' ,
placeholder : { type : 'plain_text' , text : 'Ticket title' },
},
label : { type : 'plain_text' , text : 'Title' },
},
{
type : 'input' ,
block_id : 'priority_block' ,
element : {
type : 'static_select' ,
action_id : 'priority_select' ,
options : [
{ text : { type : 'plain_text' , text : 'High' }, value : 'high' },
{ text : { type : 'plain_text' , text : 'Medium' }, value : 'medium' },
{ text : { type : 'plain_text' , text : 'Low' }, value : 'low' },
],
},
label : { type : 'plain_text' , text : 'Priority' },
},
],
};
app.view ('ticket_submission' , async ({ ack, view, client, body }) => {
await ack ();
const title = view.state .values .title_block .title_input .value ;
const priority = view.state .values .priority_block .priority_select .selected_option .value ;
await createTicket ({ title, priority, userId : body.user .id });
});
Block Kit — Rich Messages const richMessage = {
blocks : [
{
type : 'header' ,
text : { type : 'plain_text' , text : 'Deployment Ready' },
},
{
type : 'section' ,
fields : [
{ type : 'mrkdwn' , text : '*Service:*\napi-gateway' },
{ type : 'mrkdwn' , text : '*Version:*\nv1.2.3' },
{ type : 'mrkdwn' , text : '*Environment:*\nProduction' },
{ type : 'mrkdwn' , text : '*Requested by:*\n<@U1234567>' },
],
},
{ type : 'divider' },
{
type : 'actions' ,
block_id : 'deploy_actions' ,
elements : [
{
type : 'button' ,
text : { type : 'plain_text' , text : 'Approve' },
style : 'primary' ,
action_id : 'approve_deploy' ,
value : 'v1.2.3' ,
confirm : {
title : { type : 'plain_text' , text : 'Confirm Deploy' },
text : { type : 'mrkdwn' , text : 'Deploy *v1.2.3* to production?' },
confirm : { type : 'plain_text' , text : 'Deploy' },
deny : { type : 'plain_text' , text : 'Cancel' },
},
},
{
type : 'button' ,
text : { type : 'plain_text' , text : 'Reject' },
style : 'danger' ,
action_id : 'reject_deploy' ,
value : 'v1.2.3' ,
},
],
},
],
};
App Home Tab
app.event ('app_home_opened' , async ({ event, client }) => {
if (event.tab !== 'home' ) return ;
await client.views .publish ({
user_id : event.user ,
view : {
type : 'home' ,
blocks : [
{
type : 'header' ,
text : { type : 'plain_text' , text : 'My App Dashboard' },
},
{
type : 'section' ,
text : { type : 'mrkdwn' , text : 'Welcome to your app home!' },
accessory : {
type : 'button' ,
text : { type : 'plain_text' , text : 'Refresh' },
action_id : 'refresh_home' ,
},
},
],
},
});
});
Lazy Listeners (Long-Running Operations)
app.command (
'/report' ,
async ({ command, ack, respond, say }) => {
await ack ();
},
async ({ command, respond }) => {
const report = await generateReport (command.text );
await respond ({ text : report, response_type : 'in_channel' });
}
);
Socket Mode (Internal Tools)
const app = new App ({
token : process.env .SLACK_BOT_TOKEN ,
signingSecret : process.env .SLACK_SIGNING_SECRET ,
socketMode : true ,
appToken : process.env .SLACK_APP_TOKEN ,
});
OAuth (Multi-Workspace Apps) const { App , ExpressReceiver } = require ('@slack/bolt' );
const { FileInstallationStore } = require ('@slack/oauth' );
const receiver = new ExpressReceiver ({
signingSecret : process.env .SLACK_SIGNING_SECRET ,
clientId : process.env .SLACK_CLIENT_ID ,
clientSecret : process.env .SLACK_CLIENT_SECRET ,
stateSecret : process.env .SLACK_STATE_SECRET ,
scopes : ['chat:write' , 'channels:read' , 'commands' ],
installationStore : new FileInstallationStore (),
});
const app = new App ({ receiver });
Workflow Steps (Deprecated → Automations)
const ws = new WorkflowStep ('copy_review' , {
edit : async ({ ack, step, configure }) => {
await ack ();
await configure ({ blocks : editBlocks });
},
save : async ({ ack, step, view, update }) => {
await ack ();
const { values } = view.state ;
await update ({ inputs : {}, outputs : [] });
},
execute : async ({ step, complete, fail }) => {
try {
await complete ({ outputs : {} });
} catch (err) {
await fail ({ error : { message : err.message } });
}
},
});
app.step (ws);
Scopes Reference Capability Required Scopes Post messages chat:writeRead channels channels:read, groups:readRead messages channels:history, groups:historySlash commands commandsReact to messages reactions:writeUpload files files:writeRead user profiles users:read, users:read.emailHome tab im:historyBot mentions app_mentions:readDMs im:write, mpim:write
Common Patterns
Approval Workflow app.command ('/approve-request' , async ({ command, ack, client }) => {
await ack ();
await client.chat .postMessage ({
channel : '#approvals' ,
text : `<@${command.user_id} > requests approval` ,
blocks : buildApprovalBlocks (command),
});
});
app.action ('approve_action' , async ({ action, body, ack, client }) => {
await ack ();
const requestId = action.value ;
await markApproved (requestId, body.user .id );
await client.chat .update ({
channel : body.channel .id ,
ts : body.message .ts ,
text : `Approved by <@${body.user.id} >` ,
blocks : buildApprovedBlocks (requestId),
});
});
User Lookup async function lookupUser (client, email ) {
const result = await client.users .lookupByEmail ({ email });
return result.user .id ;
}
Error Handling app.error (async error => {
console .error ('Global error handler' , error);
await app.client .chat .postMessage ({
channel : process.env .ADMIN_CHANNEL ,
text : `App error: ${error.message} ` ,
});
});
Security
Always verify SLACK_SIGNING_SECRET to authenticate requests from Slack
Never log raw payloads (may contain user PII)
Use response_type: 'ephemeral' for sensitive command responses
Store tokens in encrypted secrets manager, not .env in production
Scope bot tokens to minimum required permissions
Rotate signing secrets if compromised — requires reinstalling the app
Debugging
DEBUG=bolt:* node app.js
slack run
slack triggers create --trigger-def "triggers/trigger.json"
slack events replay
Related