Build for SlackBuilder's Special

Building Integrations

Building Slack Integrations

Not every Slack integration needs to be a full app. Sometimes you just need to post an alert. Sometimes you're shipping a product that thousands of workspaces will install. This guide covers the spectrum — from a one-line webhook to a public Marketplace listing.


Pick the Right Shape

Incoming Webhook

Lightest. One URL, one HTTP POST, one message in a channel. No tokens, no scopes, no events. Perfect for CI alerts, deploy notifications, and cron jobs.

Single-Workspace App

Most common. A Slack App installed only in your workspace. Has a token, can read events, can respond interactively. Internal tools live here.

Distributed App

For products. A Slack App installed by other people's workspaces, listed on the Marketplace, with OAuth flow, multi-tenant token storage, and review.


Pattern 1 — Incoming Webhooks

The simplest integration on the platform. You get a URL; you POST JSON; a message appears.

Set it up

  1. In your Slack App settings, enable Incoming Webhooks.
  2. Click Add New Webhook to Workspace and pick the destination channel.
  3. Copy the URL — it looks like https://hooks.slack.com/services/T0/B0/abc....

Use it

curl -X POST -H 'Content-Type: application/json' \
  --data '{"text":"Deploy succeeded :tada:"}' \
  https://hooks.slack.com/services/T0/B0/abc...

Or with Block Kit for a richer layout:

curl -X POST -H 'Content-Type: application/json' \
  --data '{
    "blocks": [
      {"type": "header",  "text": {"type": "plain_text", "text": ":rocket: Production Deploy"}},
      {"type": "section", "text": {"type": "mrkdwn",     "text": "*Version:* `v2.14.3`\n*Author:* @alex\n*Duration:* 42s"}},
      {"type": "actions", "elements": [
        {"type": "button", "text": {"type": "plain_text", "text": "View build"}, "url": "https://ci.example.com/builds/4821"}
      ]}
    ]
  }' \
  https://hooks.slack.com/services/T0/B0/abc...

A webhook URL is tied to a single channel. If you need to post to many channels, use a bot token and chat.postMessage — that's just as fast and doesn't lock you into one destination.

When webhooks are the wrong choice

  • You need to read anything from Slack (messages, channel lists, user profiles).
  • You need to react to user actions (button clicks, slash commands, events).
  • You need to post to multiple channels or workspaces from one configuration.

In any of those cases, build a bot instead.


Pattern 2 — Distributing Your App

  flowchart TD
    A["User installs app via Add to Slack"] --> B["Slack OAuth flow returns temporary code"]
    B --> C["Exchange code for workspace access token"]

    C --> D["Store tokens by team_id"]
    D --> E["Handle revocation and reinstall by replacing old tokens"]

    E --> F["Submit production-ready app to Slack Marketplace"]
    F --> G["Slack reviews and approves public listing"]

The OAuth exchange in code

# After Slack redirects the user back to your callback with ?code=...,
# exchange the code for an access token:

curl -X POST "https://slack.com/api/oauth.v2.access" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "client_id=$SLACK_CLIENT_ID" \
  --data-urlencode "client_secret=$SLACK_CLIENT_SECRET" \
  --data-urlencode "code=$AUTHORIZATION_CODE" \
  --data-urlencode "redirect_uri=$SLACK_REDIRECT_URI"

Multi-tenant token storage is the #1 source of bugs in distributed Slack apps. Always look up the token by team_id before every API call. Never cache a single global token.


Pattern 3 — Cross-System Sync

Many integrations are about syncing data between Slack and another system: pulling Slack messages into a CRM, mirroring tickets into a triage channel, archiving conversations for compliance.

The building blocks:

Events API

Slack pushes events to your server in near-real-time: message, reaction_added, channel_created. Subscribe to only what you need — every event is bandwidth and processing.

Conversations history

For backfill and audit. conversations.history pages through channel messages; conversations.replies fetches thread replies. Use cursor-based pagination and respect rate limits.

Users & profile

Resolve Slack user IDs (U0123...) to real names, emails, and team membership with users.info and users.lookupByEmail. Cache aggressively — these change rarely.

Files API

Upload files into Slack with files.upload, or download files users shared via the URLs in file_shared events (you'll need the bot token to access private files).


Real-World Examples


Production Checklist

  • Use the SDK, not raw HTTP. Retries, signature verification, pagination, and rate limit handling are already built in.
  • Verify every inbound request. Compute the HMAC of the raw body with your Signing Secret and reject mismatches.
  • Store tokens encrypted at rest. Treat bot and user tokens like passwords — they grant full app access to a workspace.
  • Page through everything. conversations.history, users.list, and friends return cursors, not full lists. Don't assume one page is enough.
  • Handle account_inactive and token_revoked. Workspaces churn, users leave, admins uninstall. Your code should mark dead tokens and stop using them.

Next Steps

On this page