Push Notifications - Publishing Notifications

Ably provides two models for delivering push notifications to devices. Channel-based broadcasting provides automatic fan-out capabilities for push notifications, and direct publishing provides a means to deliver individual notifications to devices.


Push Notifications in Ably

Channel-based broadcasting

The model for delivering push notifications to devices over channels is intentionally very similar to how normal messages are delivered to realtime subscribers using Ably’s pub/sub channels. For example, a normal message published on an Ably channel is broadcast immediately to all subscribers of that channel. When broadcasting push notifications on channels, however, the process is the same with the exception that the subscribers (devices receiving push notifications) are registered in advance using our API and the message itself must contain an extra push notification payload that specifies the optional visual format and optional data payload of the native push notification.

Therefore, the process for delivering push notifications to devices using channel-based broadcasting is as follows:

  1. Subscribe one or more devices to one or more channels
  2. Publish a message on those channels with a push notification payload

Please note that a push notification published on a channel will only be delivered to a device if:

  • the extra push notification payload is included in the published message
  • a channel rule is configured explicitly enabling push notifications on that channel
  • the device is subscribed to the channel
  • the push notification payload is compatible with the subscribed push notification device

Channel-based push notification example

Push notifications are sent as special payloads alongside a normal Ably message in the extras field. The extras field is an object and must contain a push attribute object with the push payload details.

ARTMessage *message = [[ARTMessage alloc] initWithName:@"example" data:@"rest data"];
message.extras = @{
    @"push": @{
        @"notification": @{
            @"title": @"Hello from Ably!",
            @"body": @"Example push notification from Ably."
        },
        @"data": @{
            @"foo": @"bar",
            @"baz": @"qux"
        }
    }
};
[[rest.channels get:@"pushenabled:foo"] publish:@[message]];
var message = ARTMessage(name: "example", data: "rest data")
message.extras = [
    "push": [
        "notification": [
            "title": "Hello from Ably!",
            "body": "Example push notification from Ably."
        ],
        "data": [
            "foo": "bar",
            "baz": "qux"
        ]
    ]
]
rest.channels.get("pushenabled:foo").publish([message])
Message message = new Message("example", "rest data");
message.extras = io.ably.lib.util.JsonUtils.object()
    .add("push", io.ably.lib.util.JsonUtils.object()
        .add("notification", io.ably.lib.util.JsonUtils.object()
            .add("title", "Hello from Ably!")
            .add("body", "Example push notification from Ably."))
        .add("data", io.ably.lib.util.JsonUtils.object()
            .add("foo", "bar")
            .add("baz", "qux")));

rest.channels.get("pushenabled:foo").publish(message);
extras = {
  push: {
    notification: {
      title: 'Hello from Ably!',
      body: 'Example push notification from Ably.'
    },
    data: {
      foo: 'bar',
      baz: 'qux'
    }
  }
}

channel = rest.channels.get('pushenabled:foo')
channel.publish('example', 'data', extras: extras)
var extras = {
  push: {
    notification: {
      title: 'Hello from Ably!',
      body: 'Example push notification from Ably.'
    },
    data: {
      foo: 'bar',
      baz: 'qux'
    }
  }
};

var channel = rest.channels.get('pushenabled:foo');
channel.publish({ name: 'example', data: 'data', extras: extras });
var extras = {
  push: {
    notification: {
      title: 'Hello from Ably!',
      body: 'Example push notification from Ably.'
    },
    data: {
      foo: 'bar',
      baz: 'qux'
    }
  }
};

var channel = rest.channels.get('pushenabled:foo');
channel.publish({ name: 'example', data: 'data', extras: extras });
extras = {
  'push': {
    'notification': {
      'title': 'Hello from Ably!',
      'body': 'Example push notification from Ably.'
    }
  },
}

channel = rest.channels.get('pushenabled:foo')
channel.publish({ 'name': 'example', 'data': 'data', 'extras': extras });
$msg = new Message();
$msg->name = 'name';
$msg->data = 'data';
$msg->extras = [
  'push' => [
    'notification' => [
      'title' => 'Hello from Ably!',
      'body' => 'Example push notification from Ably.'
    ]
  ]
];
$channel = $rest->channels->get('pushenabled:foo';
$channel->publish($msg);

Direct publishing

Ably provides an API that allows native push notifications to be delivered directly to:

  • Devices identified by their unique device ID
  • Devices identified by their assigned clientId
  • Devices identified by their native recipient attributes such as their unique registrationToken in the case of GCM, deviceToken in the case of APNS, or targetUrl and encryptionKey in the case of a Web device (experimental). This is particularly useful when migrating to Ably with existing push notification target devices.

See the push admin publish documentation for the client library API details, and the raw push publish REST API documentation for information on the underlying direct publishing endpoint used by the client libraries.

Publish to a device ID example

var recipient = {
  deviceId: 'xxxxxxxxxxx'
};
var data = {
  notification: {
    title: 'Hello from Ably!',
    body: 'Example push notification from Ably.'
  }
};

rest.push.admin.publish(recipient, data);
var recipient = {
  deviceId: 'xxxxxxxxxxx'
};
var data = {
  notification: {
    title: 'Hello from Ably!',
    body: 'Example push notification from Ably.'
  }
};

rest.push.admin.publish(recipient, data);
recipient = {
  device_id: 'xxxxxxxxxxx'
}
data = {
  notification: {
    title: 'Hello from Ably!',
    body: 'Example push notification from Ably.'
  }
}

rest.push.admin.publish(recipient, data)
ARTPushRecipient *recipient = @{
    @"deviceId": @"xxxxxxxxxxxxxx"
};
ARTJsonObject *data = @{
    @"notification": @{
        @"title": @"Hello from Ably!",
        @"body": @"Example push notification from Ably."
    },
    @"data": @{
        @"foo": @"bar",
        @"baz": @"qux"
    }
};
[rest.push.admin publish:recipient data:data callback:^(ARTErrorInfo *error)
Message message = new Message("example", "rest data");
message.extras = io.ably.lib.util.JsonUtils.object()
    .add("push", io.ably.lib.util.JsonUtils.object()
        .add("notification", io.ably.lib.util.JsonUtils.object()
            .add("title", "Hello from Ably!")
            .add("body", "Example push notification from Ably."))
        .add("data", io.ably.lib.util.JsonUtils.object()
            .add("foo", "bar")
            .add("baz", "qux")));

rest.push.admin.publish(arrayOf(Param("deviceId", deviceId)), message);
let recipient: [String: Any] = [
    "deviceId": "xxxxxxxxxxxxxx"
]
let data: [String: Any] = [
    "notification": [
        "title": "Hello from Ably!",
        "body": "Example push notification from Ably."
    ],
    "data": [
        "foo": "bar",
        "baz": "qux"
    ]
]
rest.push.admin.publish(recipient, data: data)
recipient = {'deviceId': 'xxxxxxxxxxxx'}
message = {
  'push': {
    'notification': {
      'title': 'Hello from Ably!',
      'body': 'Example push notification from Ably.'
    }
  }
}

rest.push.admin.publish(recipient, message)
$recipient = [ 'deviceId' => 'xxxxxxxxxxxx' ];
$data = [ 'push' =>
          [ 'notification' =>
            [ 'title' => 'Hello from Ably!',
              'body' => 'Example push notification from Ably.'
            ]
          ]
        ];
$rest->push->admin->publish( $recipient, $data );

Publish to a client ID example

var recipient = {
  clientId: 'bob'
};
var notification = {
  notification: {
    title: 'Hello from Ably!',
    body: 'Example push notification from Ably.'
  }
};

rest.push.admin.publish(recipient, notification);
var recipient = {
  clientId: 'bob'
};
var notification = {
  notification: {
    title: 'Hello from Ably!',
    body: 'Example push notification from Ably.'
  }
};

rest.push.admin.publish(recipient, notification);
recipient = {
  client_id: 'bob'
}
notification = {
  notification: {
    title: 'Hello from Ably!',
    body: 'Example push notification from Ably.'
  }
}

rest.push.admin.publish(recipient, notification)
ARTPushRecipient *recipient = @{
    @"clientId": @"xxxxxxxxxxxxxx"
};
ARTJsonObject *data = @{
    @"notification": @{
        @"title": @"Hello from Ably!",
        @"body": @"Example push notification from Ably."
    },
    @"data": @{
        @"foo": @"bar",
        @"baz": @"qux"
    }
};
[rest.push.admin publish:recipient data:data callback:^(ARTErrorInfo *error)
let recipient: [String: Any] = [
    "clientId": "xxxxxxxxxxxxxx"
]
let data: [String: Any] = [
    "notification": [
        "title": "Hello from Ably!",
        "body": "Example push notification from Ably."
    ],
    "data": [
        "foo": "bar",
        "baz": "qux"
    ]
]
rest.push.admin.publish(recipient, data: data)
Message message = new Message("example", "rest data");
message.extras = io.ably.lib.util.JsonUtils.object()
    .add("push", io.ably.lib.util.JsonUtils.object()
        .add("notification", io.ably.lib.util.JsonUtils.object()
            .add("title", "Hello from Ably!")
            .add("body", "Example push notification from Ably."))
        .add("data", io.ably.lib.util.JsonUtils.object()
            .add("foo", "bar")
            .add("baz", "qux")));

rest.push.admin.publish(arrayOf(Param("clientId", clientId)), message);
recipient = {'clientId': 'xxxxxxxxxxxx'}
message = {
  'push': {
    'notification': {
      'title': 'Hello from Ably!',
      'body': 'Example push notification from Ably.'
    }
  }
}

rest.push.admin.publish(recipient, message)
$recipient = [ 'clientId' => 'xxxxxxxxxxx' ];
$data = [ 'push' =>
          [ 'notification' =>
            [ 'title' => 'Hello from Ably!',
              'body' => 'Example push notification from Ably.'
            ]
          ]
        ];
$rest->push->admin->publish( $recipient, $data );

Publish direct to a native recipient example

recipient = {
  transport_type: 'apns',
  device_token: 'xxxxxxxxxx'
}
notification = {
  notification: {
    title: 'Hello from Ably!',
    body: 'Example push notification from Ably.'
  }
}

rest.push.admin.publish(recipient, notification)
var recipient = {
  transportType: 'apns',
  deviceToken: 'xxxxxxxxxx'
};
var notification = {
  notification: {
    title: 'Hello from Ably!',
    body: 'Example push notification from Ably.'
  }
};

rest.push.admin.publish(recipient, notification);
var recipient = {
  transportType: 'apns',
  deviceToken: 'xxxxxxxxxx'
};
var notification = {
  notification: {
    title: 'Hello from Ably!',
    body: 'Example push notification from Ably.'
  }
};

rest.push.admin.publish(recipient, notification);
ARTPushRecipient *recipient = @{
    @"transportType": @"apns",
    @"deviceToken": @"XXXXXXXX"
};

ARTJsonObject *data = @{
    @"notification": @{
        @"title": @"Hello from Ably!",
        @"body": @"Example push notification from Ably."
    },
    @"data": @{
        @"foo": @"bar",
        @"baz": @"qux"
    }
};
[rest.push.admin publish:recipient data:data callback:^(ARTErrorInfo *error)
let recipient: [String: Any] = [
    "transportType": "apns",
    "deviceToken": "XXXXXXXX"
]

let data: [String: Any] = [
    "notification": [
        "title": "Hello from Ably!",
        "body": "Example push notification from Ably."
    ],
    "data": [
        "foo": "bar",
        "baz": "qux"
    ]
]
rest.push.admin.publish(recipient, data: data)
Message message = new Message("example", "rest data");
message.extras = io.ably.lib.util.JsonUtils.object()
    .add("push", io.ably.lib.util.JsonUtils.object()
        .add("notification", io.ably.lib.util.JsonUtils.object()
            .add("title", "Hello from Ably!")
            .add("body", "Example push notification from Ably."))
        .add("data", io.ably.lib.util.JsonUtils.object()
            .add("foo", "bar")
            .add("baz", "qux")));

rest.push.admin.publish(arrayOf(Param("transportType", "apns"), Param("deviceToken", deviceToken)), message);
recipient = {'transportType': 'apns', 'deviceToken': 'XXXXXXX'}
message = {
  'push': {
    'notification': {
      'title': 'Hello from Ably!',
      'body': 'Example push notification from Ably.'
    }
  }
}

rest.push.admin.publish(recipient, message)
$recipient = [  'transportType' => 'apns', 'deviceToken' => 'XXXXXXX' ];
$data = [ 'push' =>
          [ 'notification' =>
            [ 'title' => 'Hello from Ably!',
              'body' => 'Example push notification from Ably.'
            ]
          ]
        ];
$rest->push->admin->publish( $recipient, $data );

Push payload structure

A push notification payload has a generic structure as follows:

{
  "notification": {
    "title": <string, title to display at the notification>,
    "body": <string, text below title on the expanded notification>,
    "icon": <string, platform-specific>,
    "sound": <string, platform-specific>,
    "collapseKey": <string, platform-specific, used to group notifications together>
  },
  "data": {
    <key string>: <value string>,
    ...
  }
}

Depending on the transport (APNs, FCM, etc.), the following transformations are made automatically by Ably to make each field compatible with the target push notification transport:

Ably field FCM APNs Web (experimental)
notification.title notification.title aps.alert.title notification.title
notification.body notification.body aps.alert.body notification.body
notification.icon notification.icon Discarded. notification.icon
notification.sound notification.sound aps.alert.sound notification.sound
notification.collapseKey collapse_key aps.thread-id notification.collapseKey
data data Merged into root object. data

So for example, a push payload in a message published to Ably as follows:

{
  "notification": {
    "collapseKey": "chat"
  }
}

would be delivered in raw format to FCM as:

{
  "collapse_key": "chat"
}

and would be delivered in raw format to APNs as:

{
  "aps.thread-id": "chat"
}

and would be delivered in raw format to a web push target (experimental) as:

{
  "notification": {
    "collapseKey": "chat"
  }
}

Additionally, you can set transport-specific attributes which will get merged into the root object resulting from generic mapping explained above only when pushing to the selected transport. This way, you can:

  • Override the generic value for that field.
  • Add specific fields that are not supported in the generic structure.

To do this, alongside notification and data, add an object whose field is one of:

  • fcm, for FCM.
  • apns, for APNs.
  • web, for Web Notifications (experimental).

Here’s an example of a push payload that overrides the default title for APNs iOS and sets the FCM Android-specific color field:

{
  "notification": {
    "title": "Hello from Ably!",
    "body": "Example push notification from Ably."
  },
  "data": {
    "foo": "bar",
    "baz": "qux"
  },
  "apns": {
    "aps": {
      "alert": {
        "title": "Hello to iOS from Ably!"
      }
    }
  },
  "fcm": {
    "notification": {
      "color": "#d3d3d3"
    }
  }
}

API reference
Documentation

Ready to get started?

Our free plan includes 3m messages per month, 100 peak connections, 100 peak channels, and loads of features.