+```javascript
+// Create an Ably Realtime client specifying the clientId that will
+// be associated with annotations published by this client
+const realtime = new Ably.Realtime({ key: '{{API_KEY}}', clientId: 'my-client-id' });
+
+// Create a channel in a namespace called `annotations`
+// which has message annotations enabled
+const channel = realtime.channels.get('annotations:example');
+
+// Publish an annotation for a message that flags it as delivered
+await channel.annotations.publish(message, {
+ type: 'receipts:flag.v1',
+ name: 'delivered'
+});
+
+// You can also use a message's serial number
+await channel.annotations.publish(message.serial, {
+ type: 'receipts:flag.v1',
+ name: 'delivered'
+});
+```
+
+```nodejs
+// Create an Ably Realtime client specifying the clientId that will
+// be associated with annotations published by this client
+const realtime = new Ably.Realtime({ key: '{{API_KEY}}', clientId: 'my-client-id' });
+
+// Create a channel in a namespace called `annotations`
+// which has message annotations enabled
+const channel = realtime.channels.get('annotations:example');
+
+// Publish an annotation for a message that flags it as delivered
+await channel.annotations.publish(message, {
+ type: 'receipts:flag.v1',
+ name: 'delivered'
+});
+
+// You can also use a message's serial number
+await channel.annotations.publish(message.serial, {
+ type: 'receipts:flag.v1',
+ name: 'delivered'
+});
+```
+
+
+You can also specify a payload on the `data` field when publishing an annotation. While the `data` payload is available on the [individual annotation](#subscribe-individual-annotations) events, it is not included in [annotation summaries](#annotation-summaries).
+
+
+```javascript
+await channel.annotations.publish(message.serial, {
+ type: 'receipts:flag.v1',
+ name: 'delivered',
+ data: 'Message delivered!'
+});
+```
+
+```nodejs
+await channel.annotations.publish(message.serial, {
+ type: 'receipts:flag.v1',
+ name: 'delivered',
+ data: 'Message delivered!'
+});
+```
+
+
+When using the `multiple.v1` summarization method you can optionally specify a `count` by which to increment this client's contribution to the summary:
+
+
+```javascript
+await channel.annotations.publish(message.serial, {
+ type: 'rating:multiple.v1',
+ name: 'stars',
+ count: 4
+});
+```
+
+```nodejs
+await channel.annotations.publish(message.serial, {
+ type: 'rating:multiple.v1',
+ name: 'stars',
+ count: 4
+});
+```
+
+
+
+## Deleting annotations
+
+To delete a published annotation, use the `annotations.delete()` method on a channel, passing either a [message](/docs/messages) instance or the `serial` of the message to annotate. This method will publish a delete annotation message with an action of `annotation.delete`.
+
+Deleting an annotation does not remove the original annotation that was published. Instead, annotation delete messages affect the [annotation summary](#annotation-summaries) for that message by removing the contribution as specified by the delete annotation.
+
+The `clientId` specified in the [client options](/docs/api/realtime-sdk#client-options) will be associated with the published delete annotation.
+
+
+
+When publishing a delete annotation, specify the [annotation type](#annotation-types) on the `type` field of the annotation object.
+
+You can also optionally specify a `name` for the annotation which is used by certain [summarization methods](#annotation-summaries) that aggregate by `name`.
+
+
+```javascript
+// Create an Ably Realtime client specifying the clientId that will
+// be associated with annotations published by this client
+const realtime = new Ably.Realtime({ key: '{{API_KEY}}', clientId: 'my-client-id' });
+
+// Create a channel in a namespace called `annotations`
+// which has message annotations enabled
+const channel = realtime.channels.get('annotations:example');
+
+// Delete a 'delivered' annotation
+await channel.annotations.delete(message.serial, {
+ type: 'receipts:flag.v1',
+ name: 'delivered'
+});
+```
+
+```nodejs
+// Create an Ably Realtime client specifying the clientId that will
+// be associated with annotations published by this client
+const realtime = new Ably.Realtime({ key: '{{API_KEY}}', clientId: 'my-client-id' });
+
+// Create a channel in a namespace called `annotations`
+// which has message annotations enabled
+const channel = realtime.channels.get('annotations:example');
+
+// Delete a 'delivered' annotation
+await channel.annotations.delete(message.serial, {
+ type: 'receipts:flag.v1',
+ name: 'delivered'
+});
+```
+
+
+
+## Subscribing to annotation updates
+
+There are two ways to subscribe to annotation updates:
+
+1. **Annotation summaries** (recommended) - Receive aggregated summaries of all annotations for a message
+2. **Individual annotations** - Receive individual events when annotations are published or deleted
+
+### Subscribing to annotation summaries
+
+The recommended way to receive annotation updates is through annotation summaries. These events provide a summary of the complete, current state of all annotations for a message whenever annotations are published or deleted.
+
+Annotation summaries are delivered to subscribers as messages with an `action` of `message.summary`. These messages have a `summary` field which provides a summary of all the annotations for the message identified by the `serial` field on the summary message.
+
+The value of the `summary` field is an object where the keys are the [annotation types](#annotation-types). The exact structure of the value of each key depends on the [summarization method](#annotation-summaries) specified in the annotation type.
+
+
+```javascript
+// Create an Ably Realtime client specifying the clientId that will
+// be associated with annotations published by this client
+const realtime = new Ably.Realtime({ key: '{{API_KEY}}', clientId: 'my-client-id' });
+
+// Create a channel in a namespace called `annotations`
+// which has message annotations enabled
+const channel = realtime.channels.get('annotations:example');
+
+await channel.subscribe((message) => {
+ if (message.action === 'message.summary') {
+ console.log(message.summary);
+ }
+});
+```
+
+```nodejs
+// Create an Ably Realtime client specifying the clientId that will
+// be associated with annotations published by this client
+const realtime = new Ably.Realtime({ key: '{{API_KEY}}', clientId: 'my-client-id' });
+
+// Create a channel in a namespace called `annotations`
+// which has message annotations enabled
+const channel = realtime.channels.get('annotations:example');
+
+await channel.subscribe((message) => {
+ if (message.action === 'message.summary') {
+ console.log(message.summary);
+ }
+});
+```
+
+
+
+
+For more details on summary structure and usage, see [annotation summaries](#annotation-summaries).
+
+### Subscribing to individual annotations
+
+For more fine-grained visibility, you can also subscribe directly to individual annotation events using the `annotations.subscribe()` method on a channel. To subscribe to individual annotations, you must request the `ANNOTATION_SUBSCRIBE` [mode](/docs/channels/options#modes).
+
+Annotations delivered to the `annotations.subscribe()` listener will have an `action` of `annotation.create` or `annotation.delete`.
+
+
+```javascript
+// Create an Ably Realtime client specifying the clientId that will
+// be associated with annotations published by this client
+const realtime = new Ably.Realtime({ key: '{{API_KEY}}', clientId: 'my-client-id' });
+
+// Create a channel in a namespace called `annotations`
+// which has message annotations enabled.
+// Specify the ANNOTATION_SUBSCRIBE mode to enable annotation subscriptions.
+const channel = realtime.channels.get('annotations:example', { modes: ['ANNOTATION_SUBSCRIBE'] });
+
+await channel.annotations.subscribe((annotation) => {
+ if (annotation.action === 'annotation.create') {
+ console.log(`New ${annotation.type} annotation with name ${annotation.name} from ${annotation.clientId}`);
+ } else if (annotation.action === 'annotation.delete') {
+ console.log(`${annotation.clientId} deleted a ${annotation.type} annotation with name ${annotation.name}`);
+ }
+});
+```
+
+```nodejs
+// Create an Ably Realtime client specifying the clientId that will
+// be associated with annotations published by this client
+const realtime = new Ably.Realtime({ key: '{{API_KEY}}', clientId: 'my-client-id' });
+
+// Create a channel in a namespace called `annotations`
+// which has message annotations enabled.
+// Specify the ANNOTATION_SUBSCRIBE mode to enable annotation subscriptions.
+const channel = realtime.channels.get('annotations:example', { modes: ['ANNOTATION_SUBSCRIBE'] });
+
+await channel.annotations.subscribe((annotation) => {
+ if (annotation.action === 'annotation.create') {
+ console.log(`New ${annotation.type} annotation with name ${annotation.name} from ${annotation.clientId}`);
+ } else if (annotation.action === 'annotation.delete') {
+ console.log(`${annotation.clientId} deleted a ${annotation.type} annotation with name ${annotation.name}`);
+ }
+});
+```
+
+
+
+
+
+## Annotation types
+
+Annotation types determine how annotations are processed and aggregated into [summaries](#annotation-summaries).
+
+The annotation type is a string of the format `namespace:summarization.version` where:
+
+- `namespace` is a string (e.g. `reactions`) that groups related annotations. Only annotations in the same namespace will be aggregated together to produce [summaries](#annotation-summaries).
+- `summarization` specifies the summarization method (e.g. `flag`) which determines how annotations are aggregated to produce [summaries](#annotation-summaries).
+- `version` specifies the version component of the summarization method (e.g. `v1`), which allows for future changes to summarization behavior.
+
+## Annotation summaries
+
+When annotations for a message are published, Ably automatically generates a summary that provides an aggregated view of all annotations for that message.
+
+A separate summary is produced for each distinct [annotation type](#annotation-types). The summarization method specified in the [annotation type](#annotation-types) determines how annotations in the same namespace for a given message are aggregated into a summary. A summary is constructed from the set of `annotation.create` and `annotation.delete` messages that have been published for a message.
+
+You can [subscribe](#subscribe-annotation-summaries) to these summaries using the `subscribe()` method on a channel, and they will be delivered as messages with an `action` of `message.summary`. The summary will be included on the message's `summary` field, which is an object whose keys are the [annotation types](#annotation-types) and whose values describe the annotation summary for that type:
+
+
+```json
+{
+ "metrics:total.v1": {
+ "total": 42
+ },
+ "reactions:flag.v1": {
+ "total": 3,
+ "clientIds": ["client1", "client2", "client3"]
+ },
+ "categories:distinct.v1": {
+ "important": {
+ "total": 2,
+ "clientIds": ["client1", "client3"]
+ },
+ "urgent": {
+ "total": 3,
+ "clientIds": ["client1", "client2", "client3"]
+ }
+ },
+ "status:unique.v1": {
+ "important": {
+ "total": 2,
+ "clientIds": ["client1", "client3"]
+ },
+ "urgent": {
+ "total": 1,
+ "clientIds": ["client2"]
+ }
+ },
+ "voting:multiple.v1": {
+ "option-a": {
+ "total": 7,
+ "clientCounts": {
+ "client1": 3,
+ "client2": 2
+ },
+ "totalUnidentified": 2
+ },
+ "option-b": {
+ "total": 4,
+ "clientCounts": {
+ "client1": 2,
+ "client3": 1
+ },
+ "totalUnidentified": 1
+ }
+ }
+}
+```
+
+
+
+When [publishing](#publish) an annotation you can optionally specify a `name` which is used by some summarization methods to produce a summary.
+
+Ably provides five summarization methods to support different use cases which are described below.
+
+### Total
+
+The `total.v1` summarization method counts the number of annotations of a given type that were [published](#publish) for a message.
+
+[Deleting](#delete) an annotation decrements the total count for that message.
+
+The `total.v1` summarization method does not attribute counts to individual clients in the summary; it maintains only a simple count per [annotation type](#annotation-types) and does not organize counts by name.
+
+If the same client publishes an annotation of a given type to the same message twice, the `total` count is incremented twice.
+
+```javascript
+{
+ "metrics:total.v1": {
+ "total": 42
+ }
+}
+```
+
+
+
+
+### Flag
+
+The `flag.v1` summarization method counts how many distinct clients have [published](#publish) an annotation of a given type and maintains a list of those `clientId`s.
+
+[Deleting](#delete) an annotation decrements the total count for that message and removes the `clientId` from the list of clients that contributed to the summary.
+
+A given client can contribute to the summary only once per [annotation type](#annotation-type).
+
+```javascript
+{
+ "reactions:flag.v1": {
+ "total": 3,
+ "clientIds": ["client1", "client2", "client3"]
+ }
+}
+```
+
+
+
+
+### Distinct
+
+The `distinct.v1` summarization method counts how many unique clients have [published](#publish) an annotation with a given `name` for each annotation type along with the corresponding list of `clientId`s that published it.
+
+A given client can contribute to the summary for a particular annotation `name` only once, but the same client may publish additional annotations with different `name`s.
+
+```javascript
+{
+ "categories:distinct.v1": {
+ "important": {
+ "total": 2,
+ "clientIds": ["client1", "client3"]
+ },
+ "urgent": {
+ "total": 3,
+ "clientIds": ["client1", "client2", "client3"]
+ }
+ }
+}
+```
+
+[Deleting](#delete) an annotation removes the `clientId` from the list of clients that contributed to the summary for that `name`, and decrements the total count for that `name`.
+
+
+
+### Unique
+
+The `unique.v1` summarization method counts how many unique clients have [published](#publish) an annotation with a given `name` for each annotation type, while guaranteeing that each client contributes to the summary for only one `name` at a time. The summary for each annotation `name` holds a `total` count of the number of distinct clients that have pubilshed an annotation with that `name` along with the corresponding list of `clientId`s that published it.
+
+A given client can contribute to the summary for a particular annotation `name` only once. Publishing an annotation with a different `name` automatically removes that client from the summary for the previous `name` and adds them to the new one, updating the affected total values and list of `clientId`s.
+
+```javascript
+{
+ "status:unique.v1": {
+ "important": {
+ "total": 2,
+ "clientIds": ["client1", "client3"]
+ },
+ "urgent": {
+ "total": 1,
+ "clientIds": ["client2"]
+ }
+ }
+}
+```
+
+[Deleting](#delete) an annotation removes the `clientId` from the list of clients that contributed to the summary for that `name`, and decrements the total count for that `name`.
+
+
+
+### Multiple
+
+The `multiple.v1` summarization method counts both a total and a per-client count of the number of annotations that were [published](#publish) with a given `name` for each annotation type. Additionally it includes a count for the number of annotations published with a given `name` from unidentified clients.
+
+A given client can contribute to the summary for a particular annotation `name` multiple times. The same client may also publish additional annotations with different `name`s.
+
+If a client specifies a `count` when publishing an annotation, the client's contribution to the summary is incremented by the specified value.
+
+```javascript
+{
+ "voting:multiple.v1": {
+ "option-a": {
+ "total": 7,
+ "clientCounts": {
+ "client1": 3,
+ "client2": 2
+ },
+ "totalUnidentified": 2
+ },
+ "option-b": {
+ "total": 4,
+ "clientCounts": {
+ "client1": 2,
+ "client3": 1
+ },
+ "totalUnidentified": 1
+ }
+ }
+}
+```
+
+[Deleting](#delete) an annotation removes all contributions made by that `clientId` for that `name`.
+
+