Back to Blog
Developers

How to Create a Slack Webhook URL in 4 Steps

Create a Slack app at api.slack.com/apps, turn on Incoming Webhooks, click Add New Webhook to Workspace and pick a channel. Slack gives you a hooks.slack.com URL that posts to that channel. Private channels, DMs, several channels, the Workflow Builder alternative, and the errors you may see.

Slack Green Team
September 28, 2026
October 6, 2026
5 min read
Share:
slack api
webhooks
developers
automation

To create a Slack webhook, make a Slack app and turn on incoming webhooks. Go to api.slack.com/apps, click Create New App, choose From scratch and pick your workspace. Open Incoming Webhooks, switch Activate Incoming Webhooks on, click Add New Webhook, choose the channel and click Allow. Slack shows a URL like https://hooks.slack.com/services/T000/B000/XXXX. Anything you POST to it as JSON appears in that channel.

Each URL posts to one channel. You cannot change the channel, name or icon per message: in our legacy vs app webhook test the app webhook ignored all three fields and still answered ok. For a private channel you must be a member first.

We created a webhook from scratch in a test workspace on 4 October, posted eight test payloads to it, then deleted the app and kept posting. The screenshots and responses below are from that run.

Create the webhook URL

  • Create an app. Open api.slack.com/apps, click Create New App, then From scratch. Name it after what will post, such as "Deploy bot", and pick the workspace.
  • Turn on incoming webhooks. In the app settings, click Incoming Webhooks in the left menu and switch Activate Incoming Webhooks to On. If you create the app from a manifest that asks for the incoming-webhook scope, the switch is already On.
  • Add a webhook. Click Add New Webhook at the bottom. Slack opens a permission page with a Channel for webhook picker. Choose the channel and click Allow. If your workspace requires admin approval for apps, the request goes to an admin first.
  • Slack permission page for a new app: Allow the sglab w1 webhook app to access Slack, with Channel for webhook set to a private channel marked with a lock

  • Copy the URL from Webhook URLs for Your Workspace. The table also shows the channel and who added it, and the trash icon removes the URL.
  • Webhook URLs for Your Workspace in the Slack app settings, with a sample curl request, one webhook URL (secret part blurred), its channel and Added By

    Adding the webhook also posts a notice in that channel: "added an integration to this channel", followed by your app's name. Everyone in the channel sees it.

    Test it from a terminal:

    curl -X POST -H 'Content-type: application/json' \
      --data '{"text":"Hello from my webhook"}' \
      https://hooks.slack.com/services/T000/B000/XXXX

    Slack answers ok with HTTP 200 and the message appears in the channel, posted by your app. In our run each POST took 300 to 460 ms.

    Treat the URL like a password. Slack's docs warn that it contains a secret and that Slack searches for leaked webhook URLs and revokes them. Keep it in an environment variable or a secrets store, not in a public repository.

    Private channels, DMs and several channels

  • • Private channel. Join the channel first. Slack only lists private channels you are in; our private test channel showed up in the picker with a lock icon.
  • • Direct message. The channel picker also lists people. Choose yourself to get messages in your own DM, which is handy for personal alerts.
  • • Several channels. Click Add New Webhook again for each channel. Every channel gets its own URL, all under the same app. Tools that take one webhook URL, such as Grafana and Alertmanager, need one webhook per channel too.
  • • Change the channel. You cannot edit it. Add a new webhook for the new channel and remove the old one under Webhook URLs for Your Workspace.

If you need to pick the channel per message, or edit and delete messages later, use a bot token and chat.postMessage instead. Webhooks cannot delete a message once it is posted.

App webhook or Workflow Builder webhook?

Slack has a second kind of webhook that needs no app. In Workflow Builder, start a workflow From a webhook. Slack gives you a URL starting with https://hooks.slack.com/triggers/, and the workflow's steps decide what to post and where.

App incoming webhookWorkflow Builder webhook
URLhooks.slack.com/services/...hooks.slack.com/triggers/...
PlansAll plansPaid plans
PayloadFull message JSON, including Block KitUp to 20 flat variables, no nested JSON
Who can make itAnyone allowed to install appsMembers, unless admins restrict webhook workflows
Rate limitAbout 1 message per second1 request per second

Pick Workflow Builder when a non-developer should own the message text. Pick an app webhook when your code builds the message. Ideas for workflows are in Slack Workflow Builder examples.

Never appear "away" on Slack again

Cloud-based. No downloads. Works 24/7 even when your laptop is off.

Send a formatted message

A webhook accepts the same JSON as other Slack messages: text for the notification and fallback, and blocks for the layout.

{
  "text": "Deploy finished",
  "blocks": [
    {
      "type": "section",
      "text": { "type": "mrkdwn", "text": "*Deploy finished* for `web`\n<https://example.com/runs/42|Open the run>" }
    }
  ]
}

Tested curl, Python and Node.js senders, with retries for 429, are in Slack webhook curl example. On Windows, the PowerShell version uses Invoke-RestMethod and needs no module. Links use <url|text> in webhook messages, not [text](url). The syntax for bold, lists, mentions and the markdown block is in Slack Block Kit markdown, and a ready GitHub setup is in how to send a Slack message from GitHub Actions. To put a new line in the text, send \n; more in how to add a new line in Slack.

To design the blocks visually before you send them, see how to use Slack Block Kit Builder.

What a webhook accepts: our test payloads

We sent each of these to the new webhook URL and read the result in the channel:

PayloadResponseWhat showed up
{"text":"..."}200 okThe text, posted by the app
text plus a blocks section with mrkdwn200 okBold title, inline code and a working link
Form-encoded payload={"text":"..."} instead of JSON200 okThe text
{"foo":"bar"}, no text400 no_textNothing
{"text":""}400 no_textNothing
Cut-off JSON ({"text": "broken)400 invalid_payloadNothing
[docs](https://example.com) in the text200 okThe brackets stay as typed; only the bare URL becomes a link
"channel":"#social" added200 okPosted in the webhook's own channel; the override was ignored
The test channel after the run: the app posted the plain text, a Deploy finished block with a link, the form payload, a markdown link left as brackets, and the channel override test

Deleting the app does not stop the URL at once. We deleted the app through Slack's manifest API and posted again: the first request, about a second later, still returned ok and posted. Five seconds later the same URL returned 404 no_service, and so did every request after. The channel got a "removed an integration from this channel" notice.

Webhook errors

ResponseMeaning
400 invalid_payloadThe JSON is malformed, or text is not escaped
400 no_textThe payload has no text
403 action_prohibitedAn admin restricted posting this way
403 posting_to_general_channel_deniedOnly some people may post in #general
404 no_serviceThe webhook was disabled, removed or mistyped
404 channel_not_foundThe channel is gone or the app lost access
410 channel_is_archivedThe channel was archived

We saw no_text, invalid_payload and no_service ourselves; the other rows come from Slack's docs. In a later test an archived channel's webhook returned 404 no_active_hooks, not 410, and stayed broken after we unarchived it; that test and every other error we reproduced are in why a Slack webhook is not working. Slack's docs say an invalid_payload should not be retried without a fix. For no_service, create a new webhook; the old URL will not come back.

FAQ

How do I get a Slack webhook URL? Create an app at api.slack.com/apps, activate Incoming Webhooks, click Add New Webhook and choose a channel.

Can one webhook post to several channels? No. Create one webhook per channel under the same app.

Can a Slack webhook send a DM? Yes. Pick a person, such as yourself, in the channel picker when you add the webhook.

Is a Slack webhook free? App incoming webhooks work on every plan. Workflow Builder webhooks need a paid plan.

Always Active

Stop Jiggling Your Mouse.

Join hundreds of remote workers who never worry about their Slack status. Set it up once, stay green forever.

Related Articles

Guide

How to See All Your DMs in Slack, Including the Hidden Ones

The DMs tab lists your direct messages with people. App DMs, Slackbot and DMs with deleted accounts sit elsewhere. We compared the tab with the full list the Slack API returned.

Slack Green Team
Guide

Slack Daily Standup Without a Paid Bot: /remind, Workflow Builder and a Script

We set up a weekday 9:30 standup in a free Slack workspace three ways: a /remind reminder, Workflow Builder, and a short script that posts the questions and collects the thread replies.

Slack Green Team
Guide

Leave Multiple Slack Channels at Once: 4 Ways Timed, Plus a Script

Slack has no bulk leave button. We made 12 test channels and left them with the sidebar menu, the channel browser, /leave and a 30-line script, and timed each way.

Slack Green Team