snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
This commit is contained in:
225
api_docs/mobile-notifications.md
Normal file
225
api_docs/mobile-notifications.md
Normal file
@@ -0,0 +1,225 @@
|
||||
# Mobile notifications
|
||||
|
||||
Zulip Server 11.0+ supports end-to-end encryption (E2EE) for mobile
|
||||
push notifications. Mobile push notifications sent by all Zulip
|
||||
servers go through Zulip's mobile push notifications service, which
|
||||
then delivers the notifications through the appropriate
|
||||
platform-specific push notification service (Google's FCM or Apple's
|
||||
APNs). E2EE push notifications ensure that mobile notification message
|
||||
content and metadata is not visible to intermediaries.
|
||||
|
||||
Mobile clients that have [registered an E2EE push
|
||||
device](/api/register-push-device) will receive mobile notifications
|
||||
end-to-end encrypted by their Zulip server.
|
||||
|
||||
This page documents the format of the encrypted JSON-format payloads
|
||||
that the client will receive through this protocol. The same encrypted
|
||||
payload formats are used for both Firebase Cloud Messaging (FCM) and
|
||||
Apple Push Notification service (APNs).
|
||||
|
||||
## Payload examples
|
||||
|
||||
### New channel message
|
||||
|
||||
Sample JSON data that gets encrypted:
|
||||
```json
|
||||
{
|
||||
"channel_id": 10,
|
||||
"channel_name": "Denmark",
|
||||
"content": "@test_user_group",
|
||||
"mentioned_user_group_id": 41,
|
||||
"mentioned_user_group_name": "test_user_group",
|
||||
"message_id": 45,
|
||||
"realm_name": "Zulip Dev",
|
||||
"realm_url": "http://zulip.testserver",
|
||||
"recipient_type": "channel",
|
||||
"sender_avatar_url": "https://secure.gravatar.com/avatar/818c212b9f8830dfef491b3f7da99a14?d=identicon&version=1",
|
||||
"sender_full_name": "aaron",
|
||||
"sender_id": 6,
|
||||
"time": 1754385395,
|
||||
"topic": "test",
|
||||
"type": "message",
|
||||
"user_id": 10
|
||||
}
|
||||
```
|
||||
|
||||
- The `mentioned_user_group_id` and `mentioned_user_group_name` fields
|
||||
are only present for messages that mention a group containing the
|
||||
current user, and triggered a mobile notification because of that
|
||||
group mention. For example, messages that mention both the user
|
||||
directly and a group containing the user, these fields will not be
|
||||
present in the payload, because the direct mention has precedence.
|
||||
|
||||
**Changes**: New in Zulip 11.0 (feature level 413).
|
||||
|
||||
### New direct message
|
||||
|
||||
Sample JSON data that gets encrypted:
|
||||
```json
|
||||
{
|
||||
"content": "test content",
|
||||
"message_id": 46,
|
||||
"realm_name": "Zulip Dev",
|
||||
"realm_url": "http://zulip.testserver",
|
||||
"recipient_type": "direct",
|
||||
"recipient_user_ids": [6,10,12,15],
|
||||
"sender_avatar_url": "https://secure.gravatar.com/avatar/818c212b9f8830dfef491b3f7da99a14?d=identicon&version=1",
|
||||
"sender_full_name": "aaron",
|
||||
"sender_id": 6,
|
||||
"time": 1754385290,
|
||||
"type": "message",
|
||||
"user_id": 10
|
||||
}
|
||||
```
|
||||
|
||||
- The `recipient_user_ids` field is a sorted array of all user IDs in
|
||||
the direct message conversation, including both `user_id` and
|
||||
`sender_id`.
|
||||
|
||||
**Changes**: In Zulip 12.0 (feature level 429), replaced the
|
||||
`pm_users` field with `recipient_user_ids`. The old `pm_users` field
|
||||
was only present for group DMs, and was a string containing a
|
||||
comma-separated list of sorted user IDs.
|
||||
|
||||
New in Zulip 11.0 (feature level 413).
|
||||
|
||||
### Remove notifications
|
||||
|
||||
When a batch of messages that had previously been included in mobile
|
||||
notifications are marked as read, are deleted, become inaccessible, or
|
||||
otherwise should no longer be displayed to the user, a removal
|
||||
notification is sent.
|
||||
|
||||
Sample JSON data that gets encrypted:
|
||||
```json
|
||||
{
|
||||
"message_ids": [
|
||||
31,
|
||||
32
|
||||
],
|
||||
"realm_name": "Zulip Dev",
|
||||
"realm_url": "http://zulip.testserver",
|
||||
"type": "remove",
|
||||
"user_id": 10
|
||||
}
|
||||
```
|
||||
|
||||
[zulip-bouncer]: https://zulip.readthedocs.io/en/latest/production/mobile-push-notifications.html#mobile-push-notification-service
|
||||
|
||||
**Changes**: New in Zulip 11.0 (feature level 413).
|
||||
|
||||
### Test push notification
|
||||
|
||||
A user can trigger [sending an E2EE test push notification](/api/e2ee-test-notify)
|
||||
to the user's selected mobile device or all of their mobile devices.
|
||||
|
||||
Sample JSON data that gets encrypted:
|
||||
```json
|
||||
{
|
||||
"realm_name": "Zulip Dev",
|
||||
"realm_url": "http://zulip.testserver",
|
||||
"time": 1754577820,
|
||||
"type": "test",
|
||||
"user_id": 10
|
||||
}
|
||||
```
|
||||
|
||||
**Changes**: New in Zulip 11.0 (feature level 420).
|
||||
|
||||
### Data sent to Zulip's push notifications service
|
||||
|
||||
Sample JSON data sent by a self-hosted server to the Zulip's push notifications service:
|
||||
```json
|
||||
{
|
||||
"realm_uuid": "e502dde1-74fc-44b3-9e3a-114c41ed3ea4",
|
||||
"push_requests": [
|
||||
{
|
||||
"token_id": "AAAAAAAAAAE=",
|
||||
"http_headers": {
|
||||
"apns_priority": 10,
|
||||
"apns_push_type": "alert"
|
||||
},
|
||||
"payload": {
|
||||
"push_key_id": 10,
|
||||
"encrypted_data": "uOGQ9m8bdnLab/2Qq6WLdJnFUsU/NlX0955rF6GgpiZylQB/HSD+lrHct0KUXdCneu+fnGOuBMAGkYol+SLlbvdsnePn/f6wSvMDbm3iffcgiz2u8TywUlmQL/Q7Ruj5RSpLgEhpFitL/WjwQBtrA31vsqMHycmROju+tOhFlVjmzJmYy3o7ZQDi/YeB2Y+CnA5EuuXjckBYSjL4vi/YaEJXmeHvJ8Pk3T/WwXvo8CFZYlafiqSw0vC/2bkjPTFFAFVo/49nAUI+5Rpa90wJUVChsrkKTclOs4Ih1dNIDYr6+WoIKJTtIR9zgDg3YOkVHBZhlt7Se3i40WAs5JAb1PViMpAp2hbU36z1Qq0g90nmfRjXN9FRdAPaKlbFTT2PkEtS9wVBv9T14ufkhbOwaMLfx5iaHKw3XHoWo7Fe+0IF9ZJ77uhCZoA1kyFKDhl7AZ8K4DOvib8gsfkeAR4XXXnXVmLtAyjBhMrWYNsECo9j4UeE6M90z3xIVR8=",
|
||||
"aps": {
|
||||
"mutable-content": 1,
|
||||
"alert": {
|
||||
"title": "New notification"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"token_id": "AAAAAAAAAAI=",
|
||||
"fcm_priority": "high",
|
||||
"payload": {
|
||||
"push_key_id": 20,
|
||||
"encrypted_data": "OzPhtLiyU1U+3ynqyTxkFt83N5GN7t3Uw8/OkCoFKFo/cu3GAzCMMbAAhPghflkrFK37SNOuxpPiL1TzPy5tQJqdSKpQrgu6cp0Y6VVA1aV/zsCDAcSABaWeaOeC5mVL+xFpmFeEbhzUaOLchbRn4kBO4m8gqDU/rAn0cKFY1F7tyCgC+fvvcczP05itDLpkwZMnrADGp3tSHFldr4iGO1pWJxFTXFFhg63UyH1FcMXKFzBPek7hLbpLsqu5OFEQv2TtDbAYdWZr1LXRqnkHTDmMd6NAdkOsVcnk31jHThFPDqaM5zDXb24hGHW79OpBnGAQWydfeChS4pC4yHWCO6ZRDqwvJX9IydS+V7S91KCl0QSToaXvgW7Q3zvHunzu7L/rw0dQQRgPM3qIOHr7gGtptkZpmKuT6icdDGgjRtgP/L0TfxdRKa37fn6nF64+HH60wLPYWOz7vZjgTrA20MrbA3ogMfhFYpwjppidFGVWjrLpk+peQjHB1sY="
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Changes**: In Zulip 12.0 (feature level 468), the `device_id` and
|
||||
`push_account_id` fields were replaced with `token_id` and `push_key_id`
|
||||
to support the rotation of tokens provided by FCM/APNs and keys used
|
||||
to encrypt push notifications.
|
||||
|
||||
New in Zulip 11.0 (feature level 413).
|
||||
|
||||
### Data sent to FCM
|
||||
|
||||
Zulip's push notifications service uses [Firebase Admin Python SDK](https://github.com/firebase/firebase-admin-python)
|
||||
to access FCM.
|
||||
|
||||
A sample `messages` argument, which is internally used by the SDK to prepare payload for FCM,
|
||||
passed to [`firebase_admin.messaging.send_each`](https://firebase.google.com/docs/reference/admin/python/firebase_admin.messaging#send_each):
|
||||
```py
|
||||
[
|
||||
firebase_admin.messaging.Message(
|
||||
data={
|
||||
'push_key_id': '20',
|
||||
'encrypted_data': 'OzPhtLiyU1U+3ynqyTxkFt83N5GN7t3Uw8/OkCoFKFo/cu3GAzCMMbAAhPghflkrFK37SNOuxpPiL1TzPy5tQJqdSKpQrgu6cp0Y6VVA1aV/zsCDAcSABaWeaOeC5mVL+xFpmFeEbhzUaOLchbRn4kBO4m8gqDU/rAn0cKFY1F7tyCgC+fvvcczP05itDLpkwZMnrADGp3tSHFldr4iGO1pWJxFTXFFhg63UyH1FcMXKFzBPek7hLbpLsqu5OFEQv2TtDbAYdWZr1LXRqnkHTDmMd6NAdkOsVcnk31jHThFPDqaM5zDXb24hGHW79OpBnGAQWydfeChS4pC4yHWCO6ZRDqwvJX9IydS+V7S91KCl0QSToaXvgW7Q3zvHunzu7L/rw0dQQRgPM3qIOHr7gGtptkZpmKuT6icdDGgjRtgP/L0TfxdRKa37fn6nF64+HH60wLPYWOz7vZjgTrA20MrbA3ogMfhFYpwjppidFGVWjrLpk+peQjHB1sY=',
|
||||
},
|
||||
token='push-device-token-3',
|
||||
android=firebase_admin.messaging.AndroidConfig(priority='high'),
|
||||
),
|
||||
]
|
||||
```
|
||||
|
||||
**Changes**: In Zulip 12.0 (feature level 468), the `push_account_id`
|
||||
field was replaced with `push_key_id` to support the rotation of
|
||||
keys used to encrypt push notifications.
|
||||
|
||||
New in Zulip 11.0 (feature level 413).
|
||||
|
||||
### Data sent to APNs
|
||||
|
||||
Zulip's push notifications service uses [aioapns](https://github.com/Fatal1ty/aioapns) to access APNs.
|
||||
|
||||
A sample `request` argument, which is internally used by the library to prepare payload for APNs,
|
||||
passed to [`aioapns.APNs.send_notification`](https://github.com/Fatal1ty/aioapns/blob/96831003ec5a8986206cde77e59fdb4b5a3c4b24/aioapns/client.py):
|
||||
```py
|
||||
aioapns.NotificationRequest(
|
||||
apns_topic='remote_push_device_ios_app_id',
|
||||
device_token='push-device-token-1',
|
||||
message={
|
||||
'push_key_id': 10,
|
||||
'encrypted_data': 'rUNqoWOB+EQmjThJyXhDptmUrHyzSx4DPlvShzrM7XGdRVMG5qNuH0dnGQDVza9frnWNVOF3vFcuYvDnUnYRBf1j+/n1ML1K2CBnsThCGl3KJNWrKcf5fME7Q1dU2xtJ3+RAKuLtZ9y2gq6DWamui7WfQ75m1eJpYRDbbHIQEiSIZpo7X2Lie3aHkQBgE8SN5MJ6N3VM33DM6i1xGpIeWiFy+hqNloGyEI2qf6xV0SjvvkN+HbGticben4atBkAuAIKi0gIYMPyMihH26T1sEhOH3IDyO3KvaHe1NIdj0naT9RoFkN5UgdxIchXQ7qkVEjivA2E/HefpvZYlhems6TAosfJwgCMB8HuydqdImjixkugRQfugroTTG97p6xQIJSFWCOyrpuBDElI0Ale8XjmzaVo4Dbgqz5kIAhmJWtlwgJw8nt7Orr3EWUVjnIAi0nHCFObAXNShedAbyuLeC1qezqC4FZe/GOLLi4DPWgWSdk8PV5vGw9YC+XcZ38dqQogtpG7dpzMwwsqzLBmlzQ==',
|
||||
'aps': {
|
||||
'mutable-content': 1,
|
||||
'alert': {'title': 'New notification'}
|
||||
}
|
||||
},
|
||||
priority=10,
|
||||
push_type="alert",
|
||||
)
|
||||
```
|
||||
|
||||
**Changes**: In Zulip 12.0 (feature level 468), the `push_account_id`
|
||||
field was replaced with `push_key_id` to support the rotation of
|
||||
keys used to encrypt push notifications.
|
||||
|
||||
New in Zulip 11.0 (feature level 413).
|
||||
Reference in New Issue
Block a user