Go to Slack

Incoming Webhooks

You’re viewing documentation on legacy custom integrations, an older way for teams to build into their Slack team. To securely utilize the newest platform features like message buttons & the Events API, build internal integrations as part of a Slack app just for your team instead. Get started.

Send data into Slack in real-time.

Incoming Webhooks are a simple way to post messages from external sources into Slack. They make use of normal HTTP requests with a JSON payload that includes the message text and some options. Message Attachments can also be used in Incoming Webhooks to display richly-formatted messages that stand out from regular chat messages.

Start by setting up an incoming webhook integration in your Slack team to try these features out:

  1. Sending messages
  2. Adding links
  3. Runtime customizations
  4. Make it fancy with advanced formatting
  5. Putting it all together

Use curl, a simple, ubiquitous tool for sending HTTP requests on the command line, for the curl examples that follow.

Sending messages

Let's learn how to send this in-channel message as an incoming webhook:

Screenshot of a simple incoming webhook

It's a simple, multi-line message without special formatting:

This is a line of text.
And this is another one.

The first step is to prepare this message as a key/value pair in JSON. For a simple message, your JSON payload only needs to define a text property, containing the text that will be posted to the channel.

In JSON, our message is defined as:

{
    "text": "This is a line of text.\nAnd this is another one."
}

Please note that we indicated the line break as the control character \n. We also added additional whitespace for readability, which we could have more tidily presented to you as:

{"text":"This is a line of text.\nAnd this is another one."}

Once you've put together the JSON for your message, you can choose to send it to your Incoming Webhook URL one of two ways:

Send it directly in JSON

The preferred way to send Slack your JSON body is by sending a HTTP POST to your webhook URL, containing a request body with an explicit Content-type HTTP header set to application/json. This tells Slack how to interpret the data you're sending us.

POST https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
Content-type: application/json
{
    "text": "This is a line of text.\nAnd this is another one."
}

By declaring the content type, no further encoding of the POST body is needed — just provide valid JSON in UTF-8.

curl example
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"This is a line of text.\nAnd this is another one."}' \
 https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX

Send a JSON string within a parameter of a standard POST request

If you want to stick with the more traditional Content-type of application/x-www-form-urlencoded, you can sneakily send URL-escaped JSON within the payload parameter instead.

Send the payload parameter as part of your POST body and explicitly state the Content-type. Payloads should not be included as query parameters on the webhook URL.

The trickiest part of this approach is that you must properly URL encode your payload. Your elegant JSON message becomes seeming nonsense, filled with percent-encoded characters.

POST https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
Content-type: application/x-www-form-urlencoded
payload=%7B%22text%22%3A%22This%20is%20a%20line%20of%20text.%5CnAnd%20this%20is%20another%20one.%22%7D

Many HTTP clients provide convenient functions for URL encoding and setting the Content-type.

curl example
curl -X POST \
--data-urlencode 'payload={"text":"This is a line of text.\nAnd this is another one."}' \
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX

Using the --data-urlencode curl parameter automatically URL encodes the provided string.


Adding links

To create a link in your text, enclose the URL in <> angle brackets. For example: payload={"text": "<https://slack.com>"} will post a clickable link to https://slack.com.

To display hyperlinked text instead of the actual URL, use the pipe character, as shown in this example:

{
    "text": "<https://alert-system.com/alerts/1234|Click here> for details!"
}

This will be displayed in the channel as:

Screenshot of a simple incoming webhook with a link

Runtime customizations

Though it is best to use a single incoming webhook for a specific purpose, in some cases you may want to override the default channel and authoring identity of an incoming webhook.

Customizing your username and icon

Incoming webhooks originate from a default identity you configured when originally creating your webhook. You can override a custom integration's configured name with the username field in your JSON payload.

You can also override the bot icon either with icon_url or icon_emoji.

{
    "username": "ghost-bot",
    "icon_emoji": ":ghost:",
    "text": "BOO!"
}

An overridden username and icon could look like this:

Screenshot of a simple incoming webhook with an overridden icon and name

Channel override

Incoming webhooks output to a default channel and can only send messages to a single channel at a time. You can override a custom integration's configured channel by specifying the channel field in your JSON payload.

It's worth noting that Slack's concept of a channel is pretty flexible. A channel usually means what you think it will, but it can also be a direct message between two users, a private channel, or it could be the instance of a conversation between one or more users.

Specify a Slack channel by name with "channel": "#other-channel", or send a Slackbot message to a specific user with "channel": "@username".

{
    "channel": "#other-channel",
    "text": "This message will appear in #other-channel"
}

If you prefer, you can also specify a channel ID or user ID like so: "channel": "U024BE7LH".

Sending messages to "yourself"

If you're the creator of a custom incoming webhook, you can configure the webhook's default channel to send messages to your own account while setting the integration up. Then when invoking the webhook, omit the channel parameter to utilize the default selection or provide the relevant direct message conversation ID found in im.list.

Try to limit messages to a reasonable maximum of a few thousand characters.


Advanced message formatting

You can use Slack's standard message markup to add simple formatting to your messages. You can also use message attachments to display richly-formatted message blocks.

Use the Message Builder to preview your message formatting and attachments in real time!

Screenshot of a simple incoming webhook with an attachment

Please note that you must include either the text or fallback property to the message attachment, otherwise the attachment will not be displayed correctly! The value of each property can be an empty string (" ").

Putting it all together

Here is a sample curl command for posting to a channel using the payload parameter:

curl example
curl -X POST \
--data-urlencode 'payload={"text": "This is posted to <#general> and comes from *monkey-bot*.", "channel": "#general", "username": "monkey-bot", "icon_emoji": ":monkey_face:"}' \
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX

And here is a version using a Content-type of application/json:

curl example
curl -X POST \
-H 'Content-type: application/json' \
--data '{"text": "This is posted to <#general> and comes from *monkey-bot*.", "channel": "#general", "username": "monkey-bot", "icon_emoji": ":monkey_face:"}' \
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX

With either approach, this will be displayed in the #general channel as:

Screenshot of a simple incoming webhook with an icon and links

Interactive messages

Custom incoming webhooks cannot make use of interactive message buttons. Build a Slack app instead.

Handling errors

Though in most cases you'll receive a "HTTP 200 OK" response with a JSON body indicating that your message posted successfully, it's best to prepare for scenarios where attempts to publish a message will fail.

Incoming webhooks may throw errors when receiving malformed requests, when utilized webhook URLs are no longer valid, or when something truly exceptional prevents your messages from making it through to channels and users.

Incoming webhooks return more expressive errors than our Web API, including more relevant HTTP status codes (like "HTTP 400 Bad Request" and "HTTP 404 Not Found"). These changes are described in our changelog: Changes to errors for incoming webhooks.

Common errors you may encounter include:

  • invalid_payload typically indicates that received request is malformed — perhaps the JSON is structured incorrectly, or the message text is not properly escaped. The request should not be retried without correction.
  • user_not_found and channel_not_found indicate that the user or channel being addressed either do not exist or are invalid. The request should not be retried without modification or until the indicated user or channel is set up.
  • channel_is_archived indicates the specified channel has been archived and is no longer accepting new messages.
  • action_prohibited usually means that a team admin has placed some kind of restriction on this avenue of posting messages and that, at least for now, the request should not be attempted again.
  • posting_to_general_channel_denied is thrown when an incoming webhook attempts to post to the "#general" channel for a team where posting to that channel is 1) restricted and 2) the creator of the same incoming webhook is not authorized to post there. You'll receive this error with a HTTP 403.
  • too_many_attachments is thrown when an incoming webhook attempts to post a message with greater than 100 attachments. A message can have a maximum of 100 attachments associated with it.