{
"data": {
"version": "v2",
"sender": "stream.chat",
"type": "{{ event_type }}",
"id": "{{ message.id }}",
"message_id": "{{ message.id }}",
"channel_type": "{{ channel.type }}",
"channel_id": "{{ channel.id }}",
"cid": "{{ channel.cid }}",
"receiver_id": "{{ receiver.id }}"
},
"android": {
"priority": "high"
},
"apns": {
"payload": {
"aps": {
"alert": {
"title": "New message from {{ sender.name }}",
"body": "{{ truncate message.text 200 }}"
},
"badge": {{ unread_count }},
"sound": "default",
"mutable-content": 1,
"content-available": 0
}
}
}
}
Push Templates in v3
Templates in v3
For both Firebase and APNs, the payload that is being sent is rendered using the handlebars templating language, to ensure full configurability for your app.
Stream provides the following variables in the template rendering context:
Context Variables
Name | Type | Description |
---|---|---|
channel | object | Channel object. You can access the channel name and any other custom field you have defined for this channel |
sender | object | Sender object. You can access the user name, id or any other custom field you have defined for the user |
receiver | object | Receiver object. You can access the user name, id or any other custom field you have defined for the user |
message | object | Message object. You can access the text of the message (or a preview of it if the message is too large) or any other custom field you have defined for the message |
members | array | Channel members. You can access the user name, id and any other custom field of each member (i.e. excluding sender) |
otherMembers | array | Like members but the user who will be receiving the notification is excluded (i.e. excluding sender and receiver) |
unread_count | integer | Number of unread messages |
unread_channels | integer | Number of unread channels for this user |
Defaults
Push v3 supports templating for both Firebase and APNs. Configuring templates is optional — if no custom templates are provided, Stream will automatically use the default templates for Firebase and APNs.
The version field in the data payload is set to v2. It is to ensure backward compatibility with the existing SDKs.
Firebase default template:
By default, there is only a data message and no notification field in the android payload.
If a template is set, then the template will be processed and put into the notification key in the payload. To see available fields and their description please follow FCM documentation.
Following is a sample android template with notification:
{
"android": {
"notification": {
"title": "{{ sender.name }} @ {{ channel.name }}",
"body": "{{ truncate message.text 200 }}",
"click_action": "OPEN_ACTIVITY_1",
"sound": "default"
},
"priority": "high"
}
}
APN default template:
{
"payload": {
"aps": {
"alert": {
"title": "You have a new message",
"body": "{{ truncate message.text 200 }}"
},
"badge": {{ unread_count }},
"sound": "default",
"mutable-content": 1,
"content-available": 0
},
"stream": {
"version": "v2",
"sender": "stream.chat",
"type": "{{ event_type }}",
"id": "{{ message.id }}",
"cid": "{{ channel.cid }}",
"receiver_id": "{{ receiver.id }}"
}
}
}
Limitations
There are some limitations that Stream imposes on the push notification handlebars template to make sure no malformed payloads are being sent to push providers.
1: Custom Arrays Can’t Be Indexed
For example, given the context:
{
"sender": {
"name": "Bob",
"some_array": ["foo", "bar"]
}
}
And the template:
"title": {{ sender.some_array.[0] }}
The rendered payload will be:
"title": ""
2: Interpolating Whole Lists and Objects Isn’t Allowed
For example, given the context:
{
"sender": {
"name": "bob",
"some_array": ["foo", "bar"],
"address": {
"street": "willow str"
}
}
}
And the template:
"title": "{{ sender.some_array }} {{ sender.address }}"
The rendered payload will be:
"title": "[] {}"
3: Unquoted fields that aren’t in the context will be rendered as empty strings
For example, given the context:
{
"sender": {
"name": "bob"
}
}
And the template:
"title": {{ sender.missing_field }}
The rendered payload will be:
"title": ""
Advanced Use Cases
For advanced use cases (e.g. A list of channel members in the notification title, conditional rendering, etc), Stream provides some handlebars helper functions.
Helper Functions
name | type | description |
---|---|---|
implodemembers | function | takes the list of channel members and implodes it into a single string, using a custom limit, separator and suffix. |
json | function | renders passed parameter as JSON (e.g {“channel”:{{{ json channel }}}} ) |
each | function | For loop. Use this to access the current variable, @index for the current index and @first and @last as convenience booleans to determine if the iteration is at its first/last element |
if | function | If function. Tests trueness of given parameter. Supports else statement. (e.g {{#if sender.name}}{{ sender.name }}{{/if}} ) |
unless | function | Unless function. Tests falseness of given parameter. Supports else statement. (e.g {{#unless sender.name}}Missing name{{/unless}} ) |
equal | function | Equality check function. Tests equality of the given 2 parameters. Supports else statement. (e.g {{#equal channel.type “messaging” }}This is the messaging channel{{else}}This is another channel{{/equal}} ) |
unequal | function | Inequality check function. Tests inequality of the given 2 parameters. Supports else statement. (e.g {{#unequal channel.type “messaging” }}This is another channel{{else}}This is the messaging channel{{/unequal}} ) |
ifLt | function | If less than. Supports else statement. |
ifLte | function | If less than or equal. Supports else statement. |
ifGt | function | If greater than. Supports else statement. |
ifGte | function | If greater than or equal. Supports else statement. |
remainder | function | Calculates the difference between the length of an array and an integer (e.g {{remainder otherMembers 2}} |
truncate | function | Truncate given text to given length (e.g {{ truncate message.text 2000 }}) |
Most of the functions above are straight forward, except for implodeMembers
, which will be detailed further.
The full function signature is: {{implodeMembers otherMembers|members [limit=] [separator=] [nameField=] [suffixFmt=]}}
Function Parameters
name | type | description | default |
---|---|---|---|
otherMembers | members | array | Which member array to implode |
limit | integer | How many member names to show before adding the suffix | 3 |
nameField | string | Field name from which field to retrieve the member’s name. Note: does not support nesting | name |
separator | string | Separator to use for channel members | , |
suffixFmt | string | Format string to use for the suffix. Note: only %d is allowed for formatting | and %d other(s) |
Examples
Let’s put these helpers to use in a few examples:
Example 1
Rendering channel members in the notification title. Each member’s name is stored in the fullName
field.
What we want to achieve:
{
"alert": {
"title": "Bob Jones, Jessica Wright, Tom Hadle and 4 other(s)",
"body": "Bob Jones: Hello there fellow channel members"
}
}
How we will achieve it: using implodeMembers
with a custom name field (leaving others empty so that defaults will be used):
{
"alert": {
"title": "{{implodeMembers otherMembers nameField="fullName"}}",
"body": "{{ sender.fullName }}: {{ message.text }}"
}
}
Example 2
Rendering channel members in the notification title. Each member’s name is stored in the nested details.name
field.
What we want to achieve:
{
"alert": {
"title": "Bob Jones, Jessica Wright, Tom Hadle and 4 other(s)",
"body": "Bob Jones: Hello there fellow channel members"
}
}
How we will achieve it: since implodeMembers
doesn’t support nested fields, we need to use a bunch of helpers such as each
, ifLte
. Note how the use of ~
will trim the whitespaces so that the title in rendered in a single row:
{
"alert": {
"title": "
{{~#each otherMembers}}
{{#ifLte @index 2}}
{{~this.details.name}}{{#ifLt @index 2 }}, {{/ifLt~}}
{{~else if @last~}}
{{{ " " }}} and {{remainder otherMembers 3}} other(s)
{{~/ifLte~}}
{{/each~}}",
"body": "{{ sender.details.name }}: {{ message.text }}"
}
Enabling and Customizing Push Templates
The Upsert Push Template REST endpoint allows you to enable push notifications for supported event types and optionally define custom templates.
Following is a sample payload for enabling push notifications with default template:
{
"enable_push": true,
"event_type": "message.new",
"push_provider_type": "apn",
"push_provider_name": "apn"
}
Following is a sample payload for enabling push notifications with custom template:
{
"enable_push": true,
"event_type": "message.new",
"push_provider_type": "firebase",
"push_provider_name": "firebase",
"template": "{\"data\":{\"version\":\"v2\",\"sender\":\"stream.chat\",\"type\":\"{{ event_type }}\",\"id\":\"{{ message.id }}\",\"message_id\":\"{{ message.id }}\",\"channel_type\":\"{{ channel.type }}\",\"channel_id\":\"{{ channel.id }}\",\"cid\":\"{{ channel.cid }}\",\"receiver_id\":\"{{ receiver.id }}\"},\"android\":{\"priority\":\"high\"},\"apns\":{\"payload\":{\"aps\":{\"alert\":{\"title\":\"New message from {{ sender.name }}\",\"body\":\"{{ truncate message.text 200 }}\"},\"badge\":{{ unread_count }},\"sound\":\"default\",\"mutable-content\":1,\"content-available\":0}}}}"
}