Slack Incoming Webhooks: Send Messages from Your Backend
Slack incoming webhooks give your backend a lightweight way to send channel updates, deployment alerts, and customer notifications without spinning up a full Slack bot. Follow these steps to enable webhooks, post payloads, manage threads, and secure URLs according to Slack’s official guidance.
Step 1: Create a Slack App & Enable Webhooks
Start by creating or selecting a Slack app inside the Slack App management dashboard. Incoming webhooks are disabled by default, so you’ll need to toggle them on for each workspace install.
- Open https://api.slack.com/apps and click Create New App.
- Select the target workspace and name your app.
- Under Features → Incoming Webhooks, toggle Activate Incoming Webhooks to On.
Step 2: Generate a Webhook URL
When webhooks are activated, Slack lets you create channel-specific URLs tied to the permissioned user and workspace. Each URL has a fixed destination and inherits your app’s posting identity.
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX- Choose Add New Webhook to Workspace and authorize a channel.
- Copy the generated URL and store it securely (environment variables or secrets manager).
- Remember: the URL encodes team, channel, and authorized user—treat it like a credential.
Step 3: Send JSON Payloads
Posting a message is as simple as making an HTTPS POST request with a JSON body. Slack accepts plain text or Block Kit rich formatting, so you can start minimal and grow into interactive layouts.
# Basic message
curl -X POST \
-H 'Content-type: application/json' \
--data '{"text":"Hello, world."}' \
$SLACK_WEBHOOK_URL// Block Kit example
{
"text": "New customer review",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Danny left a review."
}
}
]
}Payload tips
- Always send valid JSON with
Content-type: application/json. - Use
blocksfor structured layouts or attachments for legacy formats. - Set
icon_emojioricon_urlat the app level; incoming webhooks ignore per-message overrides.
Step 4: Reply within Threads
Incoming webhooks can reply in threads when you supply the parent message timestamp. Since Slack doesn’t return timestamps from webhook posts, fetch them through other Slack APIs like conversations.history or the Events API.
curl -X POST \
-H 'Content-type: application/json' \
--data '{
"text": "Investigating the alert",
"thread_ts": "1725550825.000400"
}' \
$SLACK_WEBHOOK_URL- Store relevant message timestamps if your backend must follow up in the same thread.
- Combine incoming webhooks with Slack Web API methods to look up context or post attachments back to the channel.
Step 5: Secure URLs & Handle Errors
Webhook URLs function as bearer tokens—anyone who has the URL can post to your channel. Slack also returns descriptive error responses so you can detect misconfigurations quickly.
Security checklist
- Store webhook URLs in secrets managers or environment variables.
- Rotate the URL if it leaks—Slack lets you revoke from the app dashboard.
- Restrict who can install your app to trusted workspaces.
Common errors
- 400 Bad Request: malformed JSON or missing
textfield. - 403 Forbidden: webhook disabled or insufficient permissions.
- 404 Not Found: URL revoked or channel removed.
- invalid_payload, channel_is_archived: Slack-specific error strings in the response body.
Step 6: Automate Distribution with OAuth
If you plan to distribute your Slack integration, request the incoming-webhook OAuth scope and capture generated URLs programmatically during installation.
{
"access_token": "xoxb-123",
"incoming_webhook": {
"channel": "#alerts",
"channel_id": "C01ABCDEF",
"configuration_url": "https://slack.com/services/ABC123",
"url": "https://hooks.slack.com/services/T000/B000/XYZ"
}
}- Persist the
incoming_webhook.urlper workspace install. - Handle reinstall and uninstall flows to keep webhook inventories accurate.
- Use Slack’s configuration URL to let admins adjust posting channels later.
Summary Table
| Step | Action | Example / Notes |
|---|---|---|
| Create app | Enable Incoming Webhooks in app features | Slack dashboard → Features → Incoming Webhooks |
| Get webhook URL | Add webhook to workspace, pick channel | https://hooks.slack.com/services/… |
| Send message | POST JSON to webhook URL | { "text": "Hello, world." } |
| Reply to thread | Include thread_ts in payload | Lookup timestamp via Slack Web API |
| Automate installs | Request incoming-webhook scope | Webhook URL available in OAuth response |
Build Faster with Hooklistener
Hooklistener captures incoming webhook traffic across Slack, Stripe, Shopify, and custom providers. Use it to centralize secrets, forward events to local tunnels, and replay payloads while you iterate on formatting or incident response.