Outgoing Webhooks

Outgoing Webhooks allow you to build or set up Zulip integrations which are notified when certain types of messages are sent in Zulip. When one of those events is triggered, we'll send a HTTP POST payload to the webhook's configured URL. Webhooks can be used to power a wide range of Zulip integrations. For example, the Zulip Botserver is built on top of this API.

Zulip supports outgoing webhooks both in a clean native Zulip format, as well as a format that's compatible with Slack's outgoing webhook API, which can help with porting an existing Slack integration to work with Zulip.

To register an outgoing webhook:

  • Log in to the Zulip server.
  • Navigate to Settings () -> Your bots -> Add a new bot. Select Outgoing webhook for bot type, the URL you'd like Zulip to post to as the Endpoint URL, the format you want, and click on Create bot. to submit the form/
  • Your new bot user will appear in the Active bots panel, which you can use to edit the bot's settings.

Triggering

There are currently two ways to trigger an outgoing webhook: 1. @-mention the bot user in a stream. If the bot replies, its reply will be sent to that stream and topic. 2. Send a private message with the bot as one of the recipients. If the bot replies, its reply will be sent to that thread.

Zulip message format

The Zulip-format webhook messages post the following data, encoded as JSON:

Name Description
bot_email Email of the bot user
data The content of the message (in Markdown)
message A dict containing details on the message which triggered the outgoing webhook
token A string of alphanumeric characters you can use to authenticate the webhook request (each bot user uses a fixed token)
trigger Trigger method

Some of the important fields in the message dict include the following:

Name Description
recipient_id Unique ID of the stream that will persist even if the stream is renamed
rendered_content The content of the message, rendered in HTML

A correctly implemented endpoint will do the following:

  • For a successful request, it should return receives either a json encoded dictionary, describing how to respond to the user.
  • If the dictionary contains response_not_required set to True, no response message is sent to the user.
  • If the dictionary contains response_string key, the corresponding value is used as the content for a Zulip message sent in response; otherwise, a generic default response message is sent.
  • For a failed request, the endpoint should return data on the error.

Example payload

{
    "bot_email": "outgoing-bot@localhost",
    "data": "@**Outgoing Webhook Test** Zulip is the world\u2019s most productive group chat!",
    "message": {
        "client": "website",
        "content": "@**Outgoing Webhook Test** Zulip is the world\u2019s most productive group chat!",
        "display_recipient": "Verona",
        "id": 112,
        "is_me_message": false,
        "raw_display_recipient": "Verona",
        "reactions": [],
        "recipient_id": 20,
        "recipient_type": 2,
        "recipient_type_id": 5,
        "rendered_content": "<p><span class=\"user-mention\" data-user-id=\"25\">@Outgoing Webhook Test</span> Zulip is the world\u2019s most productive group chat!</p>",
        "sender_avatar_source": "G",
        "sender_avatar_version": 1,
        "sender_email": "iago@zulip.com",
        "sender_full_name": "Iago",
        "sender_id": 5,
        "sender_is_mirror_dummy": false,
        "sender_realm_id": 1,
        "sender_realm_str": "zulip",
        "sender_short_name": "iago",
        "stream_id": 5,
        "subject": "Verona2",
        "subject_links": [],
        "submessages": [],
        "timestamp": 1527876931,
        "type": "stream"
    },
    "token": "xvOzfurIutdRRVLzpXrIIHXJvNfaJLJ0",
    "trigger": "mention"
}

Slack-format webhook format

This interface translates the Zulip's outgoing webhook's request into Slack's outgoing webhook request. Hence the outgoing webhook bot would be able to post data to URLs which support Slack's outgoing webhooks. Here's how we fill in the fields that a Slack format webhook expects:

Name Description
token A string of alphanumeric characters you can use to authenticate the webhook request (each bot user uses a fixed token)
team_id String ID of the Zulip organization
team_domain Domain of the Zulip organization
channel_id Stream ID
channel_name Stream name
timestamp Timestamp for when message was sent
user_id ID of the user who sent the message
user_name Full name of sender
text The content of the message (in Markdown)
trigger_word Trigger method
service_id ID of the bot user

The above data is posted as list of tuples (not JSON), here's an example:

[('token', 'v9fpCdldZIej2bco3uoUvGp06PowKFOf'),
 ('team_id', 'zulip'),
 ('team_domain', 'zulip.com'),
 ('channel_id', '123'),
 ('channel_name', 'integrations'),
 ('timestamp', 1532078950),
 ('user_id', 21),
 ('user_name', 'Sample User'),
 ('text', '@**test**'),
 ('trigger_word', 'mention'),
 ('service_id', 27)]
  • For successful request, if data is returned, it returns that data, else it returns a blank response.
  • For failed request, it returns the reason of failure, as returned by the server, or the exception message.