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.
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:
- It will calculate a response that we call the "content" of
the response.
- It will encode the content in Zulip's flavor of markdown (or
just plain text).
- It will then make a dictionary with key of "content" and
the value being that content. (Note that "response_string" is
a deprecated synonym for "content".)
- It will encode that dictionary as JSON.
If the bot code wants to opt out of responding, it can explicitly
encode a JSON dictionary that contains response_not_required
set
to True
, so that no response message is sent to the user. (This
is helpful to distinguish deliberate non-responses from bugs.)
Example incoming payload
This is an example of the JSON payload that the Zulip server will POST
to your server:
{
"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"
}
Example response payloads
Here's an example of the JSON your server should respond with if
you would not like to send a response message:
{
"response_not_required": true
}
Here's an example of the JSON your server should respond with if
you would like to send a response message:
{
"content": "Hey, we just received something from Zulip!"
}
This interface translates Zulip's outgoing webhook's request into the
format that Slack's outgoing webhook interface sends. As a result,
one should be able to use this to interact with third-party
integrations designed to work with Slack's outgoing webhook interface.
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.