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.
설치로 이동 Skills Marketplace 커뮤니티가 만든 AI 스킬을 발견하고 탐색하세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/oimiragieo/agent-studio --skill slack-expert명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Zip 다운로드 다운로드 중... 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