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 an 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 Personal settings () -> 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:
- @-mention the bot user in a channel. If the bot replies, its
reply will be sent to that channel and topic.
- Send a direct message with the bot as one of the recipients.
If the bot replies, its reply will be sent to that thread.
Timeouts
The remote server must respond to a POST
request in a timely manner.
The default timeout for outgoing webhooks is 10 seconds, though this
can be configured by the administrator of the Zulip server by setting
OUTGOING_WEBHOOKS_TIMEOUT_SECONDS
in the server's
settings.
This is an example of the JSON payload that the Zulip server will POST
to your server:
{
"bot_email": "outgoing-bot@localhost",
"bot_full_name": "Outgoing webhook test",
"data": "@**Outgoing webhook test** Zulip is the world\u2019s most productive group chat!",
"message": {
"avatar_url": "https://secure.gravatar.com/avatar/1f4f1575bf002ae562fea8fc4b861b09?d=identicon&version=1",
"client": "website",
"content": "@**Outgoing webhook test** Zulip is the world\u2019s most productive group chat!",
"display_recipient": "Verona",
"id": 112,
"is_me_message": false,
"reactions": [],
"recipient_id": 20,
"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_email": "[email protected]",
"sender_full_name": "Iago",
"sender_id": 5,
"sender_realm_str": "zulip",
"stream_id": 5,
"subject": "Verona2",
"submessages": [],
"timestamp": 1527876931,
"topic_links": [],
"type": "stream"
},
"token": "xvOzfurIutdRRVLzpXrIIHXJvNfaJLJ0",
"trigger": "mention"
}
Fields documentation
Return values
-
bot_email
: string
Email of the bot user.
-
bot_full_name
: string
The full name of the bot user.
-
data
: string
The message content, in raw Markdown format (not rendered to HTML).
-
trigger
: string
What aspect of the message triggered the outgoing webhook notification.
Possible values include direct_message
and mention
.
Changes: In Zulip 8.0 (feature level 201), renamed the trigger
private_message
to direct_message
.
-
token
: string
A string of alphanumeric characters that can be used to authenticate the
webhook request (each bot user uses a fixed token). You can get the token used by a given outgoing webhook bot
in the zuliprc
file downloaded when creating the bot.
-
message
: object
A dictionary containing details on the message that triggered the
outgoing webhook, in the format used by GET /messages
.
-
avatar_url
: string | null
The URL of the message sender's avatar. Can be null
only if
the current user has access to the sender's real email address
and client_gravatar
was true
.
If null
, then the sender has not uploaded an avatar in Zulip,
and the client can compute the gravatar URL by hashing the
sender's email address, which corresponds in this case to their
real email address.
Changes: Before Zulip 7.0 (feature level 163), access to a
user's real email address was a realm-level setting. As of this
feature level, email_address_visibility
is a user setting.
-
client
: string
A Zulip "client" string, describing what Zulip client
sent the message.
-
content
: string
The content/body of the message.
-
content_type
: string
The HTTP content_type
for the message content. This
will be text/html
or text/x-markdown
, depending on
whether apply_markdown
was set.
-
display_recipient
: string | (object)[]
Data on the recipient of the message;
either the name of a channel or a dictionary containing basic data on
the users who received the message.
-
edit_history
: (object)[]
An array of objects, with each object documenting the
changes in a previous edit made to the message,
ordered chronologically from most recent to least recent
edit.
Not present if the message has never been edited or if the realm has
disabled viewing of message edit history.
Every object will contain user_id
and timestamp
.
The other fields are optional, and will be present or not
depending on whether the channel, topic, and/or message
content were modified in the edit event. For example, if
only the topic was edited, only prev_topic
and topic
will be present in addition to user_id
and timestamp
.
Changes: In Zulip 10.0 (feature level 284), removed the
prev_rendered_content_version
field as it is an internal
server implementation detail not used by any client.
-
prev_content
: string
Only present if message's content was edited.
The content of the message immediately prior to this
edit event.
-
prev_rendered_content
: string
Only present if message's content was edited.
The rendered HTML representation of prev_content
.
-
prev_stream
: integer
Only present if message's channel was edited.
The channel ID of the message immediately prior to this
edit event.
Changes: New in Zulip 3.0 (feature level 1).
-
prev_topic
: string
Only present if message's topic was edited.
The topic of the message immediately prior to this
edit event.
Changes: New in Zulip 5.0 (feature level 118).
Previously, this field was called prev_subject
;
clients are recommended to rename prev_subject
to
prev_topic
if present for compatibility with
older Zulip servers.
-
stream
: integer
Only present if message's channel was edited.
The ID of the channel containing the message
immediately after this edit event.
Changes: New in Zulip 5.0 (feature level 118).
-
timestamp
: integer
The UNIX timestamp for the edit.
-
topic
: string
Only present if message's topic was edited.
The topic of the message immediately after this edit event.
Changes: New in Zulip 5.0 (feature level 118).
-
user_id
: integer | null
The ID of the user that made the edit.
Will be null
only for edit history
events predating March 2017.
Clients can display edit history events where this
is null
as modified by either the sender (for content
edits) or an unknown user (for topic edits).
-
id
: integer
The unique message ID. Messages should always be
displayed sorted by ID.
-
is_me_message
: boolean
Whether the message is a /me status message
-
last_edit_timestamp
: integer
The UNIX timestamp for when the message was last edited,
in UTC seconds.
Not present if the message has never been edited.
-
reactions
: (object)[]
Data on any reactions to the message.
-
emoji_name
: string
Name of the emoji.
-
emoji_code
: string
A unique identifier, defining the specific emoji codepoint requested,
within the namespace of the reaction_type
.
-
reaction_type
: string
A string indicating the type of emoji. Each emoji reaction_type
has an independent namespace for values of emoji_code
.
Must be one of the following values:
-
unicode_emoji
: In this namespace, emoji_code
will be a
dash-separated hex encoding of the sequence of Unicode codepoints
that define this emoji in the Unicode specification.
-
realm_emoji
: In this namespace, emoji_code
will be the ID of
the uploaded custom emoji.
-
zulip_extra_emoji
: These are special emoji included with Zulip.
In this namespace, emoji_code
will be the name of the emoji (e.g.
"zulip").
-
user_id
: integer
The ID of the user who added the reaction.
Changes: New in Zulip 3.0 (feature level 2). The user
object is deprecated and will be removed in the future.
-
user
: object
Dictionary with data on the user who added the
reaction, including the user ID as the id
field. Note that reactions data received from the
events API has a slightly different
user
dictionary format, with the user ID field
called user_id
instead.
Changes: Deprecated and to be removed in a future release
once core clients have migrated to use the adjacent user_id
field, which was introduced in Zulip 3.0 (feature level 2).
Clients supporting older Zulip server versions should use
the user ID mentioned in the description above as they would
the user_id
field.
-
recipient_id
: integer
A unique ID for the set of users receiving the
message (either a channel or group of users). Useful primarily
for hashing.
Changes: Before Zulip 10.0 (feature level 327), recipient_id
was the same across all incoming 1:1 direct messages. Now, each
incoming message uniquely shares a recipient_id
with outgoing
messages in the same conversation.
-
sender_email
: string
The Zulip API email address of the message's sender.
-
sender_full_name
: string
The full name of the message's sender.
-
sender_id
: integer
The user ID of the message's sender.
-
sender_realm_str
: string
A string identifier for the realm the sender is in. Unique only within
the context of a given Zulip server.
E.g. on example.zulip.com
, this will be example
.
-
stream_id
: integer
Only present for channel messages; the ID of the channel.
-
subject
: string
The topic
of the message. Currently always ""
for direct messages,
though this could change if Zulip adds support for topics in direct
message conversations.
The field name is a legacy holdover from when topics were
called "subjects" and will eventually change.
-
submessages
: (object)[]
Data used for certain experimental Zulip integrations.
-
msg_type
: string
The type of the message.
-
content
: string
The new content of the submessage.
-
message_id
: integer
The ID of the message to which the submessage has been added.
-
sender_id
: integer
The ID of the user who sent the message.
-
id
: integer
The ID of the submessage.
-
timestamp
: integer
The UNIX timestamp for when the message was sent,
in UTC seconds.
-
topic_links
: (object)[]
Data on any links to be included in the topic
line (these are generated by custom linkification
filters that match content in the
message's topic.)
Changes: This field contained a list of urls before
Zulip 4.0 (feature level 46).
New in Zulip 3.0 (feature level 1). Previously, this field was called
subject_links
; clients are recommended to rename subject_links
to topic_links
if present for compatibility with older Zulip servers.
-
type
: string
The type of the message: "stream"
or "private"
.
-
rendered_content
: string
The content/body of the message rendered in HTML.
Replying with a message
Many bots implemented using this outgoing webhook API will want to
send a reply message into Zulip. Zulip's outgoing webhook API
provides a convenient way to do that by simply returning an
appropriate HTTP response to the Zulip server.
A correctly implemented bot will return a JSON object containing one
of two possible formats, described below.
Example response payloads
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.)
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!"
}
The content
field should contain Zulip-format Markdown.
Note that an outgoing webhook bot can use the Zulip REST
API with its API key in case your bot needs to do
something else, like add an emoji reaction or upload a file.
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 |
ID of the Zulip organization prefixed by "T". |
team_domain |
Hostname of the Zulip organization |
channel_id |
Channel ID prefixed by "C" |
channel_name |
Channel name |
thread_ts |
Timestamp for when message was sent |
timestamp |
Timestamp for when message was sent |
user_id |
ID of the user who sent the message prefixed by "U" |
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', 'T1512'),
('team_domain', 'zulip.example.com'),
('channel_id', 'C123'),
('channel_name', 'integrations'),
('thread_ts', 1532078950),
('timestamp', 1532078950),
('user_id', 'U21'),
('user_name', 'Full Name'),
('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.