# Incoming webhook integrations An incoming webhook allows a third-party service to push data to Zulip when something happens. There are several ways to set up an incoming webhook in Zulip: * Use our [REST API](/api/rest) endpoint for [sending messages](/api/send-message). This works great for internal tools or cases where the third-party tool wants to control the formatting of the messages in Zulip. * Use one of our supported [integration frameworks](/integrations/meta-integration), such as the [Slack-compatible incoming webhook](/integrations/doc/slack_incoming), [Zapier integration](/integrations/doc/zapier), or [IFTTT integration](/integrations/doc/ifttt). * Implementing an incoming webhook integration (detailed on this page), where all the logic for formatting the Zulip messages lives in the Zulip server. This is how most of [Zulip's official integrations](/integrations/) work, because they enable Zulip to support third-party services that just have an "outgoing webhook" feature (without the third party needing to do any work specific to Zulip). In an incoming webhook integration, the third-party service's "outgoing webhook" feature sends an `HTTP POST` to a special URL when it has something for you, and then the Zulip "incoming webhook" integration handles that incoming data to format and send a message in Zulip. New official Zulip webhook integrations can take just a few hours to write, including tests and documentation, if you use the right process. ## Quick guide * Set up the [Zulip development environment](https://zulip.readthedocs.io/en/latest/development/overview.html). * Use [Zulip's JSON integration](/integrations/doc/json), , or a similar site to capture an example webhook payload from the third-party service. Create a `zerver/webhooks//fixtures/` directory, and add the captured JSON payload as a test fixture. * Create an `Integration` object, and add it to the `WEBHOOK_INTEGRATIONS` list in `zerver/lib/integrations.py`. Search for `WebhookIntegration` in that file to find an existing one to copy. * Write a draft webhook handler in `zerver/webhooks//view.py`. There are a lot of examples in the `zerver/webhooks/` directory that you can copy. We recommend templating from a short one, like `zendesk`. * Write a test for your fixture in `zerver/webhooks//tests.py`. Run the test for your integration like this: ``` tools/test-backend zerver/webhooks// ``` Iterate on debugging the test and webhooks handler until it all works. * Capture payloads for the other common types of `POST`s the third-party service will make, and add tests for them; usually this part of the process is pretty fast. * Document the integration in `zerver/webhooks//doc.md`(required for getting it merged into Zulip). You can use existing documentation, like [this one](https://raw.githubusercontent.com/zulip/zulip/main/zerver/webhooks/github/doc.md), as a template. This should not take more than 15 minutes, even if you don't speak English as a first language (we'll clean up the text before merging). ## Hello world walkthrough Check out the [detailed walkthrough](incoming-webhooks-walkthrough) for step-by-step instructions. ## Checklist ### Files that need to be created Select a name for your incoming webhook and use it consistently. The examples below are for a webhook named `MyWebHook`. * `zerver/webhooks/mywebhook/__init__.py`: Empty file that is an obligatory part of every python package. Remember to `git add` it. * `zerver/webhooks/mywebhook/view.py`: The main webhook integration function, called `api_mywebhook_webhook`, along with any necessary helper functions. * `zerver/webhooks/mywebhook/fixtures/message_type.json`: Sample JSON payload data used by tests. Add one fixture file per type of message supported by your integration. * `zerver/webhooks/mywebhook/tests.py`: Tests for your webhook. * `zerver/webhooks/mywebhook/doc.md`: End-user documentation explaining how to add the integration. * `static/images/integrations/logos/mywebhook.svg`: A square logo for the platform/server/product you are integrating. Used on the documentation pages as well as the sender's avatar for messages sent by the integration. * `static/images/integrations/mywebhook/001.png`: A screenshot of a message sent by the integration, used on the documentation page. This can be generated by running `tools/screenshots/generate-integration-docs-screenshot --integration mywebhook`. * `static/images/integrations/bot_avatars/mywebhook.png`: A square logo for the platform/server/product you are integrating which is used to create the avatar for generating screenshots with. This can be generated automatically from `static/images/integrations/logos/mywebhook.svg` by running `tools/setup/generate_integration_bots_avatars.py`. ### Files that need to be updated * `zerver/lib/integrations.py`: Add your integration to `WEBHOOK_INTEGRATIONS`. This will automatically register a URL for the incoming webhook of the form `api/v1/external/mywebhook` and associate it with the function called `api_mywebhook_webhook` in `zerver/webhooks/mywebhook/view.py`. Also add your integration to `DOC_SCREENSHOT_CONFIG`. This will allow you to automatically generate a screenshot for the documentation by running `tools/screenshots/generate-integration-docs-screenshot --integration mywebhook`. ## Common Helpers * If your integration will receive a test webhook payload, you can use `get_setup_webhook_message` to create our standard message for test payloads. You can import this from `zerver/lib/webhooks/common.py`, and it will generate a message like this: "GitHub webhook is successfully configured! 🎉" ## General advice * Consider using our Zulip markup to make the output from your integration especially attractive or useful (e.g. emoji, Markdown emphasis, or @-mentions). * Use topics effectively to ensure sequential messages about the same thing are threaded together; this makes for much better consumption by users. E.g. for a bug tracker integration, put the bug number in the topic for all messages; for an integration like Nagios, put the service in the topic. * Integrations that don't match a team's workflow can often be uselessly spammy. Give careful thought to providing options for triggering Zulip messages only for certain message types, certain projects, or sending different messages to different streams/topics, to make it easy for teams to configure the integration to support their workflow. * Consistently capitalize the name of the integration in the documentation and the Client name the way the vendor does. It's OK to use all-lower-case in the implementation. * Sometimes it can be helpful to contact the vendor if it appears they don't have an API or webhook we can use; sometimes the right API is just not properly documented. * A helpful tool for testing your integration is [UltraHook](http://www.ultrahook.com/), which allows you to receive webhook calls via your local Zulip development environment. This enables you to do end-to-end testing with live data from the service you're integrating and can help you spot why something isn't working or if the service is using custom HTTP headers. ## URL specification The base URL for an incoming webhook integration bot, where `INTEGRATION_NAME` is the name of the specific webhook integration and `API_KEY` is the API key of the bot created by the user for the integration, is: ``` {{ api_url }}/v1/external/INTEGRATION_NAME?api_key=API_KEY ``` The list of existing webhook integrations can be found by browsing the [Integrations documentation](/integrations/) or in `zerver/lib/integrations.py` at `WEBHOOK_INTEGRATIONS`. Parameters accepted in the URL include: ### api_key *(required)* The API key of the bot created by the user for the integration. To get a bot's API key, see the [API keys](/api/api-keys) documentation. ### stream The stream for the integration to send notifications to. Can be either the stream ID or the [URL-encoded][url-encoder] stream name. By default the integration will send direct messages to the bot's owner. !!! tip "" A stream ID can be found when [browsing streams][browse-streams] in the web or desktop apps. ### topic The topic in the specified stream for the integration to send notifications to. The topic should also be [URL-encoded][url-encoder]. By default the integration will have a topic configured for stream messages. ### only_events, exclude_events Some incoming webhook integrations support these parameters to filter which events will trigger a notification. You can append either `&only_events=["event_a","event_b"]` or `&exclude_events=["event_a","event_b"]` (or both, with different events) to the URL, with an arbitrary number of supported events. You can use UNIX-style wildcards like `*` to include multiple events. For example, `test*` matches every event that starts with `test`. !!! tip "" For a list of supported events, see a specific [integration's documentation](/integrations) page. [browse-streams]: /help/introduction-to-channels#browse-and-subscribe-to-channels [add-bot]: /help/add-a-bot-or-integration [url-encoder]: https://www.urlencoder.org/ ## Related articles * [Integrations overview](/api/integrations-overview) * [Incoming webhook walkthrough](/api/incoming-webhooks-walkthrough) * [Non-webhook integrations](/api/non-webhook-integrations)