Slack Incoming Webhooks: Send Messages from Your Backend

Updated October 6, 20258 min read

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.

  1. Open https://api.slack.com/apps and click Create New App.
  2. Select the target workspace and name your app.
  3. 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 blocks for structured layouts or attachments for legacy formats.
  • Set icon_emoji or icon_url at 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 text field.
  • 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.url per 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

StepActionExample / Notes
Create appEnable Incoming Webhooks in app featuresSlack dashboard → Features → Incoming Webhooks
Get webhook URLAdd webhook to workspace, pick channelhttps://hooks.slack.com/services/…
Send messagePOST JSON to webhook URL{ "text": "Hello, world." }
Reply to threadInclude thread_ts in payloadLookup timestamp via Slack Web API
Automate installsRequest incoming-webhook scopeWebhook 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.

Secure vault for webhook URLs and environment tunneling
Replay Slack payloads to test Block Kit layouts
Team-wide history for debugging missed alerts
Alerts when webhooks fail or return errors

Further Reading