From f2b43e45529680d23f35e6ea66a1ca9ddae319c1 Mon Sep 17 00:00:00 2001 From: Simon Woolf Date: Wed, 6 May 2026 18:16:54 +0100 Subject: [PATCH 1/4] Document annotations.get() in the message annotations docs Adds a new "Retrieve annotations" section covering channel.annotations.get() with samples for JS, Node, Java, Swift, and Python, plus a note pointing users to channel.getMessage() when they only need the latest summary. Also promotes the "Annotation message properties" subheading to a top-level heading. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/pages/docs/messages/annotations.mdx | 70 ++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/src/pages/docs/messages/annotations.mdx b/src/pages/docs/messages/annotations.mdx index c3ab7ca093..19c6e4a8a5 100644 --- a/src/pages/docs/messages/annotations.mdx +++ b/src/pages/docs/messages/annotations.mdx @@ -799,9 +799,75 @@ await channel.annotations.subscribe(annotation_handler) ``` -### Annotation message properties +## Retrieve annotations -Annotations are a special type of message with the following properties: +To retrieve all annotations that have been published for a message, use the `annotations.get()` method on a channel. Pass in either a [message](/docs/messages) instance or the `serial` of the message. This returns a [paginated result](/docs/api/rest-sdk/types#paginated-result) containing every individual annotation event for that message, starting from the earliest. You can pass an optional `limit` to control the page size. + + + + +```javascript +// Retrieve all annotations for a message +const page = await channel.annotations.get(message, { limit: 100 }); +console.log(page.items); + +// You can also use a message's `serial` +const page2 = await channel.annotations.get(message.serial, { limit: 100 }); +``` + +```nodejs +// Retrieve all annotations for a message +const page = await channel.annotations.get(message, { limit: 100 }); +console.log(page.items); + +// You can also use a message's `serial` +const page2 = await channel.annotations.get(message.serial, { limit: 100 }); +``` + +```java +// Retrieve all annotations for a message +Param[] params = new Param[]{ new Param("limit", "100") }; +PaginatedResult page = channel.annotations.get(message, params); +for (Annotation annotation : page.items()) { + System.out.println(annotation); +} + +// You can also use a message's `serial` +PaginatedResult page2 = channel.annotations.get(message.serial, params); +``` + +```swift +// Retrieve all annotations for a message +let query = ARTAnnotationsQuery(limit: 100) +channel.annotations.get(for: message, query: query) { page, error in + guard let page = page else { return } + for annotation in page.items { + print(annotation) + } +} + +// You can also use a message's `serial` +channel.annotations.get(forMessageSerial: message.serial, query: query) { page, error in + // Handle result +} +``` + +```python +# Retrieve all annotations for a message +page = await channel.annotations.get(message, {'limit': 100}) +for annotation in page.items: + print(annotation) + +# You can also use a message's `serial` +page2 = await channel.annotations.get(message.serial, {'limit': 100}) +``` + + +## Annotation message properties + +Annotations have the following properties: | Property | Description | | -------- | ----------- | From 0f8dabe23af994649a2966808dfaeb2e00459059 Mon Sep 17 00:00:00 2001 From: Simon Woolf Date: Wed, 6 May 2026 18:45:08 +0100 Subject: [PATCH 2/4] Document annotations API in SDK reference docs Adds the annotations API surface to the realtime and rest SDK reference pages, which previously documented only the Message annotations property and a minimal MessageAnnotations entry. - Adds `RealtimeAnnotations` (publish/delete/get/subscribe/unsubscribe) and `RestAnnotations` (publish/delete/get) sections to the channels pages, plus a Channel.annotations property pointing to them. - Adds Annotation, OutboundAnnotation (JS/Cocoa), AnnotationAction, GetAnnotationsParams (JS), ARTAnnotationsQuery (Cocoa), and the per-language summary entry types (SummaryClientIdList, SummaryClientIdCounts, SummaryTotal, plus the Java `Summary` wrapper) to realtime-sdk/types.mdx. - Updates the MessageAnnotations.summary row to describe the typed structure per language rather than leaving it as JsonObject. - Restricts new content to languages that actually ship the API: JS, Node, Java, Swift, Objective-C, Python. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/pages/docs/api/realtime-sdk/channels.mdx | 236 +++++++++++++++++++ src/pages/docs/api/realtime-sdk/types.mdx | 222 ++++++++++++++++- src/pages/docs/api/rest-sdk/channels.mdx | 149 ++++++++++++ 3 files changed, 606 insertions(+), 1 deletion(-) diff --git a/src/pages/docs/api/realtime-sdk/channels.mdx b/src/pages/docs/api/realtime-sdk/channels.mdx index 163fafb434..3756535776 100644 --- a/src/pages/docs/api/realtime-sdk/channels.mdx +++ b/src/pages/docs/api/realtime-sdk/channels.mdx @@ -106,6 +106,14 @@ Provides access to the [Presence](/docs/presence-occupancy/presence) object for Provides access to the [PushChannel](/docs/api/realtime-sdk/push#push-channel) object for this channel which can be used to access members present on the channel, or participate in presence. + + +#### annotations + +Provides access to the [`RealtimeAnnotations`](#realtime-annotations) object for this channel, which can be used to publish, delete, retrieve and subscribe to [annotations](/docs/messages/annotations) on messages. + + + #### object @@ -1763,6 +1771,234 @@ Checks if a channel is in a given state and returns `null` if it is, else calls Returns a promise. If the channel is already in the given state, the promise will immediately resolve to `null`. If not, it will call [`once()`](#once) to return a promise which resolves the next time the channel transitions to the given state. + + +## RealtimeAnnotationsARTRealtimeAnnotationsio.ably.lib.realtime.RealtimeAnnotationsRealtimeAnnotations + +The `RealtimeAnnotations` object, accessed from the [annotations](#channel-annotations) property of a `Channel`, is used to publish, delete, retrieve, and subscribe to [annotations](/docs/messages/annotations) on messages. See the [annotations documentation](/docs/messages/annotations) for an overview of annotation concepts and types. + +### RealtimeAnnotations Methods + +#### publish + + + +`publish(Message message, Annotation annotation): Promise` + +`publish(String messageSerial, Annotation annotation): Promise` + + + + +`void publish(Message message, Annotation annotation)` + +`void publish(String messageSerial, Annotation annotation)` + +`void publish(Message message, Annotation annotation, CompletionListener listener)` + +`void publish(String messageSerial, Annotation annotation, CompletionListener listener)` + + + + +`publishForMessage(message: ARTMessage, annotation: ARTAnnotation, callback: ARTCallback?)` + +`publishForMessageSerial(messageSerial: String, annotation: ARTAnnotation, callback: ARTCallback?)` + + + + +`async publish(msg_or_serial, annotation: Annotation)` + + + +Publishes an [`Annotation`](/docs/api/realtime-sdk/types#annotation) for a message, identified either by a [`Message`](#message) instance or by its [`serial`](#serial). The annotation must include at least a `type`; other required fields depend on the annotation type. The `action` is set automatically to `annotation.create`. + +If a [`clientId`](/docs/api/realtime-sdk/types#client-options) is set on the client, it will be associated with the published annotation. Some annotation [summarization methods](/docs/messages/annotations#annotation-types) require an [identified](/docs/auth/identified-clients) client. + +##### Parameters + +| Parameter | Description | Type | +|-----------|-------------|------| +| msg_or_serialmessage or messageSerial | The message to annotate, identified either by a [`Message`](#message) instance or by its `serial` | [`ARTMessage`](#message) or `String`[`Message`](#message) or `String` | +| annotation | The annotation to publish. Must include at least the `type` | [`ARTAnnotation`](/docs/api/realtime-sdk/types#annotation)[`Annotation`](/docs/api/realtime-sdk/types#annotation) | + +#### delete + + + +`delete(Message message, Annotation annotation): Promise` + +`delete(String messageSerial, Annotation annotation): Promise` + + + + +`void delete(Message message, Annotation annotation)` + +`void delete(String messageSerial, Annotation annotation)` + +`void delete(Message message, Annotation annotation, CompletionListener listener)` + +`void delete(String messageSerial, Annotation annotation, CompletionListener listener)` + + + + +`deleteForMessage(message: ARTMessage, annotation: ARTAnnotation, callback: ARTCallback?)` + +`deleteForMessageSerial(messageSerial: String, annotation: ARTAnnotation, callback: ARTCallback?)` + + + + +`async delete(msg_or_serial, annotation: Annotation)` + + + +Publishes an annotation removal request for a message, removing the contribution that this `clientId` previously made to the [annotation summary](/docs/messages/annotations#annotation-summaries). The exact behaviour depends on the [annotation type](/docs/messages/annotations#annotation-types). The `action` is set automatically to `annotation.delete`. + +The annotation must include at least a `type`, and may include a `name` for those annotation types where the name is needed to identify which contribution to remove. + +##### Parameters + +The parameters are the same as for [`publish`](#annotations-publish). + +#### get + + + +`get(Message message, GetAnnotationsParams? params): Promise>` + +`get(String messageSerial, GetAnnotationsParams? params): Promise>` + + + + +`PaginatedResult get(Message message, Param[] params)` + +`PaginatedResult get(String messageSerial, Param[] params)` + +`PaginatedResult get(Message message)` + +`PaginatedResult get(String messageSerial)` + +`void getAsync(Message message, Param[] params, Callback> callback)` + +`void getAsync(String messageSerial, Param[] params, Callback> callback)` + + + + +`getForMessage(message: ARTMessage, query: ARTAnnotationsQuery, callback: ARTPaginatedAnnotationsCallback)` + +`getForMessageSerial(messageSerial: String, query: ARTAnnotationsQuery, callback: ARTPaginatedAnnotationsCallback)` + + + + +`async get(msg_or_serial, params: dict | None = None) -> PaginatedResult` + + + +Retrieves a [paginated result](#paginated-result) containing all individual [`Annotation`](/docs/api/realtime-sdk/types#annotation) events that have been published for a message, ordered from earliest to most recent. + +If you only need the latest [annotation summary](/docs/messages/annotations#annotation-summaries) for a message, prefer [`channel.getMessage()`](/docs/messages/updates-deletes#get): the returned message contains the up-to-date `annotations.summary`. Use `annotations.get()` only when you need the full list of raw annotations. + +##### Parameters + +| Parameter | Description | Type | +|-----------|-------------|------| +| msg_or_serialmessage or messageSerial | The message to retrieve annotations for, identified either by a [`Message`](#message) instance or by its `serial` | [`ARTMessage`](#message) or `String`[`Message`](#message) or `String` | +| queryparams | An optional set of restrictions on which annotations to retrieve, in particular a `limit` | [`GetAnnotationsParams`](/docs/api/realtime-sdk/types#get-annotations-params)[`Param[]`](#param)[`ARTAnnotationsQuery`](/docs/api/realtime-sdk/types#annotations-query)`dict` | + +##### Returns + +A [`PaginatedResult`](#paginated-result) containing an array of [`Annotation`](/docs/api/realtime-sdk/types#annotation) objects. + +#### subscribe + + + +`subscribe(messageCallback listener): Promise` + +`subscribe(String | String[] type, messageCallback listener): Promise` + + + + +`void subscribe(AnnotationListener listener)` + +`void subscribe(String type, AnnotationListener listener)` + + + + +`subscribe(callback: (ARTAnnotation) -> Void) -> ARTEventListener?` + +`subscribe(type: String, callback: (ARTAnnotation) -> Void) -> ARTEventListener?` + + + + +`async subscribe(listener)` + +`async subscribe(type_or_types, listener)` + + + +Registers a listener that is called each time an individual [`Annotation`](/docs/api/realtime-sdk/types#annotation) is received on the channel. + +To receive individual realtime annotations (instead of just the rolled-up [summaries](/docs/messages/annotations#annotation-summaries)), the channel must have requested the `ANNOTATION_SUBSCRIBE` [channel mode](/docs/channels/options#modes). Most clients will not need this and can rely on the summary updates instead. + +##### Parameters + +| Parameter | Description | Type | +|-----------|-------------|------| +| type | An optional annotation `type` (or array of types) to filter the listener on. If omitted, the listener is called for all annotations on the channel | `String` or `String[]` | +| listener | A function called with each received annotation | `(annotation: Annotation) => void``AnnotationListener``(ARTAnnotation) -> Void``callable` | + +#### unsubscribe + + + +`unsubscribe(): void` + +`unsubscribe(messageCallback listener): void` + +`unsubscribe(String | String[] type, messageCallback listener): void` + + + + +`void unsubscribe(AnnotationListener listener)` + +`void unsubscribe(String type, AnnotationListener listener)` + + + + +`unsubscribe()` + +`unsubscribe(listener: ARTEventListener)` + +`unsubscribe(type: String, listener: ARTEventListener)` + + + + +`unsubscribe()` + +`unsubscribe(listener)` + +`unsubscribe(type_or_types, listener)` + + + +Deregisters annotation listeners. Called with no arguments, removes all listeners. Called with a `listener`, removes that listener from all types it was registered for. Called with a `type` and a `listener`, removes the listener from that type only. + + + ## Related types ### ChannelStateARTRealtimeChannelStateChannel::STATE Enumio.ably.lib.realtime.ChannelState EnumIO.Ably.Realtime.ChannelState Enumably.ChannelState Enum diff --git a/src/pages/docs/api/realtime-sdk/types.mdx b/src/pages/docs/api/realtime-sdk/types.mdx index c762b5ed16..26b073f3b4 100644 --- a/src/pages/docs/api/realtime-sdk/types.mdx +++ b/src/pages/docs/api/realtime-sdk/types.mdx @@ -583,7 +583,227 @@ An `Array` of [`Message`](/docs/api/realtime-sdk/types#message) objects | Property | Description | Type | |----------|-------------|------| -| summary | An object whose keys are annotation types, and the values are aggregated summaries for that annotation type | `Record` | +| summary | An object whose keys are [annotation types](/docs/messages/annotations#annotation-types), and whose values are aggregated [summary entries](#summary-entry) for that annotation type. The structure of each value depends on the summarization method, for example a `total.v1` entry will have a `total` field, while a `flag.v1` entry will have `total` and `clientIds` fields. See [annotation summaries](/docs/messages/annotations#annotation-summaries) for details | `Record`[`Summary`](#summary)`NSDictionary``Dict[str, Dict]``Record` | + + + +### AnnotationARTAnnotationio.ably.lib.types.AnnotationAnnotation + +An `Annotation` represents an individual annotation event for a message — either an `annotation.create` or `annotation.delete`. + +The same type is used both for publishing an annotation and for receiving one. When publishing, only `type` is required; some annotation types additionally need `name` (and `count` for `multiple.v1`), and `data` is optional. The remaining fields are server-set, and are populated by Ably on annotations received via [`channel.annotations.subscribe`](/docs/api/realtime-sdk/channels#annotations-subscribe) or [`channel.annotations.get`](/docs/api/realtime-sdk/channels#annotations-get). + +#### PropertiesMembersAttributes + +| Property | Description | Type | +|----------|-------------|------| +| typetype | The [annotation type](/docs/messages/annotations#annotation-types) (for example `reactions:distinct.v1`). Required when publishing | `String` | +| namename | The name of the annotation, used by some annotation types for aggregation | `String` | +| countcount | An optional count, only relevant to the `multiple.v1` annotation type | `int``Integer``Integer``NSNumber` | +| datadata | An optional payload for the annotation. Available on individual annotations but not aggregated or included in [annotation summaries](/docs/messages/annotations#annotation-summaries) | `Any``Object``Any``Any` | +| extrasextras | Metadata and/or ancillary payloads, if provided. Valid payloads include [`push`](/docs/push/publish#payload), `headers` (a map of strings to strings for arbitrary customer-supplied metadata), [`ephemeral`](/docs/pub-sub/advanced#ephemeral), and [`privileged`](/docs/platform/integrations/skip-integrations) objects | `JSONObject`, `JSONArray``JSON Object``Dictionary`, `Array``NSDictionary *`, `NSArray *` | +| idid | A unique ID assigned by Ably to this annotation. Server-set | `String` | +| clientIdclient_id | The client ID of the publisher of this annotation. Server-set, from the publishing client's connection | `String` | +| timestamptimestamp | The timestamp of when the annotation was received by Ably, as milliseconds since the Unix epoch. Server-set | `Integer``Long Integer``NSDate` | +| actionaction | Whether this is an annotation being added or removed. Set automatically by the SDK when publishing or deleting | [`AnnotationAction`](#annotation-action) | +| serialserial | This annotation's unique serial (lexicographically totally ordered). Server-set | `String` | +| messageSerialmessageSerialmessage_serial | The serial of the message that this annotation is annotating. Set automatically by the SDK from the [`Message`](#message) or `messageSerial` argument passed to publish/delete | `String` | +| encodingencoding | This is typically empty, as all annotations received from Ably are automatically decoded client-side using this value. However, if the annotation encoding cannot be processed, this attribute contains the remaining transformations not applied to the `data` payload | `String` | + + + + + +### AnnotationActionARTAnnotationActionio.ably.lib.types.AnnotationActionAnnotationAction + +An enum identifying whether an annotation event is a creation or a deletion. + + + + +```javascript + const AnnotationActions = [ + 'annotation.create', + 'annotation.delete', + ] +``` + + + + + + +```nodejs + const AnnotationActions = [ + 'annotation.create', + 'annotation.delete', + ] +``` + + + + + + +```java + public enum AnnotationAction { + ANNOTATION_CREATE, // 0 + ANNOTATION_DELETE, // 1 + } +``` + + + + + + +```objc + typedef NS_ENUM(NSUInteger, ARTAnnotationAction) { + ARTAnnotationActionCreate, + ARTAnnotationActionDelete, + }; +``` + + + + + + +```swift + enum ARTAnnotationAction : UInt { + case create + case delete + } +``` + + + + + + +```python + class AnnotationAction(Enum): + ANNOTATION_CREATE = 'annotation.create' + ANNOTATION_DELETE = 'annotation.delete' +``` + + + + + + + + +### GetAnnotationsParams + +The parameters object accepted by [`channel.annotations.get`](/docs/api/realtime-sdk/channels#annotations-get). + +| Property | Description | Type | +|----------|-------------|------| +| limit | An upper limit on the number of annotations returned. The default is 100, and the maximum is 1000 | `Integer` | + + + + + +### ARTAnnotationsQuery + +The query object accepted by [`channel.annotations.get`](/docs/api/realtime-sdk/channels#annotations-get). Constructed via `ARTAnnotationsQuery(limit:)`. + +| Property | Description | Type | +|----------|-------------|------| +| limit | An upper limit on the number of annotations returned. The default is 100, and the maximum is 1000 | `NSUInteger` | + + + + + +### Summary entry types + +The values held in [`MessageAnnotations.summary`](#message-annotations) depend on the [summarization method](/docs/messages/annotations#annotation-types) of the annotation type. The shape of each entry corresponds to one of the following. + + + +The TypeScript type [`SummaryEntry`](#summary-entry) is a union of these shapes. + + + + +In Java, [`MessageAnnotations.summary`](#message-annotations) is exposed as a [`Summary`](#summary) object that holds the raw `JsonObject` for each annotation type. To extract a strongly-typed entry from the [`Summary`](#summary), use one of the static converters on the `Summary` class: + +| Converter | Returns | +|-----------|---------| +| `Summary.asSummaryDistinctV1(JsonObject)` | `Map` | +| `Summary.asSummaryUniqueV1(JsonObject)` | `Map` | +| `Summary.asSummaryMultipleV1(JsonObject)` | `Map` | +| `Summary.asSummaryFlagV1(JsonObject)` | [`SummaryClientIdList`](#summary-client-id-list) | +| `Summary.asSummaryTotalV1(JsonObject)` | [`SummaryTotal`](#summary-total) | + + + + +In Objective-C and Swift, [`MessageAnnotations.summary`](#message-annotations) is exposed as a loose `NSDictionary`. To extract a strongly-typed entry, use one of the global parser functions: + +| Parser | Returns | +|--------|---------| +| `ARTSummaryDistinctV1(NSDictionary)` | `NSDictionary` | +| `ARTSummaryUniqueV1(NSDictionary)` | `NSDictionary` | +| `ARTSummaryMultipleV1(NSDictionary)` | `NSDictionary` | +| `ARTSummaryFlagV1(NSDictionary)` | [`ARTSummaryClientIdList`](#summary-client-id-list) | +| `ARTSummaryTotalV1(NSDictionary)` | [`ARTSummaryTotal`](#summary-total) | + + + + +In Python the summary is a plain `dict`. The shapes below describe the structure rather than dedicated classes. + + + +#### SummaryClientIdListARTSummaryClientIdListio.ably.lib.types.SummaryClientIdListflag.v1 entry + +The summary entry for `flag.v1`, and the per-name value for `distinct.v1` and `unique.v1`. + +| Property | Description | Type | +|----------|-------------|------| +| total | The total number of clients who have published an annotation with this name (or type, depending on context) | `Integer``int``NSInteger` | +| clientIds | A list of the clientIds of all clients who have published an annotation with this name (or type, depending on context) | `String[]``List``NSArray``List[str]` | +| clipped | Whether the list of clientIds has been clipped due to exceeding the maximum number of clients | `Boolean``boolean``BOOL` | + +#### SummaryClientIdCountsARTSummaryClientIdCountsio.ably.lib.types.SummaryClientIdCountsmultiple.v1 entry + +The per-name value for the `multiple.v1` annotation type. + +| Property | Description | Type | +|----------|-------------|------| +| total | The sum of the counts from all clients who have published an annotation with this name | `Integer``int``NSInteger` | +| clientIds | A map from clientIds to the count each of those clients has contributed | `Record``Map``NSDictionary``Dict[str, int]` | +| totalUnidentifiedtotalUnidentified | The sum of counts contributed by unidentified clients (which are not included in the `clientIds` map) | `Integer``int``NSInteger` | +| clipped | Whether the `clientIds` map has been clipped due to exceeding the maximum number of clients | `Boolean``boolean``BOOL` | +| totalClientIdstotalClientIds | The total number of distinct identified clients (equal to the size of `clientIds` if `clipped` is false) | `Integer``int``NSInteger` | + +#### SummaryTotalARTSummaryTotalio.ably.lib.types.SummaryTotaltotal.v1 entry + +The summary entry for the `total.v1` annotation type. + +| Property | Description | Type | +|----------|-------------|------| +| total | The total number of annotations of this type that have been published for the message | `Integer``int``NSInteger` | + + + +#### io.ably.lib.types.Summary + +A wrapper around the loose `Map` representation of [`MessageAnnotations.summary`](#message-annotations). + +| Member | Description | Type | +|--------|-------------|------| +| `JsonObject get(String annotationType)` | Returns the raw JSON for the given annotation type, or `null` | `JsonObject` | + +Use the static converter methods listed [above](#summary-entry) (`Summary.asSummaryDistinctV1`, `Summary.asSummaryFlagV1`, etc.) to turn a `JsonObject` into a strongly-typed entry. + + + + ### PublishResult diff --git a/src/pages/docs/api/rest-sdk/channels.mdx b/src/pages/docs/api/rest-sdk/channels.mdx index abe67b05d1..bea30de1d4 100644 --- a/src/pages/docs/api/rest-sdk/channels.mdx +++ b/src/pages/docs/api/rest-sdk/channels.mdx @@ -86,6 +86,14 @@ Provides access to the [REST Presence](/docs/presence-occupancy/presence) object Provides access to the [PushChannel](/docs/api/realtime-sdk/push#push-channel) object for this channel which can be used to access members present on the channel, or participate in presence. + + +#### annotations + +Provides access to the [`RestAnnotations`](#rest-annotations) object for this channel, which can be used to publish, delete, and retrieve [annotations](/docs/messages/annotations) on messages. + + + #### object @@ -813,6 +821,147 @@ On failure to retrieve message versions, the callback receives an [`ARTErrorInfo + + +## RestAnnotationsARTRestAnnotationsio.ably.lib.rest.RestAnnotationsRestAnnotations + +The `RestAnnotations` object, accessed from the [annotations](#channel-annotations) property of a `Channel`, is used to publish, delete, and retrieve [annotations](/docs/messages/annotations) on messages over REST. See the [annotations documentation](/docs/messages/annotations) for an overview of annotation concepts and types. + +### RestAnnotations Methods + +#### publish + + + +`publish(Message message, Annotation annotation): Promise` + +`publish(String messageSerial, Annotation annotation): Promise` + + + + +`void publish(Message message, Annotation annotation)` + +`void publish(String messageSerial, Annotation annotation)` + +`void publishAsync(Message message, Annotation annotation, CompletionListener listener)` + +`void publishAsync(String messageSerial, Annotation annotation, CompletionListener listener)` + + + + +`publishForMessage(message: ARTMessage, annotation: ARTAnnotation, callback: ARTCallback?)` + +`publishForMessageSerial(messageSerial: String, annotation: ARTAnnotation, callback: ARTCallback?)` + + + + +`async publish(msg_or_serial, annotation: Annotation)` + + + +Publishes an [`Annotation`](/docs/api/realtime-sdk/types#annotation) for a message, identified either by a [`Message`](#message) instance or by its [`serial`](#serial). The annotation must include at least a `type`. The `action` is set automatically to `annotation.create`. + +##### Parameters + +| Parameter | Description | Type | +|-----------|-------------|------| +| msg_or_serialmessage or messageSerial | The message to annotate, identified either by a [`Message`](#message) instance or by its `serial` | [`ARTMessage`](#message) or `String`[`Message`](#message) or `String` | +| annotation | The annotation to publish. Must include at least the `type` | [`ARTAnnotation`](/docs/api/realtime-sdk/types#annotation)[`Annotation`](/docs/api/realtime-sdk/types#annotation) | + +#### delete + + + +`delete(Message message, Annotation annotation): Promise` + +`delete(String messageSerial, Annotation annotation): Promise` + + + + +`void delete(Message message, Annotation annotation)` + +`void delete(String messageSerial, Annotation annotation)` + +`void deleteAsync(Message message, Annotation annotation, CompletionListener listener)` + +`void deleteAsync(String messageSerial, Annotation annotation, CompletionListener listener)` + + + + +`deleteForMessage(message: ARTMessage, annotation: ARTAnnotation, callback: ARTCallback?)` + +`deleteForMessageSerial(messageSerial: String, annotation: ARTAnnotation, callback: ARTCallback?)` + + + + +`async delete(msg_or_serial, annotation: Annotation)` + + + +Publishes an annotation removal request for a message. The `action` is set automatically to `annotation.delete`. See the [annotations documentation](/docs/messages/annotations#delete) for behaviour details. + +##### Parameters + +The parameters are the same as for [`publish`](#annotations-publish). + +#### get + + + +`get(Message message, GetAnnotationsParams? params): Promise>` + +`get(String messageSerial, GetAnnotationsParams? params): Promise>` + + + + +`PaginatedResult get(Message message, Param[] params)` + +`PaginatedResult get(String messageSerial, Param[] params)` + +`PaginatedResult get(Message message)` + +`PaginatedResult get(String messageSerial)` + +`void getAsync(Message message, Param[] params, Callback> callback)` + +`void getAsync(String messageSerial, Param[] params, Callback> callback)` + + + + +`getForMessage(message: ARTMessage, query: ARTAnnotationsQuery, callback: ARTPaginatedAnnotationsCallback)` + +`getForMessageSerial(messageSerial: String, query: ARTAnnotationsQuery, callback: ARTPaginatedAnnotationsCallback)` + + + + +`async get(msg_or_serial, params: dict | None = None) -> PaginatedResult` + + + +Retrieves a [paginated result](#paginated-result) containing all individual [`Annotation`](/docs/api/realtime-sdk/types#annotation) events that have been published for a message, ordered from earliest to most recent. + +##### Parameters + +| Parameter | Description | Type | +|-----------|-------------|------| +| msg_or_serialmessage or messageSerial | The message to retrieve annotations for, identified either by a [`Message`](#message) instance or by its `serial` | [`ARTMessage`](#message) or `String`[`Message`](#message) or `String` | +| queryparams | An optional set of restrictions on which annotations to retrieve, in particular a `limit` | [`GetAnnotationsParams`](/docs/api/realtime-sdk/types#get-annotations-params)[`Param[]`](/docs/api/rest-sdk/types#param)[`ARTAnnotationsQuery`](/docs/api/realtime-sdk/types#annotations-query)`dict` | + +##### Returns + +A [`PaginatedResult`](#paginated-result) containing an array of [`Annotation`](/docs/api/realtime-sdk/types#annotation) objects. + + + ## Related types ### MessageARTMessageAbly::Models::MessageAbly\Models\Messageio.ably.lib.types.MessageIO.Ably.Message From 1168c442b87d503dcd2329d64f16aac3a74e6370 Mon Sep 17 00:00:00 2001 From: Simon Woolf Date: Thu, 7 May 2026 13:03:14 +0100 Subject: [PATCH 3/4] Address Copilot review on annotations docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Drop the #summary-entry intra-page link for languages where the Summary entry types section is not rendered (csharp, go, flutter, ruby, php), so the row prose still works without a broken anchor. - Clarify which Annotation fields are user-settable when publishing vs server- or SDK-populated, rather than calling all non-(type, name, count, data) fields "server-set" — extras and encoding are user-settable, and action / messageSerial are SDK-set on publish. - Switch the messages/annotations.mdx paginated-result link from rest-sdk/types to realtime-sdk/types, matching the wider docs convention for cross-product guide pages (14 vs 6 references). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/pages/docs/api/realtime-sdk/types.mdx | 4 ++-- src/pages/docs/messages/annotations.mdx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/docs/api/realtime-sdk/types.mdx b/src/pages/docs/api/realtime-sdk/types.mdx index 26b073f3b4..5017487d97 100644 --- a/src/pages/docs/api/realtime-sdk/types.mdx +++ b/src/pages/docs/api/realtime-sdk/types.mdx @@ -583,7 +583,7 @@ An `Array` of [`Message`](/docs/api/realtime-sdk/types#message) objects | Property | Description | Type | |----------|-------------|------| -| summary | An object whose keys are [annotation types](/docs/messages/annotations#annotation-types), and whose values are aggregated [summary entries](#summary-entry) for that annotation type. The structure of each value depends on the summarization method, for example a `total.v1` entry will have a `total` field, while a `flag.v1` entry will have `total` and `clientIds` fields. See [annotation summaries](/docs/messages/annotations#annotation-summaries) for details | `Record`[`Summary`](#summary)`NSDictionary``Dict[str, Dict]``Record` | +| summary | An object whose keys are [annotation types](/docs/messages/annotations#annotation-types), and whose values are aggregated [summary entries](#summary-entry)summary entries for that annotation type. The structure of each value depends on the summarization method, for example a `total.v1` entry will have a `total` field, while a `flag.v1` entry will have `total` and `clientIds` fields. See [annotation summaries](/docs/messages/annotations#annotation-summaries) for details | `Record`[`Summary`](#summary)`NSDictionary``Dict[str, Dict]``Record` | @@ -591,7 +591,7 @@ An `Array` of [`Message`](/docs/api/realtime-sdk/types#message) objects An `Annotation` represents an individual annotation event for a message — either an `annotation.create` or `annotation.delete`. -The same type is used both for publishing an annotation and for receiving one. When publishing, only `type` is required; some annotation types additionally need `name` (and `count` for `multiple.v1`), and `data` is optional. The remaining fields are server-set, and are populated by Ably on annotations received via [`channel.annotations.subscribe`](/docs/api/realtime-sdk/channels#annotations-subscribe) or [`channel.annotations.get`](/docs/api/realtime-sdk/channels#annotations-get). +The same type is used both for publishing an annotation and for receiving one. When publishing, only `type` is required; some annotation types additionally need `name` (and `count` for `multiple.v1`), and `data`, `extras`, and `encoding` are optional user-set fields. The other fields (`id`, `clientId`, `timestamp`, `serial`, `messageSerial`, `action`) are server- or SDK-populated, and are present on annotations received via [`channel.annotations.subscribe`](/docs/api/realtime-sdk/channels#annotations-subscribe) or [`channel.annotations.get`](/docs/api/realtime-sdk/channels#annotations-get). See the per-field descriptions below for details. #### PropertiesMembersAttributes diff --git a/src/pages/docs/messages/annotations.mdx b/src/pages/docs/messages/annotations.mdx index 19c6e4a8a5..b1a5a7a99f 100644 --- a/src/pages/docs/messages/annotations.mdx +++ b/src/pages/docs/messages/annotations.mdx @@ -801,7 +801,7 @@ await channel.annotations.subscribe(annotation_handler) ## Retrieve annotations -To retrieve all annotations that have been published for a message, use the `annotations.get()` method on a channel. Pass in either a [message](/docs/messages) instance or the `serial` of the message. This returns a [paginated result](/docs/api/rest-sdk/types#paginated-result) containing every individual annotation event for that message, starting from the earliest. You can pass an optional `limit` to control the page size. +To retrieve all annotations that have been published for a message, use the `annotations.get()` method on a channel. Pass in either a [message](/docs/messages) instance or the `serial` of the message. This returns a [paginated result](/docs/api/realtime-sdk/types#paginated-result) containing every individual annotation event for that message, starting from the earliest. You can pass an optional `limit` to control the page size.