name: slack-api description: Integrates Slack API (Web API, Events API, Bolt Framework, Incoming Webhooks, Block Kit) using the slack-sdk Python v3.x with proper event handling, Block Kit construction, and OAuth patterns. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: coding triggers: slack, slack api, slack bot, slack webhook, block kit, slack events, bolt python, send message to slack archetypes:
- tactical
- generation anti_triggers:
- brainstorming
- vague ideation
- code golf
- over-engineering response_profile: verbosity: low directive_strength: high abstraction_level: operational role: implementation scope: implementation output-format: code content-types:
- code
- guidance
- do-dont
- examples related-skills: coding-discord-api, coding-twilio-api, coding-sendgrid-api
Slack API Integration (Bolt, Web API, Block Kit, Events)
Integrates the Slack platform — Web API, Events API, Bolt Framework, Incoming Webhooks, and Block Kit — using the slack-sdk Python SDK v3.x and bolt-python. When loaded, this skill makes the model implement Slack integrations with proper event handling, Block Kit message construction, OAuth flow, webhook verification, and slash command processing.
TL;DR for Code Generation
- Initialize
WebClientfromSLACK_BOT_TOKENenvironment variable — never hardcode tokens - Use Block Kit (
blocksparameter) for rich messages — never send plain text fallback without blocks - For event-driven apps, use
Bolt(from slack_bolt import App) with Socket Mode for development - Verify Slack request signatures on every incoming webhook and command using
request_verification - Handle
SlackApiErrorwith status-specific logic — inspectresponse["error"]for the error code - Use
views_open/views_updatefor modal interactions;ack()every interaction within 3 seconds - Use
chat_postMessagewithblocksfor messages,files_upload_v2for file sharing
When to Use
Use this skill when:
- Building Slack bots that respond to messages, slash commands, or events
- Sending rich (Block Kit) notifications to Slack channels from external applications via Incoming Webhooks
- Implementing OAuth-based Slack app installations with the OAuth flow
- Creating interactive components (modals, message actions, shortcuts) for Slack users
- Monitoring Slack events (messages, reactions, channel joins) via the Events API
- Automating channel management, message archiving, or user provisioning via the Web API
When NOT to Use
Avoid this skill for:
- SMS or email delivery (use
coding-twilio-apiorcoding-sendgrid-apiinstead) - Voice or video calling — Slack handles that natively; use the Web API to create huddles instead
- Replacing a dedicated customer support platform — Slack is an internal communication tool
- Discord-specific integrations (use
coding-discord-apifor Discord bots)
Core Workflow
Initialize the Client — Create a
WebClient(token=os.environ["SLACK_BOT_TOKEN"])for API calls, or use BoltApp(token=..., signing_secret=...)for event-driven apps. Checkpoint: Verify the token by callingclient.auth_test()on startup — this validates the token and returns the bot user ID and team info.Construct the Message — Use Block Kit builders for rich messages. Always include a fallback
textparameter for notifications. Structure blocks as an array of block dicts. Checkpoint: Preview Block Kit JSON using Slacks Block Kit Builder before deploying.Send or Respond — Use
client.chat_postMessage()for proactive messages,say()in Bolt for command responses, orresponse.write()for webhook replies. For slash commands, always callack()first within 3 seconds. Checkpoint: Check the responsets(timestamp) — its presence confirms successful delivery.Handle Events (Bolt Framework) — Register event listeners with
@app.event("event_type"). Use@app.command()for slash commands,@app.action()for interactive components,@app.view()for modal submissions. Checkpoint: Always callack()as the first operation in every handler to prevent timeout errors.Error Handling and Retry — Catch
SlackApiErrorand inspectresponse["error"]. Common errors:not_authorized,invalid_blocks,ratelimited,channel_not_found. Implement retry with exponential backoff for rate limits. Checkpoint: Log the error payload with correlation IDs for debugging.
Implementation Patterns
Pattern 1: Block Kit Notification with Webhook
import os
import json
import requests
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
# ❌ BAD — plain text only, no blocks, no error handling, hardcoded channel
client = WebClient(token="xoxb-xxx")
client.chat_postMessage(channel="#general", text="Deploy complete")
# ✅ GOOD — Block Kit message with fallback text, error handling, env-based config
import logging
logger = logging.getLogger(__name__)
def send_deploy_notification(service: str, version: str, environment: str, status: str) -> str | None:
"""Send a rich Block Kit deployment notification to Slack."""
client = WebClient(token=os.environ["SLACK_BOT_TOKEN"])
channel = os.environ.get("SLACK_DEPLOY_CHANNEL", "#deployments")
color = "#36a64f" if status == "success" else "#ff0000"
blocks = [
{
"type": "header",
"text": {"type": "plain_text", "text": f"Deploy: {service} v{version} — {status}"},
},
{
"type": "section",
"fields": [
{"type": "mrkdwn", "text": f"*Service:*\n{service}"},
{"type": "mrkdwn", "text": f"*Version:*\n{version}"},
{"type": "mrkdwn", "text": f"*Environment:*\n{environment}"},
{"type": "mrkdwn", "text": f"*Status:*\n{status}"},
],
},
{
"type": "context",
"elements": [{"type": "mrkdwn", "text": f"Deploy completed at {__import__('datetime').datetime.now().isoformat()}"}],
},
]
try:
response = client.chat_postMessage(
channel=channel,
text=f"Deploy: {service} v{version} — {status}", # Fallback for notifications
blocks=blocks,
unfurl_links=False,
)
ts: str = response["ts"]
logger.info("Notification sent", extra={"channel": channel, "ts": ts, "service": service})
return ts
except SlackApiError as exc:
error_code = exc.response["error"]
logger.error("Slack API error", extra={"error": error_code, "channel": channel})
if error_code == "ratelimited":
retry_after = exc.response.headers.get("Retry-After", "5")
logger.warning("Rate limited", extra={"retry_after": retry_after})
return None
Pattern 2: Bolt Slash Command with Modal
import os
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
from slack_sdk.errors import SlackApiError
# ❌ BAD — plain text response, no ack() handling, no error checking
@app.command("/ticket")
def handle_ticket(command, respond):
respond(f"Creating ticket: {command['text']}")
# ✅ GOOD — modal response with ack(), input validation, error handling
app = App(
token=os.environ["SLACK_BOT_TOKEN"],
signing_secret=os.environ["SLACK_SIGNING_SECRET"],
)
@app.command("/ticket")
def open_ticket_modal(command, ack, client, logger):
"""Open a modal to create a support ticket."""
# Acknowledge the command immediately (must be within 3 seconds)
ack()
modal_view = {
"type": "modal",
"callback_id": "ticket_submit",
"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": "Brief description"},
},
"label": {"type": "plain_text", "text": "Title"},
},
{
"type": "input",
"block_id": "priority_block",
"element": {
"type": "static_select",
"action_id": "priority_select",
"placeholder": {"type": "plain_text", "text": "Select priority"},
"options": [
{"text": {"type": "plain_text", "text": "Low"}, "value": "low"},
{"text": {"type": "plain_text", "text": "Medium"}, "value": "medium"},
{"text": {"type": "plain_text", "text": "High"}, "value": "high"},
{"text": {"type": "plain_text", "text": "Critical"}, "value": "critical"},
],
},
"label": {"type": "plain_text", "text": "Priority"},
},
],
}
try:
client.views_open(trigger_id=command["trigger_id"], view=modal_view)
except SlackApiError as exc:
logger.error(f"Failed to open modal: {exc.response['error']}")
@app.view("ticket_submit")
def handle_ticket_submission(ack, body, client, logger):
"""Handle modal submission."""
ack()
values = body["view"]["state"]["values"]
title = values["title_block"]["title_input"]["value"]
priority = values["priority_block"]["priority_select"]["selected_option"]["value"]
# Store or process the ticket
ticket_id = f"TICKET-{hash(title) % 10000:04d}"
user_id = body["user"]["id"]
try:
client.chat_postMessage(
channel=user_id,
text=f"Ticket {ticket_id} created!",
blocks=[
{
"type": "section",
"text": {"type": "mrkdwn", "text": f"*Ticket {ticket_id} Created*\n*Title:* {title}\n*Priority:* {priority}"},
}
],
)
except SlackApiError as exc:
logger.error(f"Failed to confirm: {exc.response['error']}")
# Socket Mode for local development
if __name__ == "__main__":
handler = SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"])
handler.start()
Pattern 3: Incoming Webhook (Simplest Integration)
import os
import json
import requests
# ❌ BAD — no Block Kit, no error handling, assumes webhook URL is valid
requests.post(
"https://hooks.slack.com/services/T00/B00/xxx",
json={"text": "Hello"},
)
# ✅ GOOD — Block Kit, structured payload, error handling, timeout
def send_webhook_notification(webhook_url: str, message: str, title: str | None = None) -> bool:
"""Send a simple Block Kit notification via Slack Incoming Webhook."""
blocks = [
{
"type": "section",
"text": {"type": "mrkdwn", "text": message},
}
]
if title:
blocks.insert(0, {
"type": "header",
"text": {"type": "plain_text", "text": title},
})
payload = {
"text": message, # Fallback for notifications
"blocks": blocks,
"unfurl_links": False,
}
try:
resp = requests.post(
webhook_url,
json=payload,
timeout=10,
headers={"Content-Type": "application/json"},
)
resp.raise_for_status()
return True
except requests.exceptions.RequestException as exc:
logging.getLogger(__name__).error("Webhook failed", extra={"error": str(exc)})
return False
Constraints
MUST DO
- Store
SLACK_BOT_TOKEN,SLACK_SIGNING_SECRET, andSLACK_APP_TOKENin environment variables — never hardcode - Always include a
textfallback parameter alongsideblocksfor notification banners and accessibility - Call
ack()as the first operation in every Bolt handler (commands, actions, shortcuts, views) — you have 3 seconds - Verify request signatures on every incoming Slack request using Bolt built-in verification or manual HMAC
- Use Socket Mode (
SocketModeHandler) for local development — no public HTTP endpoint required - Check
response["error"]inSlackApiErrorfor specific error codes —ratelimited,invalid_blocks,not_authorized - Preview Block Kit JSON in the Block Kit Builder before deploying to production
MUST NOT DO
- Never reuse or expose OAuth access tokens across different workspaces — each Slack installation gets its own token
- Never skip
ack()in interaction handlers — unacknowledged interactions will produce a timeout error to the user - Hardcode channel IDs or names — load them from configuration or use
conversations_listto resolve names - Send sensitive data (passwords, API keys, PII) in Slack messages — Slack is not an encrypted platform
- Poll the Events API — use the Event Subscriptions or Socket Mode for real-time event delivery
Output Template
When implementing Slack API code, the output must follow this structure:
- Client Initialization —
WebClientfromSLACK_BOT_TOKENenv var; BoltAppfrom bot token + signing secret - Message Construction — Block Kit JSON array with at least one block type;
textfallback always included - Interaction Handling —
ack()called immediately; async processing after acknowledgment - Error Handling — Catches
SlackApiErrorand inspectsresponse["error"]; logs error with correlation context - Security — Request signature verification on all incoming requests (Bolt handles this automatically)
Related Skills
| Skill | Purpose |
|---|---|
coding-discord-api |
Discord bot development — alternative to Slack for community-oriented messaging |
coding-twilio-api |
SMS and Voice communication — use alongside Slack for multi-channel notifications |
coding-sendgrid-api |
Transactional email — use for email fallback when Slack notifications need escalation |