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
- In your Slack App settings, enable Incoming Webhooks.
- Click Add New Webhook to Workspace and pick the destination channel.
- 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_inactiveandtoken_revoked. Workspaces churn, users leave, admins uninstall. Your code should mark dead tokens and stop using them.
Next Steps
Build a Bot
Add interactivity, slash commands, and event handling on top of your integration.
Ship a Workflow Step
Let users of your integration drag your API directly into Workflow Builder.
API Reference
Browse every endpoint — oauth.v2.access, chat.postMessage, conversations.history, and more.
Sample App
A real Slack integration end-to-end — clone it, run it, learn from it.

