slack-api

star 4

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.

paulpas By paulpas schedule Updated 6/4/2026

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 WebClient from SLACK_BOT_TOKEN environment variable — never hardcode tokens
  • Use Block Kit (blocks parameter) 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 SlackApiError with status-specific logic — inspect response["error"] for the error code
  • Use views_open / views_update for modal interactions; ack() every interaction within 3 seconds
  • Use chat_postMessage with blocks for messages, files_upload_v2 for 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-api or coding-sendgrid-api instead)
  • 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-api for Discord bots)

Core Workflow

  1. Initialize the Client — Create a WebClient(token=os.environ["SLACK_BOT_TOKEN"]) for API calls, or use Bolt App(token=..., signing_secret=...) for event-driven apps. Checkpoint: Verify the token by calling client.auth_test() on startup — this validates the token and returns the bot user ID and team info.

  2. Construct the Message — Use Block Kit builders for rich messages. Always include a fallback text parameter for notifications. Structure blocks as an array of block dicts. Checkpoint: Preview Block Kit JSON using Slacks Block Kit Builder before deploying.

  3. Send or Respond — Use client.chat_postMessage() for proactive messages, say() in Bolt for command responses, or response.write() for webhook replies. For slash commands, always call ack() first within 3 seconds. Checkpoint: Check the response ts (timestamp) — its presence confirms successful delivery.

  4. 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 call ack() as the first operation in every handler to prevent timeout errors.

  5. Error Handling and Retry — Catch SlackApiError and inspect response["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, and SLACK_APP_TOKEN in environment variables — never hardcode
  • Always include a text fallback parameter alongside blocks for 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"] in SlackApiError for 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_list to 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:

  1. Client InitializationWebClient from SLACK_BOT_TOKEN env var; Bolt App from bot token + signing secret
  2. Message Construction — Block Kit JSON array with at least one block type; text fallback always included
  3. Interaction Handlingack() called immediately; async processing after acknowledgment
  4. Error Handling — Catches SlackApiError and inspects response["error"]; logs error with correlation context
  5. 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

Live References

Install via CLI
npx skills add https://github.com/paulpas/agent-skill-router --skill slack-api
Repository Details
star Stars 4
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator