How to Set Up Slack Incoming Webhooks: A Complete Developer Guide
Slack's incoming webhook API is one of the simplest ways to send messages to a channel programmatically. Here's how to set it up, what the payload looks like, and how to send rich, formatted notifications.
Jason Warner
April 16, 2026
How to Set Up Slack Incoming Webhooks: A Complete Developer Guide
Slack's incoming webhook integration is one of the fastest ways to push notifications into a channel from an external system. A deployment finished? Send a message. A payment failed? Send a message. An alert fired? Send a message.
Here's everything you need to know to get it working, including the format for rich Block Kit messages and how to handle delivery reliably.
Step 1: Create a Slack App
Incoming webhooks are tied to a Slack app. If you don't have one yet:
- Go to api.slack.com/apps
- Click Create New App → From scratch
- Give it a name (something like "Deploy Notifier" or "Alert Bot")
- Select the workspace where you want to post messages
Step 2: Enable Incoming Webhooks
In your app settings:
- Navigate to Features → Incoming Webhooks
- Toggle Activate Incoming Webhooks on
- Scroll down and click Add New Webhook to Workspace
- Choose the channel you want to post to
- Click Allow
Slack will display a webhook URL. It looks like this:
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
Copy this URL — you'll use it to send messages. Treat it like a secret: anyone who has this URL can post to your channel.
Step 3: Send Your First Message
The minimum payload is just a text field:
curl -X POST https://hooks.slack.com/services/T00000000/B00000000/XXXX \
-H 'Content-Type: application/json' \
-d '{"text": "Hello from my app!"}'
If it works, you'll get ok back from Slack and your message will appear in the channel.
Step 4: Send Rich Messages with Block Kit
Plain text works, but Block Kit lets you build structured, formatted messages with sections, buttons, dividers, and images.
Here's a deployment notification:
{
"text": "Deployment to production succeeded",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "✅ Deployment Succeeded"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Environment:*\nProduction"
},
{
"type": "mrkdwn",
"text": "*Branch:*\nmain"
},
{
"type": "mrkdwn",
"text": "*Deployed by:*\nJason Warner"
},
{
"type": "mrkdwn",
"text": "*Duration:*\n2m 14s"
}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {"type": "plain_text", "text": "View Deploy Logs"},
"url": "https://your-app.com/deploys/123",
"style": "primary"
}
]
}
]
}
The text field is a fallback for notifications and accessibility — always include it alongside blocks.
Use Slack's Block Kit Builder to design your message layout visually before writing the JSON by hand.
Handling Errors
Slack returns ok for success and an error string for failures:
| Response | Meaning |
|---|---|
ok |
Message delivered |
no_text |
Missing text field in non-blocks message |
invalid_payload |
Malformed JSON |
action_prohibited |
Webhook disabled or channel archived |
channel_not_found |
Channel was deleted |
too_many_attachments |
More than 100 attachments |
Slack rate-limits incoming webhooks to 1 request per second per webhook. If you need higher throughput, create multiple webhooks or use the Slack Web API with a bot token instead.
Rotating a Compromised Webhook URL
If your webhook URL gets exposed (committed to a public repo, leaked in logs), go to your app's Incoming Webhooks page and delete the compromised webhook. Create a new one. Old URLs stop working immediately after deletion.
A good practice: store the webhook URL in an environment variable, never hardcoded in source.
Sending Webhook Events to Slack via Bluejay Relay
A common pattern is using Bluejay Relay to route events from other services (Stripe, GitHub, Shopify) directly to Slack — without writing any application code.
For example, to get a Slack message every time a Stripe payment succeeds:
- Create a Bluejay Relay integration with Stripe as the source
- Add a destination pointing to your Slack webhook URL
- Configure a transformation to format the payload as a Slack Block Kit message
The transformation template might look like:
{
"text": "New payment: {{ event.data.object.amount | divided_by: 100 }} {{ event.data.object.currency | upcase }}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Payment received:* {{ event.data.object.amount | divided_by: 100 }} {{ event.data.object.currency | upcase }}\n*Customer:* {{ event.data.object.customer }}"
}
}
]
}
This pattern means no new code in your application — just configure the relay and the transformation.
Summary
- Create a Slack app and enable incoming webhooks in 5 minutes
- Post to the webhook URL with a JSON body containing
textorblocks - Use Block Kit for rich, structured notifications
- Store the URL as a secret, rotate it if it's compromised
- Rate limit is 1 req/sec per webhook URL
Want to route other services' webhooks directly to Slack? Try Bluejay Relay free — transform and forward webhooks without writing code.
Build more reliable webhook workflows.
Capture, transform, and retry webhooks with full observability. Free to start, no credit card.