| title | Chat Features | |||
|---|---|---|---|---|
| section | client-lib-development-guide | |||
| index | 55 | |||
| jump_to |
|
This document outlines the feature specification for the Ably Chat product.
The key words “must”, “must not”, “required”, “shall”, “shall not”, “should”, “should not”, “recommended”, “may”, and “optional” (whether lowercased or uppercased) in this document are to be interpreted as described in RFC 2119.
(CHA-V1)Specification Version: This document defines the Ably chat library features specification (‘features spec’).(CHA-V1a)The current version of the Chat library feature specification is version1.0.0.
This specification has undergone the following major revisions, stemming from a review process. A major revision is understood to mean a significant public API change that affects a wide-range of functionality or implementation in the library. Specification points that were removed due to a major review/revision are marked as such.
- Single Channel Migration: CHADR-097, March 2025
- V1 API Review: CHADR-101, May 2025
- Pre-V1 Error Code Review: CHA-1207, October 2025
- V1 released (no changes), October 2025
(CHA-GP1)As far as is practicable, the implementation details such as underlying Realtime Channels should be hidden from the public API. This allows developers to interact with Chat without having to understand many low-level primitives.(CHA-GP2)The public API should avoid implicit operations as a side-effect to some other operation. For example, adding a subscriber to messages in a Chat Room should not automatically trigger that Room to attach. This is in contrast to the current core SDKs. Avoiding side-effects provides a clean, easy to understand API.(CHA-GP2a)[Testable]Whenever the Chat SDK fetches a realtime channel, it must do so with theattachOnSubscribechannel option set tofalse.
(CHA-GP3)Wherever possible, Chat features should be exposed in the public API as properties of their parent. For example,messageswould be considered a property of aroomobject. This allows for greater composability and extensibility in the future.(CHA-GP4)Avoid overloading methods and optional parameters. Prefer object-type parameters wherever practical and idiomatic.(CHA-GP5)When raising anErrorInfo, avoid relying on the generic40000and50000codes.(CHA-GP5a)Prefer codes already defined inably-common, for example – useInvalidArgumentfor an invalid argument passed to a function.(CHA-GP5b)If the error is chat-specific, define a new code in the102000 - 103000range reserved for chat.
(CHA-GP6)Error messages must follow the standard formatunable to <op>; <reason>.(CHA-GP6a)Error messages must be written assuming that the audience is the customer developer, not an Ably engineer.
(CHA-IN1)In order to initialize a chat client, the user must supply an instance ofRealtimeClient. Upon initialization, the chat client must configure this client in order to allow Ably to track usage of the Chat SDK, as described in the sub-items below.(CHA-IN1a)[Testable]Upon initialization of the chat client, it must create a wrapper SDK proxy client by callingcreateWrapperSDKProxyon theRealtimeClient.(CHA-IN1b)[Testable]When creating theCHA-IN1awrapper SDK proxy client, the chat client must pass aWrapperSDKProxyOptionswhoseagentsdescribe the Chat SDK and its version. For example,{ "agents": { "chat-js": "1.0.1" } }.(CHA-IN1b1)[Testable]When SDKs utilise themselves as a wrapper (e.g. JS in the context of React), both agents must be specified. For example,{ "agents": { "chat-js": "1.0.1", "chat-react": "1.0.1" } }.
(CHA-IN1c)SDK authors must ensure that they have registered the agents used byCHA-IN1bin the common repository, perRSC7d5.(CHA-IN1d)[Testable]The chat client must use the wrapper SDK proxy client created inCHA-IN1aas the realtime client on which it performs all subsequent actions (for example, fetching channels or calling#request).(CHA-IN1e)[Testable]WhereWrapperSDKProxyOptionsdoes not exist, these agents must be injected into the Realtime client’s agents properties, as well as channel params onchannels.getcalls.
(CHA-CL1)[Testable]TheChatClientprovides adisposemethod to clean up all resources and prepare the client for garbage collection.(CHA-CL1a)[Testable]Whendisposeis called on aChatClient, it must first dispose of all rooms. This will release all rooms currently in use.(CHA-CL1b)[Testable]After disposing of rooms, theChatClientmust dispose of the connection instance.(CHA-CL1c)This operation is naturally asynchronous, however many languages use synchronous destructor sequences. Therefore each language should implement whatever is most idiomatic, and the exact implementation details are left up to each individual SDK.
A Room is the atomic unit of Chat. All chat operations are performed in the context of a room that a client is currently attached to. As the Room is the atomic unit, its state should be considered as the combination of the states of underlying resources.
The connection status of the Chat client closely reflects the status of the underlying Realtime client.
(CHA-CS1)Every chat client has astatus, which describes the current status of the connection.(CHA-CS1a)TheINITIALIZEDstatus is a default status when the realtime client is first initialized. This value may be seen if the realtime client doesn’t have autoconnect turned on.(CHA-CS1b)TheCONNECTINGstatus is used when the client is in the process of connecting to Ably servers.(CHA-CS1c)TheCONNECTEDstatus is used when the client connected to Ably servers.(CHA-CS1d)TheDISCONNECTEDstatus is used when the client is not currently connected to Ably servers. This state may be temporary as the underlying Realtime SDK seeks to reconnect.(CHA-CS1e)TheSUSPENDEDstatus is used when the client is in an extended state of disconnection, but will attempt to reconnect.(CHA-CS1f)TheFAILEDstatus is used when the client is disconnected from the Ably servers due to some non-retriable failure such as authentication failure. It will not attempt to reconnect.(CHA-CS1g)TheCLOSINGstatus is used when a user has calledcloseon the underlying Realtime client and it is attempting to close the connection with Ably. The library will not attempt to reconnect.(CHA-CS1h)TheCLOSEDstatus is used when theclosecall on the underlying Realtime client has succeeded, either via mutual agreement with the server or forced after a time out. The library will not attempt to reconnect.
(CHA-CS2)The connection must expose its current status.(CHA-CS2a)[Testable]The chat client must expose its current connection status, a single value from the list inCHA-CS1.(CHA-CS2b)[Testable]The chat client must expose the latest error, if any, associated with its current status.
(CHA-CS3)[Testable]The initial status and error of the connection must be whatever status the realtime client returns whilst the connection status object is constructed.(CHA-CS4)The chat client must allow its connection status to be observed by clients.(CHA-CS4a)[Testable]Connection status update events must contain the newly entered connection status.(CHA-CS4b)[Testable]Connection status update events must contain the previous connection status.(CHA-CS4c)[Testable]Connection status update events must contain the connection error (if any) that pertains to the newly entered connection status.(CHA-CS4d)[Testable]Clients must be able to register a listener for connection status events and receive such events.(CHA-CS4e)[Testable]Clients must be able to unregister a listener for connection status events and from that point onwards, cease to receive such events on that listener.(CHA-CS4f)This specification point has been removed. It was valid until the V1 API Review.
(CHA-CS5)The chat client must monitor the underlying realtime connection for connection status changes.(CHA-CS5a)This specification point has been removed. It was valid until the V1 API Review.(CHA-CS5a1)This specification point has been removed. It was valid until the V1 API Review.(CHA-CS5a2)This specification point has been removed. It was valid until the V1 API Review.(CHA-CS5a3)This specification point has been removed. It was valid until the V1 API Review.(CHA-CS5a4)This specification point has been removed. It was valid until the V1 API Review.
(CHA-CS5b)This specification point has been removed. It was valid until the V1 API Review.(CHA-CS5c)[Testable]If a connection state event is observed from the underlying realtime library, the client must emit a status change event. The current status of that event shall reflect the status change in the underlying realtime library, along with the accompanying error.
The status of any given Chat Room is determined by the state of the underlying realtime channel. For more information on this, see Room Lifecycle.
(CHA-RS1)Every room has astatus, which describes the current status of the room.(CHA-RS1a)TheINITIALIZEDstatus is the initial status before any attachment operations have taken place.(CHA-RS1b)TheATTACHINGstatus is used when the room is in the process of attaching to Ably.(CHA-RS1c)TheATTACHEDstatus means that the room is fully connected to Ably and functioning normally.(CHA-RS1d)TheDETACHINGstatus is used when the room is in the process of detaching from Ably.(CHA-RS1e)TheDETACHEDstatus means that the room has been detached from Ably by a user-initiated request. It will not attempt to re-connect without explicit intervention.(CHA-RS1f)TheSUSPENDEDstatus is when the room has been detached from Ably for an extended period of time. The room will periodically attempt to reconnect.(CHA-RS1g)TheFAILEDstatus means that something has gone wrong with the room (such as lack of permissions). The room will not attempt to connect to Ably and will require user intervention.(CHA-RS1h)TheRELEASINGstatus means that the room is being released and the underlying resources are being cleaned up.(CHA-RS1i)TheRELEASEDstatus means that the room has been cleaned up and the object can no longer be used.(CHA-RS1j)TheINITIALIZINGstatus is reserved for React uses, and should not be included outside of the JS/TS SDK.
(CHA-RS2)A room must expose its current status.(CHA-RS2a)[Testable]A room must expose its current status, a single value from the list provided above.(CHA-RS2b)A room must expose the latest error, if any, associated with its current status.(CHA-RS2b1)[Testable]If an error is associated with the current status, then this must be exposed.(CHA-RS2b2)[Testable]If there is no error is associated with the current status, then the room status should not expose any errors.
(CHA-RS3)[Testable]A newly created room must have a current status ofINITIALIZED.(CHA-RS4)A room must allow changes to room status to be observed by clients.(CHA-RS4a)[Testable]Room status update events must contain the newly entered room status.(CHA-RS4b)[Testable]Room status update events must contain the previous room status.(CHA-RS4c)[Testable]Room status update events must not contain an error, where that error pertains to the current status.(CHA-RS4d)[Testable]Room status update events must contain an error, where that error pertains to the current status.(CHA-RS4e)[Testable]Clients must be able to register a listener for room status updates and receive such events.(CHA-RS4f)[Testable]Clients must be able to unregister a listener for room status updates and from that point onwards, cease to receive such events on that listener only.(CHA-RS4g)This specification point has been removed. It was valid until the V1 API Review.
(CHA-RS5)[Testable]The initial status and error of the connection must be whatever status the realtime client returns whilst the connection status object is constructed.
Rooms are considered the atomic units of chat, and each is underpinned by a dedicated realtime channel.
There are three room lifecycle operations: ATTACH, DETACH, RELEASE.
Discontinuities in Realtime connections can happen – whereby continuity of message delivery is disrupted. Therefore, a room and its associated features may also experience discontinuity events – where the user may need to take some action to restore continuity in their application. In Chat, we explicitly tell the user when there’s a discontinuity, rather than require them to implement the monitoring themselves.
(CHA-RL13)The room lifecycle shall maintain ahasAttachedOnceflag, which represents whether or not the nextATTACHEDchannel state change is the first one received (i.e. the product of a call toATTACH). It defaults tofalse. Subsequent specification items will explain how this flag is manipulated.(CHA-RL14)The room lifecycle shall maintain aisExplicitlyDetachedflag, which represents whether or not the current roomDETACHEDstatus is the result of a user-initiateddetachoperation. It defaults tofalse. Subsequent specification items will explain how this flag is manipulated.(CHA-RL1)A room must be explicitly attached via theATTACHoperation.(CHA-RL1a)[Testable]If the room is already in theATTACHEDstatus, then this operation is no-op.(CHA-RL1j)This specification point has been removed.(CHA-RL1b)This specification point has been removed, it was valid up until the single-channel migration.(CHA-RL1c)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-RL1l)[Testable]If the room is in theRELEASEDstatus, the operation shall be rejected with anErrorInfowith theRoomInInvalidStateerror code from the chat-specific error codes.(CHA-RL1d)[Testable]If a room lifecycle operation is already in progress, this operation shall wait until the current operation completes before continuing, subject toCHA-RL7.(CHA-RL1e)[Testable]Notwithstanding the above points, when the attachment operation begins, the room shall be transitioned to theATTACHINGstatus before attempting to attach to the realtime channel.(CHA-RL1k)[Testable]Following theCHA-RL1etransition, the client shall initiate and await the attachment of the realtime channel.(CHA-RL1k1)[Testable]If the attachment is successful, the room status shall be transitioned toATTACHED, theisExplicitlyDetachedflag shall be set tofalseand thehasAttachedOnceflags set totrue.(CHA-RL1k2)[Testable]If the attachment is unsuccessful (i.e.channel.attach()throws an error), the attach error is theErrorInfofrom the underlying channel operation. The current channel state shall be used to determine the new room status, which will be enacted, accompanied by the attach error.(CHA-RL1k3)[Testable]If the attachment is unsuccessful (i.e.channel.attach()throws an error), the attach error will be thrown as the result of the roomattach()operation.
(CHA-RL1f)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL1g)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL1g1)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL1g2)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL1g3)This specification point has been removed. It was valid up until the single-channel migration.
(CHA-RL1h)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL1h1)This clause has been deleted.(CHA-RL1h2)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL1h3)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL1h4)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL1h5)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL1h6)This specification point has been removed. It was valid up until the single-channel migration.
(CHA-RL1i)This specification point has been removed. It was superseded by CHA-RC1f.(CHA-RL1i1)This specification point has been removed. It was made redundant by the introduction ofCHA-RC1.
(CHA-RL2)A room must be explicitly detached via theDETACHoperation.(CHA-RL2i)[Testable]If a room lifecycle operation is already in progress, this operation shall wait until the current operation completes before continuing, subject toCHA-RL7.(CHA-RL2a)[Testable]If the room status is alreadyDETACHED, then this operation is a no-op.(CHA-RL2b)[Testable]This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL2c)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-RL2l)[Testable]If the room is in theRELEASEDstatus, the operation shall be rejected with anErrorInfowith theRoomInInvalidStateerror code from the chat-specific error codes.(CHA-RL2d)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-RL2m)[Testable]If the room is in theFAILEDstatus, the operation shall be rejected with anErrorInfowithRoomInInvalidStateerror code from the chat-specific error codes.(CHA-RL2j)[Testable]Notwithstanding the above points, when the detachment operation begins, the room shall be transitioned to theDETACHINGstatus before attempting to detach from the realtime channel.(CHA-RL2k)[Testable]Following theCHA-RL2jtransition, the client shall initiate and await the detachment of the realtime channel.(CHA-RL2k1)[Testable]If the detachment is successful, the room status shall be transitioned toDETACHED, and theisExplicitlyDetachedflag set totrue.(CHA-RL2k2)[Testable]If the detachment is unsuccessful (i.e.channel.detach()throws an error), the detach error is theErrorInfofrom the underlying channel operation. The current channel state shall be used to determine the new room status, which will be enacted, accompanied by the detach error.(CHA-RL2k3)[Testable]If the detachment is unsuccessful (i.e.channel.detach()throws an error), the detach error will be thrown as the result of the roomdetach()operation.
(CHA-RL2e)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL2f)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL2g)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL2h)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL2h1)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL2h2)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL2h3)This specification point has been removed. It was valid up until the single-channel migration.
(CHA-RL3)A room must be explicitly released via theRELEASEoperation. This operation is not performed directly on a Room object by the client, but is described here.(CHA-RL3k)[Testable]If a room lifecycle operation is already in progress, this operation shall wait until the current operation completes before continuing, subject toCHA-RL7.(CHA-RL3a)[Testable]If the room is already in theRELEASEDstatus, this operation is no-op.(CHA-RL3b)[Testable]If the room is in theDETACHEDstatus, then the room is immediately transitioned toRELEASEDand the operation succeeds.(CHA-RL3j)[Testable]If the room is in theINITIALIZEDstatus, then the room is immediately transitioned toRELEASEDand the operation succeeds.(CHA-RL3c)This specification point has been removed.(CHA-RL3i)This specification point has been removed.(CHA-RL3m)[Testable]When the release operation commences, the room will transition into theRELEASINGstatus.(CHA-RL3n)AfterCHA-RL3m, the room enters a detach loop.(CHA-RL3n1)[Testable]If the channel state isFAILED, the client shall immediately breaks from the loop.(CHA-RL3n2)[Testable]The client shall initiate and await achannel.detach()operation. If this operation succeeds, the client shall exit the loop.(CHA-RL3n3)[Testable]If thechannel.detach()operation fails, and the realtime channel state isFAILED(checked after the failure), the loop shall terminate.(CHA-RL3n4)[Testable]If thechannel.detach()operation fails otherwise, the client shall wait a short time (250ms) and restart the loop fromCHA-RL3n1.
(CHA-RL3o)[Testable]Upon operation completion, the room status shall be transitioned toRELEASED.(CHA-RL3h)[Testable]Upon operation completion, the underlying Realtime Channel shall be released from the core SDK to prevent leakage.(CHA-RL3l)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL3d)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL3e)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL3f)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL3g)This specification point has been removed. It was valid up until the single-channel migration.
(CHA-RL7)Room lifecycle operations are atomic and exclusive operations: one operation must complete (whether that’s failure or success) before the next one may begin.(CHA-RL7a)[Testable]Room lifecycle operations have a precedence. If multiple operations are scheduled to run, they run in the following order:(CHA-RL7a1)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL7a2)1. TheRELEASEoperation – Highest priority.(CHA-RL7a3)2. TheATTACHandDETACHoperations have equal precedence.
(CHA-RL9)Many operations in the Chat SDK will still attempt to proceed if the room is in anATTACHINGstatus. However, their ability to proceed will depend on the subsequent status that the room takes. This specification point is defined here to avoid unnecessary repetition. It is primarily testable via the points that refer to it.(CHA-RL9a)[Testable]When the room status isATTACHINGat the point of operation commencement, the caller will subscribe a one-time listener to the room status.(CHA-RL9b)[Testable]If the next room status received by the caller isATTACHED, the operation shall proceed as normal.(CHA-RL9c)[Testable]If the next room status received is any other value, the operation must throw an error. TheErrorInfoshall have theRoomInInvalidStateerror code from the chat-specific error codes. Thecausefield must contain any error associated with the room status change, if present.(CHA-RL9d)[Testable]Once execution has resumed, the client MAY check the room status again, to handle spurious changes or race conditions.
(CHA-RL10)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL5)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL5a)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL5a1)This specification point has been removed. It was valid up until the single-channel migration.
(CHA-RL5b)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL5c)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL5d)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL5e)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL5f)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL5f1)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL5f2)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL5f3)This specification point has been removed. It was valid up until the single-channel migration.
(CHA-RL6)This specification point has been removed. It was superseded byCHA-RL8.(CHA-RL6a)This specification point has been removed. It was superseded byCHA-RL8.(CHA-RL8)This specification point has been removed. It was a duplicate ofCHA-RS3.
As well as user-initiated operations, the room must monitor its underlying resources and act accordingly. This includes handling disconnections from the server, as well as discontinuities in channel messages.
(CHA-RL4)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL4a)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL4a1)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL4a2)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL4a3)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL4a4)This specification point has been removed. It was valid up until the single-channel migration.
(CHA-RL4b)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL4b1)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL4b2)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL4b3)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL4b4)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL4b5)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL4b6)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL4b7)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL4b8)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL4b9)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RL4b10)This specification point has been removed. It was valid up until the single-channel migration.
(CHA-RL11)The room must monitor the underlying realtime channel state, and update the room status accordingly.(CHA-RL11a)[Testable]The room will subscribe to non-UPDATEchannel state events on the underlying realtime channel.(CHA-RL11b)[Testable]If a room lifecycle operation is in progress and such a channel state event is received, the operation is no-op.(CHA-RL11c)[Testable]If a room lifecycle operation is not in progress and such a channel state event is received, then the room status is set according to the underlying channel state, along with any associated error.
(CHA-RL12)The room must monitor the underlying realtime channel state and emit notifications of discontinuity.(CHA-RL12a)[Testable]The room will subscribe toUPDATEandATTACHEDstate events on the underlying realtime channel.(CHA-RL12b)[Testable]If such a channel event is received, and theresumedflag isfalse, and thehasAttachedOnceproperty istrueand theisExplicitlyDetachedflag isfalse, then a discontinuity event will be emitted, using anErrorInfowith theRoomDiscontinuitycode from the error codes and the error from theChannelStateChangeas the cause.
(CHA-RL15)The room must allow users to subscribe to discontinuity events.(CHA-RL15a)[Testable]The room must allow users to supply a listener that shall receive events when a room discontinuity is detected.(CHA-RL15b)[Testable]Users must be able to unsubscribe a previously added listener, after which point events shall no longer be received.(CHA-RL15c)[Testable]Attempting to unsubscribe a previously removed listener shall result in a no-op.
Each chat room can be configured individually, allowing options to be passed as to which features are enabled and also feature-specific settings.
(CHA-RC1)Chat Rooms are singleton objects with respect to the Chat Client on which they are created.(CHA-RC1a)This specification point has been removed. It was superseded by CHA-RC1f.(CHA-RC1f)[Testable]Requesting a room from the Chat Client shall return a future, that eventually resolves to an instance of a room with the provided id and (optional) options.(CHA-RC1f7)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-RC1f8)[Testable]If theRoomsinstance has been disposed (following a call todispose), attempting to get a room must throw an error with codeResourceDiposed.(CHA-RC1f1)[Testable]If the room name exists in the room map, but the room has been requested with different options, then anErrorInfowith theRoomExistsWithDifferentOptionserror code from the chat-specific error codes shall be thrown.(CHA-RC1f2)[Testable]If the room name exists in the room map, and it is requested with the same options, then the same instance of the room must be reused.(CHA-RC1f3)[Testable]If noCHA-RC1grelease operation is in progress, a new room instance shall be created, added to the room map and returned as the future value.(CHA-RC1f4)[Testable]If aCHA-RC1grelease operation is in progress, the entry shall be added to the room map, but the new room instance must not be created until the release operation has resolved. The future shall then subsequently resolve with the new room value.(CHA-RC1f5)In relation toCHA-RC1f4, we recommend storing a future in the room map.(CHA-RC1f6)It should be noted that the “get” operations in this section are interruptible by aCHA-RC1grelease call.
(CHA-RC1b)This specification point has been removed, it was superseded byCHA-RC1f2(CHA-RC1c)This specification point has been removed, it was superseded byCHA-RC1f3(CHA-RC1d)This specification point has been removed.(CHA-RC1d1)This specification point has been removed.
(CHA-RC1e)This specification point has been removed.(CHA-RC1g)A room may bereleased, which causes it to be disposed of and eligible for garbage collection.(CHA-RC1g1)The release operation returns a future that shall resolve when the operation completes.(CHA-RC1g2)[Testable]If the room does not exist in the room map, and no release operation is in progress, this operation is no-op.(CHA-RC1g3)[Testable]If the room does not exist in the room map, and a release operation is already in progress, then the associated future will be returned.(CHA-RC1g4)[Testable]If a release operation is already in progress, any pendingCHA-RC1ffuture shall be rejected / throw an error. The error must use theRoomReleasedBeforeOperationCompletederror code from the chat-specific error codes. The room shall be removed from the room map and the operation must return the future associated with the previous operation.(CHA-RC1g5)[Testable]The room is removed from the room map and aCHA-RL3release operation is initiated for the room object. A future is returned which resolves when the release operation completes.(CHA-RC1g6)[Testable]Once theCHA-RL3release operation completes, each individual feature must be cleaned up for garbage collection via its individualdisposemethod.
(CHA-RC5)[Testable]All chat feature properties (e.g.room.messages) are enabled by default. The specific behaviour of each feature is controlled by its respective room optionsCHA-RC2.(CHA-RC2)Chat rooms are configurable, so as to enable or disable certain functionality. When requesting a room, options as to what functionality should be enabled/disabled, can be provided (RoomOptions).(CHA-RC2a)[Testable]If a room is requested with invalid configuration, for example: a negative typing timeout, anErrorInfowith codeInvalidArgumentmust be thrown.(CHA-RC2c)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RC2b)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RC2d)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RC2e)This specification point has been removed. It was valid up until the single-channel migration.(CHA-RC2f)This specification point has been removed. It was valid up until the single-channel migration.
(CHA-RC3)Multiple chat features share one realtime channel (for example, presence and messages). Each feature may have different channel modes/options that need to be applied.(CHA-RC3a)This specification point has been replaced byCHA-RC3d.(CHA-RC3b)This specification point has been replaced byCHA-RC3d.(CHA-RC3c)[Testable]Unless explicitly stated otherwise, the channel used shall be<roomName>::$chat. So if your room name isfoo, the channel will befoo::$chat.(CHA-RC3d)[Testable]When retrieving theCHA-RC3cchannel viachannels.get(), the channel modes in theChannelOptionsshould be set as follows. SetChannelOptions.modesto an initial value of['PUBLISH', 'SUBSCRIBE', 'PRESENCE', 'ANNOTATION_PUBLISH']. Then follow these steps:(CHA-RC3d1)[Testable]If thepresence.enableEventsroom option (CHA-PR9c) istrue, then addPRESENCE_SUBSCRIBEtoChannelOptions.modes.(CHA-RC3d2)[Testable]If themessages.rawMessageReactionsroom option (CHA-MR9) istrue, then addANNOTATION_SUBSCRIBEtoChannelOptions.modes.
(CHA-RC4)Users may provide room options to therooms.get()call to control the behaviour of the room.(CHA-RC4a)[Testable]Where room options have not been provided, the client shall provide defaults.(CHA-RC4b)[Testable]Where a partial room options have been provided, the client shall deep-merge the provided values with the defaults.
Rooms in the Chat SDK are a complex type that register many different listeners and consume many different events. Therefore, there must be methods available to clean up any listeners to allow for garbage collection.
(CHA-RD1)[Testable]TheRoomsmap / manager must offer an internal API for disposal, that must prepare the rooms manager for garbage collection and release all managed rooms.(CHA-RD1a)[Testable]When disposal is called, it must mark the instance as disposed to prevent future room operations.(CHA-RD1b)[Testable]If no rooms exist whendisposeis called, the method must complete successfully without error.(CHA-RD1c)[Testable]If rooms exist in the map, the procedure must release all rooms currently in the rooms map concurrently.(CHA-RD1d)[Testable]The procedure must wait for all room release operations to complete before resolving.
Messages are the quintessential component of a chat room – the purpose of chat is for users to talk to each other!
Broadly speaking, messages are published via REST calls to the Chat HTTP API and message events are received in Realtime over a corresponding realtime channel.
Messages shall be exposed to consumers via the messages property of a Room.
(CHA-M1)This specification point has been removed. It was valid up until the single-channel migration.(CHA-M2)AMessagecorresponds to a single message in a chat room. This is analogous to a single user-specified message on an Ably channel (NOTE: not aProtocolMessage).(CHA-M2d)[Testable]AMessagecontains a unique, immutableserial, which is a lexicographically sortable string. Global message order can be determined by a simple string comparison of the serials. The SDK must not attempt to parse timeserial strings.(CHA-M2e)[Testable]In global ordering, aMessageis considered to occur before anotherMessageif theserialof the firstMessageis before the latter when lexicographically sorted.(CHA-M2f)[Testable]In global ordering, aMessageis considered after anotherMessageif theserialof the firstMessageis after the latter when lexicographically sorted.(CHA-M2g)[Testable]TwoMessagesare considered to be the same if they have the sameserial, that is to say, bothSerialstrings are identical.
(CHA-M10)AMessagecan be modified by applying a newactionto it, such as an update or delete. Applying anactionmust generate a newMessageinstance with the sameserialand an updatedversion.(CHA-M10a)[Testable]Theversionof aMessageis a lexicographically sortable via it’sserialfield, unique identifier for each version of theMessage.(CHA-M10b)This clause has been deleted.(CHA-M10c)This clause has been deleted.(CHA-M10d)This clause has been deleted.(CHA-M10e)[Testable]To sortMessageversionsof the sameMessage(instances with the sameserial) in global order, sortMessageinstances lexicographically by theirversion.serialproperty.(CHA-M10e1)[Testable]TwoMessageinstances of the sameserialare considered the same version if they have the sameversion.serialproperty.(CHA-M10e2)[Testable]AmongMessageinstances of the sameserial, the one with a lexicographically higherversion.serialis newer.(CHA-M10e3)[Testable]AmongMessageinstances of the sameserial, the one with a lexicographically lowerversion.serialis older.
(CHA-M10f)This specification point has been removed.(CHA-M10g)This specification point has been removed.(CHA-M10h)This specification point has been removed.
(CHA-M3)A client must be able to send a message to a room.(CHA-M3f)[Testable]A client may send a message via the Chat REST API.(CHA-M3a)[Testable]When a message is sent successfully via the Chat REST API, the caller shall receive a struct representing theMessagein response, as if it were received via Realtime event.(CHA-M3a1)[Testable]The message shall be populated directly from the data returned in the server response.
(CHA-M3b)[Testable]A message may be sent via the Chat REST API withoutmetadataorheaders. When these are not specified by the user, they must be omitted from the REST payload.(CHA-M3c)This clause has been deleted.(CHA-M3d)This clause has been deleted.(CHA-M3e)[Testable]If an error is returned from the REST API, itsErrorInforepresentation shall be thrown as the result of thesendcall.
(CHA-M8)A client must be able to update a message in a room.(CHA-M8a)[Testable]A client may update a message via the Chat REST API by calling theupdatemethod.(CHA-M8b)[Testable]When a message is updated successfully via the REST API, the caller shall receive a struct representing the Messagev4 in response, as if it were received via Realtime event.(CHA-M8b1)[Testable]The message shall be populated directly from the data returned in the server response.
(CHA-M8c)[Testable]An update operation has PUT semantics. If a field is not specified in the update, it is assumed to be removed.(CHA-M8d)[Testable]If an error is returned from the REST API, itsErrorInforepresentation must be thrown as the result of theupdatecall.(CHA-M8e)[Testable]A client must be able to update a message without requiring the fullMessagetype. At minimum, it must be able to provide aserialstring and an object/other parameters to update and an (optional) operation information.- @(CHA-M8e1) The exact idiom for the method signature is left to the best practice for the language.
(CHA-M8f)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-M8g)[Testable]If theserialpassed to this method is invalid:undefined, null, empty string, an error with codeInvalidArgumentmust be thrown.
(CHA-M9)A client must be able to delete a message in a room.(CHA-M9a)[Testable]A client may delete a message via the Chat REST API by calling thedeletemethod.(CHA-M9b)[Testable]When a message is deleted successfully via the REST API, the caller shall receive a struct representing the Messagev4 in response, as if it were received via Realtime event.(CHA-M9b1)[Testable]The message shall be populated directly from the data returned in the server response.
(CHA-M9c)[Testable]If an error is returned from the REST API, itsErrorInforepresentation must be thrown as the result of thedeletecall.(CHA-M9d)[Testable]A client must be able to delete a message without requiring the fullMessagetype. At minimum, it must be able to provide aserialstring and an (optional) operation information.- @(CHA-M9d1) The exact idiom for the method signature is left to the best practice for the language.
(CHA-M9e)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-M9f)[Testable]If theserialpassed to this method is invalid:undefined, null, empty string, an error with codeInvalidArgumentmust be thrown.
(CHA-M4)Messages can be received via a subscription in realtime.(CHA-M4a)[Testable]A subscription can be registered to receive incoming messages. Adding a subscription has no side effects on the status of the room or the underlying realtime channel.(CHA-M4b)[Testable]A subscription can de-registered from incoming messages. Removing a subscription has no side effects on the status of the room or the underlying realtime channel.(CHA-M4c)This specification point has been removed. It was valid up until the v1 API review.(CHA-M4l)When a realtime message with thenamefield set tochat.messageis received, it shall be translated into a message event based on itsaction. This message event contains atypefield with the event type as well as amessagefield containing the Messagev4. This event shall then broadcast to all subscribers.(CHA-M4d)[Testable]If a realtime message with an unknownnameis received, the SDK shall silently discard the message, though it may log atDEBUGorTRACElevel.(CHA-M4m)[Testable]Theactionfield of the realtime message determines the type of chat message event that is emitted, based on the following rules.(CHA-M4m1)[Testable]Ifactionis set toMESSAGE_CREATE, then an event withtypeset tomessage.createdshall be emitted.(CHA-M4m2)[Testable]Ifactionis set toMESSAGE_UPDATE, then an event withtypeset tomessage.updatedshall be emitted.(CHA-M4m3)[Testable]Ifactionis set toMESSAGE_DELETE, then an event withtypeset tomessage.deletedshall be emitted.(CHA-M4m4)[Testable]This specification point has been removed. It was valid until the introduction of CHADR-108.(CHA-M4m5)If an event is to be ignored (i.e. because theactionis unknown), the library must log the fact atINFOlevel.
(CHA-M4k)[Testable]Incoming realtime events that are malformed, or have incomplete data, shall still emit an event, but fields will be replaced with default values.(CHA-M4k1)[Testable]Text fields (e.g. serial, string, clientId) shall be empty strings. Theversionfield specification has been superseded byCHA-M4k5.(CHA-M4k2)[Testable]Object fields (e.g. headers, metadata) shall be empty maps/objects.(CHA-M4k3)This specification point has been removed.(CHA-M4k4)This specification point has been removed.(CHA-M4k5)[Testable]Themessage.timestampfield shall be 0 (the unix epoch).(CHA-M4k6)[Testable]Themessage.version.serialfield shall matchmessage.serial.(CHA-M4k7)[Testable]Themessage.version.timestampfield shall matchmessage.timestamp.
(CHA-M5)For a given subscription, messages prior to the point of subscription can be retrieved in a history-like request. Note that this is the point in the message flow(subscription point)at which the subscription was made, NOT the channel attachment point.(CHA-M5a)[Testable]If a subscription is added when the underlying realtime channel isATTACHED, then thesubscription pointis the currentchannelSerialof the realtime channel.(CHA-M5b)[Testable]If a subscription is added when the underlying realtime channel is in any other state, then itssubscription pointbecomes theattachSerialat the the point of channel attachment.(CHA-M5b1)[Testable]If the channel fails to attach then an error should be thrown with an error code 102112 (room in invalid state).
(CHA-M5c)[Testable]If a channel leaves theATTACHEDstate and then re-entersATTACHEDwithresumed=false, then it must be assumed that messages have been missed. Thesubscription pointof any subscribers must be reset to theattachSerial.(CHA-M5d)[Testable]If a channelUPDATEevent is received andresumed=false, then it must be assumed that messages have been missed. Thesubscription pointof any subscribers must be reset to theattachSerial.(CHA-M5e)Each subscription shall expose a method or callback that allows for messages to be queried. These messages are queried via the “REST API”#rest-fetching-messages-history.(CHA-M5f)[Testable]This method must accept any of the standard history query options, except fororderBy, which must always benewestFirst.(CHA-M5g)[Testable]The subscriberssubscription pointmust be additionally specified (internally, by us) in thefromSerialquery parameter.(CHA-M5h)[Testable]The method must return a standardPaginatedResult, which can be further inspected to paginate across results.(CHA-M5i)[Testable]If the REST API returns an error, then the method must throw itsErrorInforepresentation.(CHA-M5j)This specification point has been removed.
(CHA-M6)Messages (history) must be queryable from a paginated REST API.(CHA-M6a)[Testable]A method must be exposed that accepts the standard Ably REST API query parameters. It shall call the “REST API history endpoint”#rest-fetching-messages-history and return aPaginatedResultcontaining messages, which can then be paginated through. Each message must be represented by the standardMessageobject.(CHA-M6b)[Testable]If the REST API returns an error, then the method must throw itsErrorInforepresentation.(CHA-M13)A single message must be retrievable from the REST API.(CHA-M13a)[Testable]A method must be exposed that accepts a message serial and returns a singleMessageobject. It shall call the “REST API single message endpoint”#rest-fetching-single-message and return the message if found.(CHA-M13a1)[Testable]If not found an error should be thrown with an error code 40400 and status code 404.
(CHA-M13b)[Testable]If the REST API returns an error, then the method must throw itsErrorInforepresentation.
(CHA-M7)This specification point has been removed. It was valid up until the single-channel migration.(CHA-M11)AMessagemust have a methodwithto apply changes from events (MessageEventandMessageReactionSummaryEvent) to produce updated message instances.(CHA-M11a)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-M11h)[Testable]When the method receives aMessageEventof typecreated, it must throw anErrorInfowith codeInvalidArgument.(CHA-M11b)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-M11i)[Testable]ForMessageEventthe method must verify that themessage.serialin the event matches the message’s own serial. If they don’t match, an error with codeInvalidArgumentmust be thrown.(CHA-M11c)[Testable]ForMessageEventof typeupdateanddelete, if the event message is older or the same (see CHA-M10), the original message must be returned unchanged.(CHA-M11d)[Testable]ForMessageEventof typeupdateanddelete, if the event message is newer (see CHA-M10), the method must return a new message based on the event and deep-copying the reactions from the original message.(CHA-M11e)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-M11j)[Testable]ForMessageReactionSummaryEvent, the method must verify that themessageSerialin the event matches the message’s own serial. If they don’t match, an error with codeInvalidArgumentmust be thrown.(CHA-M11f)[Testable]ForMessageReactionSummaryEvent, the method must return a newMessageinstance (deep copy) with the updated reactions, preserving all other properties of the original message.(CHA-M11g)[Testable]ForMessageReactionSummaryEvent, the method must deep-copy the reactions from the event before applying them to the returned message instance.
(CHA-M12)[Testable]TheMessagesinstance must provide an internal disposal mechanism, that removes all user-provided listeners, and un-registers any internal listeners that have been associated with the Realtime channel.
Users can add reactions to messages, such as thumbs-up or heart emojis. Summaries (counts per reaction name, and who reacted) of the message reactions are stored with the message and are visible to all users in the room. Message reactions are powered by Pub/Sub message annotations.
MessagesReactions object is the entry point for interacting with Message Reactions. It shall be exposed to consumers via a reactions property inside the Messages obejct. From the Room level: room.messages.reactions.
(CHA-MR1)Message reactions are powered by Pub/Sub message annotations. The annotation type used is of the formatreaction:<aggregation>.
(CHA-MR2)There are three types of message reactions, each corresponding to a different annotation aggregation type.(CHA-MR2a)Reactions of typeuniqueuse the annotation typereaction:unique.v1. In this type, each client can add only one reaction (byname). Reacting again changes the reaction to the new one (similar to WhatsApp or iMessage).(CHA-MR2b)Reactions of typedistinctuse the annotation typereaction:distinct.v1. Each client can add multiple reaction with different names, but each uniquenameonly once (similar to Slack).(CHA-MR2c)Reactions of typemultipleuse the annotation typereaction:multiple.v1. In this type, each client can add any reactionnamemultiple times including duplicates. Reactions can include acountfield indicating how many times the reaction should be counted towards the total (similar to claps on Medium).
(CHA-MR3)[Testable]Thereactionsproperty of theMessageobject is an object or map that contains summaries for all message reaction types. For convenience when using this in the public API, these are keyed by reaction type and not annotation type (eg. “distinct” and not “reaction:distinct.v1”).(CHA-MR3a)[Testable]The values for each reaction type summary are the same as defined in PubSub for the respective annotation aggregation types.(CHA-MR3b)[Testable]Foruniquereaction types, the summary is atMessage.reactions.uniqueand is of typeDict<string, SummaryClientIdList>(seeTM7b2).(CHA-MR3b3)[Testable]Fordistinctreaction types, the summary is atMessage.reactions.distinctand is of typeDict<string, SummaryClientIdList>(seeTM7b1).(CHA-MR3b2)[Testable]Formultiplereaction types, the summary is atMessage.reactions.multipleand is of typeDict<string, SummaryClientIdCounts>(seeTM7b3).
(CHA-MR4)[Testable]Users should be able to send a reaction to a message via the `send` method of the `MessagesReactions` object (room.messages.reactions.send).(CHA-MR4a)[Testable]The `send` method accepts a message serial as the first parameter to identify which message to react to.(CHA-MR4a1)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-MR4a2)[Testable]If theserialpassed to this method is invalid:undefined, null, empty string, an error with codeInvalidArgumentmust be thrown.
(CHA-MR4b)[Testable]The `send` method accepts a `params` object as the second parameter with the following properties:(CHA-MR4b1)[Testable]A `name` property (required) specifying the reaction identifier (e.g., emoji string).(CHA-MR4b2)[Testable]A `type` property (optional) specifying the reaction type. If not provided, the default reaction type for the room is used.(CHA-MR4b3)[Testable]A `count` property (optional) specifying the reaction count. This is only valid for reactions of type `multiple`. Defaults to 1 and must be a positive integer.
(CHA-MR11)[Testable]Users should be able to delete a reaction from a message via the `delete` method of the `MessagesReactions` object (room.messages.reactions.delete).(CHA-MR11a)[Testable]The `delete` method accepts a message serial as the first parameter to identify which message to delete the reaction from.(CHA-MR11a1)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-MR11a2)[Testable]If theserialpassed to this method is invalid:undefined, null, empty string, an error with codeInvalidArgumentmust be thrown.
(CHA-MR11b)[Testable]The `delete` method accepts a `params` object as the second parameter with the following properties:(CHA-MR11b1)[Testable]A `name` property specifying the reaction identifier (e.g., emoji string). It is required for all reaction types except forUnique.(CHA-MR11b1a)[Testable]If the reaction type is notUniqueand thenameparam is not provided, an error with codeInvalidArgumentmust be thrown.(CHA-MR11b2)[Testable]A `type` property (optional) specifying the reaction type. If not provided, the default reaction type for the room is used.
(CHA-MR13)[Testable]Users should be able to fetch the message reaction summary for a given client ID via theclientReactionsmethod of theMessageReactionsobject.(CHA-MR13a)This method accepts the following arguments:(CHA-MR13a1)messageSerial, string: theserialof the message for which reactions should be fetched(CHA-MR13a2)clientId, optional string: theclientIdwhose reactions should be fetched (absent means the current client, to be determined implicitly from the authenticated user)
(CHA-MR13b)[Testable]Calls the REST API fetch reactions summary endpoint and returns the summary.(CHA-MR13b1)[Testable]If the user does not pass theclientIdargument, then the REST API call must not include theforClientIdparameter.
(CHA-MR13c)[Testable]If the REST API returns an error, then the method must throw itsErrorInforepresentation.
(CHA-MR5)[Testable]Users may configure a default message reactions type for a room. This configuration is provided at theRoomOptions.messages.defaultMessageReactionTypeproperty, or idiomatic equivalent. The default value isdistinct.
(CHA-MR6)[Testable]Users must be able to subscribe to message reaction summaries via thesubscribemethod of theMessagesReactionsobject (room.messages.reactions.subscribe). The events emitted will be of typeMessageReactionSummaryEvent.(CHA-MR6a)[Testable]Invalid reaction summary events received over realtime must still produce an event, but with fields filled in with default values.(CHA-MR6a1)[Testable]The summary shall be assumed to be an empty object if unspecified.(CHA-MR6a2)[Testable]If the reaction type (e.g. unique, distinct) is unknown – then it shall be ignored.(CHA-MR6a3)[Testable]If data for a known reaction type (e.g. unique, distinct, multiple) is missing – then it shall be set to an empty object.(CHA-MR6a4)If an event is to be ignored, the library must log the fact atINFOlevel.
(CHA-MR7)[Testable]Users must be able to subscribe to raw message reactions (as individual annotations) via thesubscribeRawmethod of theMessagesReactionsobject (room.messages.reactions.subscribeRaw). The events emitted are of typeMessageReactionRawEvent.(CHA-MR7a)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-MR7c)[Testable]The attempt to subscribe to raw message reactions must throw anErrorInfousing theFeatureNotEnabledInRoomerror code from the chat-specific error codes if the room is not configured to support raw message reactions (when room optionRoomOptions.messages.rawMessageReactionsis not set totrue; it defaults tofalse).(CHA-MR7b)[Testable]Invalid reaction events received over realtime must still produce an event, but with fields filled in with default values.(CHA-MR7b1)[Testable]If the reaction type (e.g. unique, distinct) is unknown – the event shall be ignored (we would be guessing the semantics and likely get it wrong).(CHA-MR7b2)[Testable]If the reaction action type (e.g. add, remove) is unknown – the event shall be ignored (we would be guessing the semantics and likely get it wrong).(CHA-MR7b3)[Testable]String fields (e.g. name, clientId, serial) must take a default value of empty string.(CHA-MR7b4)If an event is to be ignored, the library must log the fact atINFOlevel.
(CHA-MR8)Raw message reactions must not be used to update the message reaction summary on the client-side as this is likely to produce wrong results. Message reaction summaries are to be used for this.
(CHA-MR9)[Testable]The room optionRoomOptions.messages.rawMessageReactionscontrols whether raw message reactions are enabled or not.CHA-RC3d2describes how to implement this. The default value isfalse.(CHA-MR9a)This specification point has been replaced byCHA-RC3d2.
(CHA-MR10)This specification point has been replaced byCHA-RC3d.
(CHA-MR12)[Testable]TheMessageReactionsinstance must provide an internal disposal mechanism, that removes all user-provided listeners, and un-registers any internal listeners that have been associated with the Realtime channel.
Ephemeral room reactions are one-time events that are sent to the room, such as thumbs-up or heart emojis. They are supposed to capture the current emotions in the room (e.g. everyone spamming the 🎉 emoji when a team scores the winning goal).
They are ephemeral as we do not currently store these messages do not support server-authoritative counting.
All ephemeral room reactions are handled over the Realtime connection.
Reactions shall be exposed to consumers via the reactions property of a Room.
(CHA-ER1)This specification point has been removed. It was valid up until the single-channel migration.(CHA-ER2)AReactioncorresponds to a single reaction in a chat room. This is analogous to a single user-specified message on an Ably channel (NOTE: not aProtocolMessage).(CHA-ER3)Ephemeral room reactions are sent to Ably via the Realtime connection via asendmethod.(CHA-ER3a)This specification point was removed. It was valid until the single-channel migration.(CHA-ER3f)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-ER3g)[Testable]If the connection state is notCONNECTED, the operation shall throw anErrorInfowith codeDisconnected.(CHA-ER3d)[Testable]Reactions are sent on the channel using anephemeralmessage in this format.(CHA-ER3b)This clause has been deleted.(CHA-ER3c)This clause has been deleted.
(CHA-ER4)A user may subscribe to reaction events in Realtime.(CHA-ER4a)[Testable]A user may provide a listener to subscribe to reaction events. This operation must have no side-effects in relation to room or underlying status. When a realtime message with nameroomReactionis received, this message is converted into a reaction object and emitted to subscribers.(CHA-ER4b)[Testable]A user may unsubscribe a registered listener. This operation must have no side-effects in relation to room or underlying status. Once unsubscribed, subsequent reaction events must not be emitted to this listener.(CHA-ER4c)[Testable]Realtime events with an unknownnameshall be silently discarded.(CHA-ER4d)[Testable]This specification point has been removed. It was valid until the introduction of CHADR-108.(CHA-ER4e)[Testable]Realtime events that are malformed, or incomplete, must trigger an event, but fields shall be populated with default values.(CHA-ER4e1)[Testable]If thenamefield within the realtime messagedatais omitted, it shall be replaced with an empty string. NOTE: this is NOT the same as thenamefield on the message itself.(CHA-ER4e2)[Testable]The metadata and headers fields shall be replaced with empty objects.(CHA-ER4e3)[Testable]The clientId field shall be an empty string.(CHA-ER4e4)[Testable]Timestamp fields shall be the current timestamp.
(CHA-ER5)This specification point has been removed. It was valid up until the single-channel migration.(CHA-ER6)[Testable]TheRoomReactionsinstance must provide an internal disposal mechanism, that removes all user-provided listeners, and un-registers any internal listeners that have been associated with the Realtime channel.
Presence allows chat room users to indicate to others that they’re online, as well as other information including their profile picture URL etc.
Presence shall be exposed to consumers via the presence property of a Room.
(CHA-PR1)This specification point has been removed. It was valid up until the single-channel migration.(CHA-PR2)This specification point has been removed. It was valid up until the adoption of CHADR-107.(CHA-PR2a)[Testable]This specification point has been removed. It was valid up until the adoption of CHADR-107.(CHA-PR2b)This specification point has been removed. It was valid up until the adoption of CHADR-107.
(CHA-PR3)Users may enter presence. The behaviour depends on the current room status, as presence operations in a Realtime Client cause implicit attaches.(CHA-PR3a)[Testable]Users may choose to enter presence, optionally providing custom data to enter with.(CHA-PR3b)This clause has been replaced byCHA-PR3c - CHA-PR3g.(CHA-PR3c)This specification point has been removed.- @(CHA-PR3c1) This specification point has been removed.
(CHA-PR3c2)This specification point has been removed.
(CHA-PR3d)[Testable]If the room status isAttaching, then the operation shall invoke aCHA-RL9pending room status operation and wait for it to complete. If this operation fails, then the error fromCHA-RL9must be raised. If this succeeds (i.e the room becomesATTACHED), the library must invoke the underlyingpresence.enter()call.(CHA-PR3h)[Testable]If the room status is anything other thanAttachedorAttaching, anErrorInfousing theRoomInInvalidStateerror code from the chat-specific error codes must be thrown. The message shall explain that attach must be called first.(CHA-PR3e)[Testable]If the room status isAttached, then theentercall will invoke the underlyingpresence.enter()call.(CHA-PR3f)This specification point has been removed. It was superseded byCHA-PR3h.(CHA-PR3g)This specification point has been removed. It was superseded byCHA-PR3h.
(CHA-PR10)Users may update their presence data. The behaviour depends on the current room status, as presence operations in a Realtime Client cause implicit attaches.(CHA-PR10a)[Testable]Users may choose to update their presence data, optionally providing custom data to update with.(CHA-PR10b)This clause has been replaced byCHA-PR10c - CHA-PR10g.(CHA-PR10c)This specification point has been removed.(CHA-PR10c1)This specification point has been removed.(CHA-PR10c2)This specification point has been removed.
(CHA-PR10d)[Testable]If the room status isAttaching, then the operation shall invoke aCHA-RL9pending room status operation and wait for it to complete. If this operation fails, then the error fromCHA-RL9must be raised. If this succeeds (i.e the room becomesATTACHED), the library must invoke the underlyingpresence.update()call.(CHA-PR10h)[Testable]If the room status is anything other thanAttachedorAttaching, anErrorInfousing theRoomInInvalidStateerror code from the chat-specific error codes must be thrown. The message shall explain that attach must be called first.(CHA-PR10e)[Testable]If the room status isAttached, then theupdatecall will invoke the underlyingpresence.enter()call.(CHA-PR10f)This specification point has been removed. It was superseded byCHA-PR10h.(CHA-PR10g)This specification point has been removed. It was superseded byCHA-PR10h.
(CHA-PR4)Users may leave presence.(CHA-PR4a)[Testable]Users may choose to leave presence, which results in them being removed from the Realtime presence set. The user may optionally provide custom data to leave with.
(CHA-PR5)[Testable]It must be possible to query if a given clientId is in the presence set.(CHA-PR6)[Testable]It must be possible to retrieve all the @Members of the presence set. The behaviour depends on the current room status, as presence operations in a Realtime Client cause implicit attaches.(CHA-PR6a)This clause has been replaced byCHA-PR6b - CHA-PR6f.(CHA-PR6b)This specification point has been removed.(CHA-PR6b1)This specification point has been removed.(CHA-PR6b2)This specification point has been removed.
(CHA-PR6c)[Testable]If the room status isAttaching, then the operation shall invoke aCHA-RL9pending room status operation and wait for it to complete. If this operation fails, then the error fromCHA-RL9must be raised. If this succeeds (i.e the room becomesATTACHED), the library must invoke the underlyingpresence.get()call.(CHA-PR6h)[Testable]If the room status is anything other thanAttachedorAttaching, anErrorInfousing theRoomInInvalidStateerror code from the chat-specific error codes must be thrown. The message shall explain that attach must be called first.(CHA-PR6d)[Testable]If the room status isAttached, then thegetcall will invoke the underlyingpresence.get()call.(CHA-PR6e)This specification point has been removed. It was superseded byCHA-PR6h.(CHA-PR6f)This specification point has been removed. It was superseded byCHA-PR6h.
(CHA-PR7)Users may subscribe to presence events.(CHA-PR7d)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-PR7e)[Testable]If presence events have been disabled perCHA-PR9c, then the client shall throw anErrorInfousing theFeatureNotEnabledInRoomerror code from the chat-specific error codes.(CHA-PR7a)[Testable]Users may provide a listener to subscribe to all presence events in a room.(CHA-PR7b)[Testable]This specification point has been removed.(CHA-PR7c)[Testable]A subscription to presence may be removed, after which it shall receive no further events.
(CHA-PR8)This specification point has been removed. It was valid up until the single-channel migration.(CHA-PR9)Users may configure their presence options via theRoomOptionsprovided at room configuration time.(CHA-PR9a)This specification point has been removed. It was valid up until the single-channel migration and was replaced byCHA-PR9c.(CHA-PR9b)This specification point has been removed. It was valid up until the single-channel migration and was replaced byCHA-PR9c.(CHA-PR9c)The optionpresence.enableEventscontrols whether or not the client should receive presence events from the server.CHA-RC3d1describes how to implement this.(CHA-PR9c1)[Testable]This option defaults totrue.(CHA-PR9c2)This specification point has been replaced byCHA-RC3d1.
(CHA-PR11)[Testable]ThePresenceinstance must provide an internal disposal mechanism, that removes all user-provided listeners, and un-registers any internal listeners that have been associated with the Realtime channel.
Typing Indicators allow chat room users to indicate to others that they are typing. This is most common in 1:1 and community chat, where a UI would display somthing like “Alice and Bob are typing…” to users.
This system uses ephemeral non-persisted heartbeat messages. They do not appear in channel history, resume or rewind.
Typing Indicators shall be exposed to consumers via the typing property of a Room.
For the purpose of this section, an ephemeral message is understood to mean a regular message where message.extras.ephemeral is set to true, i.e typing-message-format.
(CHA-T1)This specification point has been removed. It was replaced byCHA-RC3c.(CHA-T2)This specification point has been removed. It was replaced byCHA-T9.(CHA-T8)This specification point has been removed. It was replaced byCHA-RC3c.(CHA-T9)This specification point has been removed. It was valid until the V1 API Review.(CHA-T16)[Testable]Users must be able to retrieve a list of the currently typing client IDs through atyping.current()method. The list must be a copy of the up-to-date internal list of typing client IDs.(CHA-T15)[Testable]Users must be able to provide a listener to subscribe to new typing events that the Chat client emits. This operation must have no side effects on the status of the room or the underlying realtime channel.(CHA-T10)[Testable]Users may configure a heartbeat interval (the no-op period fortyping.keystrokewhen the heartbeat timer is set active atCHA-T4a4). This configuration is provided at theRoomOptions.typing.heartbeatThrottleMsproperty, or idiomatic equivalent. The default is 10000ms.(CHA-T10a)A grace period shall be set by the client (the grace period on theCHA-T10heartbeat interval when receiving events). The default value shall be set to 2000ms.(CHA-T10a1)This grace period is used to determine how long to wait (since the last recorded received typing event), beyond the heartbeat interval, before removing a client from the typing set. This is used to prevent flickering when a user is typing and stops typing for a short period of time. SeeCHA-T13b1for a detailed description of how this is used.
(CHA-T3)This specification point has been removed. It was replaced byCHA-T10.(CHA-T4)Users may indicate that they have started typing using thekeystrokemethod.(CHA-T4d)This specification point has been removed. It was valid until the V1 API Review.(CHA-T4e)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-T4f)[Testable]If the connection state is notCONNECTED, the operation shall throw anErrorInfowith codeDisconnected.(CHA-T4a)If typing is not already in progress (i.e. a heartbeat timer has not been set by the successful publishCHA-T4a4of atyping.startedevent):(CHA-T4a1)This specification point has been removed. It was replaced byCHA-T4a3+.(CHA-T4a2)This specification point has been removed. It was replaced byCHA-T4a3+.(CHA-T4a3)[Testable]The client shall publish an ephemeral message to the channel with thenamefield set totyping.started, the format of which is detailed here.(CHA-T4a4)[Testable]Upon successful publish, a heartbeat timer shall be set according to theCHA-T10timeout interval.(CHA-T4a5)[Testable]The client must wait for the publish to succeed or fail before returning the result to the caller. If the publish fails, the client must handle this transparently and re-throw the error directly to the caller.
(CHA-T4b)This specification point has been removed. It was superseded byCHA-T4c.(CHA-T4c)If typing is already in progress (i.e. a heartbeat timer set according toCHA-T4a4exists and has not expired):(CHA-T4c1)[Testable]The client must not send atyping.startedevent, instead it shall return a successful no-op to the caller.
(CHA-T5)Users may explicitly indicate that they have stopped typing usingstopmethod.(CHA-T5a)[Testable]If typing is not in progress (i.e. aCHA-T4a4heartbeat timer does not exist or is expired), this operation is a no-op.(CHA-T5b)This specification point has been removed.(CHA-T5c)This specification point has been removed. It was valid until the V1 API Review.(CHA-T5f)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-T5g)[Testable]If the connection state is notCONNECTED, the operation shall throw anErrorInfowith codeDisconnected.(CHA-T5d)[Testable]The client shall publish an ephemeral message to the channel with the name field set totyping.stopped, the format of which is detailed here. The client must wait for the publish to succeed or fail before returning the result to the caller.(CHA-T5d1)[Testable]The client must wait for the publish to succeed or fail before returning the result to the caller. If the publish fails, the client must handle this transparently and re-throw the error directly to the caller.(CHA-T5e)[Testable]On successfully publishing the message in(CHA-T5d), theCHA-T4a4timer shall be unset.
(CHA-T6)Users may subscribe to typing events – updates to a set of clientIDs that are typing. This operation, like all subscription operations, has no side-effects in relation to room lifecycle.(CHA-T6a)[Testable]Users may provide a listener to subscribe to typing events in a chat room.(CHA-T6b)[Testable]A subscription to typing may be removed, after which it shall receive no further events.(CHA-T6c)This specification point has been removed, it has been superseded byCHA-T6d.(CHA-T6c1)This specification point has been removed, it has been superseded byCHA-T6d.(CHA-T6c2)This specification point has been removed, it has been superseded byCHA-T6d.
(CHA-T7)This specification point has been removed. It was valid up until the single-channel migration.(CHA-T13)When a typing event (typing.startortyping.stop) is received from the realtime client, the Chat client shall emit appropriate events to all registered listeners(CHA-T15).(CHA-T13a)[Testable]Typing events received from the server must have a validclientIdand must not be malformed, i.e they must conform to the expected payload (unknown fields should be ignored). Invalid events shall not be handled or emitted to any registered listeners (CHA-T15).(CHA-T13b)[Testable]When a typing event is received on the realtime client:(CHA-T13b1)[Testable]If the event represents a new client typing, then the Chat client shall add the typersclientIdto the typing set and a emit the updated set, via a new typing event, to any listeners. It shall also begin a timer with a timeout period that is the sum of theCHA-T10heartbeat interval and theCHA-T10agraсe period.(CHA-T13b2)[Testable]Each additionaltyping.startedevent from the same client shall reset the(CHA-T13b1)timer for that client.(CHA-T13b3)[Testable]If the(CHA-T13b1)timer expires, the client shall remove theclientIdfrom the typing set and emit a typing stopped event for the given client to all registered listeners (CHA-T15).(CHA-T13b4)[Testable]If the event represents a client that has stopped typing, the Chat client shall remove theclientIdfrom the typing set and emit a typing stopped event to any registered listeners (CHA-T15). It shall also cancel the(CHA-T13b1)timer for the given typing client.(CHA-T13b5)[Testable]If the event represents a client that has stopped typing, but theclientIdfor that client is not present in the typing set, then no stop typing event shall be emitted to any registered listeners (CHA-T15).
(CHA-T14)[Testable]Multiple asynchronous calls to keystroke/stop typing must eventually converge to a consistent state.(CHA-T14a)[Testable]When a call tokeystrokeorstopis made, it should attempt to acquire a mutex lock.(CHA-T14b)[Testable]Once the lock is acquired, if another call is made to either function, the second call shall be queued and wait until it can acquire the lock before executing.(CHA-T14b1)[Testable]During this time, each new subsequent call to either function shall abort the previously queued call, this should be registered as a successful no-op to the caller. In doing so, there shall only ever be one pending call while the mutex is held, thus the most recent call shall “win” and execute once the mutex is released.(CHA-T14b2)[Testable]If an error occurs while the lock is held, the mutex shall be released and the error shall be thrown to the caller.
(CHA-T17)[Testable]TheMessagesinstance must provide an internal disposal mechanism, that removes all user-provided listeners, and un-registers any internal listeners that have been associated with the Realtime channel. Any timeouts or timers associated with typing heartbeats must also be cancelled.
Occupancy allows chat room users to find out how many people are currently online in the Chat Room, which is useful for showing statistics such as the viewer count in a livestream. It allows this to happen without
the overhead of having everyone in presence.
Occupancy shall be exposed to consumers via the occupancy property of a Room.
(CHA-O1)This specification point has been removed. It was valid up until the single-channel migration.(CHA-O2)The occupancy event format is shown here(CHA-O3)[Testable]Users can request an instantaneous occupancy check via the REST API. The request is detailed here, with the response format being a simple occupancy data struct(CHA-O4)Users can subscribe to realtime occupancy updates.(CHA-O4e)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-O4h)[Testable]If occupancy events have been disabled perCHA-O6, then the client shall throw anErrorInfousing theFeatureNotEnabledInRoomerror code from the chat-specific error codes.(CHA-O4a)[Testable]Users may register a listener that receives occupancy events in realtime.(CHA-O4b)[Testable]A subscription to occupancy events may be removed, after which it shall receive no further events.(CHA-O4c)This specification point has been removed. It was valid until the V1 API Review.(CHA-O4f)[Testable]When a regular occupancy event is received on the channel (a standard PubSub occupancy event per the docs), the SDK will convert it into occupancy event V2 format, with typeOccupancyEventType.Updated, and broadcast it to subscribers.(CHA-O4d)[Testable]This specification point has been removed. It was valid until the introduction of CHADR-108.(CHA-O4g)[Testable]If an invalid occupancy event is received from the channel, or is incomplete, then an event must be emitted but the values shall be 0.
(CHA-O5)This specification point has been removed. It was valid up until the single-channel migration.(CHA-O6)Enabling occupancy events causes extra messages to be received by the client. Therefore, whether to receive these messages shall be user-configurable.(CHA-O6a)[Testable]Ifoccupancy.enableEventsis set totrue, then the client shall set theoptions.params.occupancychannel parameter to"metrics"on the underlying realtime channel perCHA-RC3.(CHA-O6b)[Testable]Ifoccupancy.enableEventsis set tofalse, then the client shall not set theoptions.params.occupancychannel parameter.(CHA-O6c)[Testable]The default value ofoccupancy.enableEventsisfalse.
(CHA-O7)Users can access the latest occupancy received in realtime, via theoccupancy.current()method.(CHA-O7a)[Testable]Thecurrentmethod should return the latest occupancy numbers received over the realtime connection in a[meta]occupancyevent.(CHA-O7b)[Testable]If no realtime events have been received yet,current()returns undefined/null.(CHA-O7c)This specification point has been removed. It was valid up until the pre-v1 error code review.(CHA-O7d)[Testable]If occupancy events are not enabled viaRoomOptions.occupancy.enableEvents,current()throws anErrorInfousing theFeatureNotEnabledInRoomerror code from the chat-specific error codes.
(CHA-O8)[Testable]TheOccupancyinstance must provide an internal disposal mechanism, that removes all user-provided listeners, and un-registers any internal listeners that have been associated with the Realtime channel.
(CHA-RST1)REST API requests shall be made via therequest()method on the underling Ably SDK.(CHA-RST2)REST API requests shall use the API Protocol Version of the underlying core SDK.(CHA-RST3)[Testable]REST API requests must be supported usingJSONandmsgpackas aContent-Type(useBinaryProtocolon the underlying Ably SDK)(CHA-RST6)[Testable]All URL path variables (that is, the path segments given in angle brackets, e.g.<roomName>) must be percent-encoded as follows: the path variable must first be encoded to UTF-8 octets, and then each octet that does not satisfy thepcharrule in section 3.3 of RFC 3986 must be percent-encoded using the mechanism described in section 2.1 of the same RFC. Implementations are free to percent-encode any other octets that do not satisfy theunreservedrule in section 2.3 of the same RFC; this allows implementations to use standard library URI-encoding functions that percent-encode a wider set of characters than those listed inpchar, for example JavaScript’sencodeURIComponentfunction. (Be careful when selecting a standard library function for URI-encoding to ensure that it does indeed produce output suitable for use in a URI path segment; for example, JavaScript’sencodeURIdoes not percent-encode the/character, rendering it unsuitable.)(CHA-RST4)[Testable]This specification point has been replaced byCHA-RST6.(CHA-RST5)[Testable]This specification point has been replaced byCHA-RST6.
Below is the full REST payload format for the V4 endpoint. The metadata and headers keys are optional.
POST /chat/v4/rooms/<roomName>/messages
{
"text": "the message text",
"metadata": {
"foo": {
"bar": 1
}
},
"headers": {
"baz": "qux"
}
}
A successful request shall result in status code 201 Created.
The response body is as follows.
{
"serial": "01726585978590-001@abcdefghij:001",
"createdAt": 1726232498871
}
A successful request shall result in status code 201 Created.
The response body is as follows.
{
// A full message object
}
{
"type": "message.created"
"message": {
"name": "chat.message"
"encoding": "json"
"data": {
"text": "the message text",
"metadata": {
"foo": {
"bar": 1
}
}
},
"timestamp": 1726232498871,
"createdAt": 1726232498871,
"extras": {
"headers": {
"baz": "qux"
},
},
"serial": "01726585978590-001@abcdefghij:001",
"action": "message.create",
"version": "01726585978590-001@abcdefghij:001",
"operation": {}
}
}
{
"type": "message.created"
"message": {
"name": "chat.message"
"encoding": "json"
"data": {
"text": "the message text",
"metadata": {
"foo": {
"bar": 1
}
}
},
"timestamp": 1726232498871,
"extras": {
"headers": {
"baz": "qux"
},
},
"serial": "01726585978590-001@abcdefghij:001",
"action": "message.create",
"version": {
serial: "01726585978590-001@abcdefghij:001",
timestamp: 1726232498871,
}
}
Below is the full REST payload format for the endpoint. The description, headers and both metadata keys are optional.
PUT /chat/v4/rooms/<roomName>/messages/<serial>
{
"message": {
"text": "the new message text",
"metadata": {
"foo": {
"bar": 1
}
},
"headers": {
"baz": "qux"
},
},
"description": "why-the-action-was-performed"
"metadata": {
"foo": "bar"
}
}
A successful request shall result in status code 200 Ok.
The response body is as follows.
{
"version": "01726585978590-001@abcdefghij:001",
"timestamp": 1726585978590,
"message": {
// A full message object
}
}
A successful request shall result in status code 200 Ok.
The response body is as follows.
{
// A full message object
}
{
"type": "message.updated"
"message": {
"name": "chat.message"
"encoding": "json"
"data": {
"text": "the new message text",
"metadata": {
"foo": {
"bar": 1
}
}
},
"createdAt": 1726232498871,
"timestamp": 1726585978590,
"extras": {
"headers": {
"baz": "qux"
}
}
"serial": "01726232498871-001@abcdefghij:001",
"version": "01726585978590-001@abcdefghij:001"
"action": "message.update"
"operation": {
"clientId": "who-performed-the-action",
"description": "why-the-action-was-performed"
"metadata": {
"foo": "bar"
}
}
}
}
Below is the full REST payload format for the endpoint.
POST /chat/v4/rooms/<roomName>/messages/<serial>/delete
{
"description": "why-the-action-was-performed"
"metadata": {
"foo": "bar"
}
}
A successful request shall result in status code 200 Ok.
The response body is as follows.
{
"version": "01826232498871-001@abcdefghij:001",
"timestamp": 1826232498871,
"message": {
// A full message object
}
}
A successful request shall result in status code 200 Ok.
The response body is as follows.
{
// A full message object
}
{
"type": "message.deleted",
"message": {
"name": "chat.message",
"encoding": "json",
"data": {
"text": "the original message text",
"metadata": {
"foo": {
"bar": 1
}
}
},
"timestamp": "1726232498871",
"extras": {
"headers": {
"baz": "qux"
}
},
"serial": "01726585978590-001@abcdefghij:001",
"action": "message.deleted",
"version": {
"serial": "01826232498871-001@abcdefghij:001",
"timestamp": 1826232498871,
"clientId": "who-performed-the-action",
"description": "why-the-action-was-performed",
"metadata": {
"foo": "bar"
}
}
}
}
GET /chat/v3/rooms/<roomName>/messages/<serial>
A single V3 Message struct or status 404 if not found.
GET /chat/v4/rooms/<roomName>/messages/<serial>
A single V4 Message struct or status 404 if not found.
GET /chat/v4/rooms/<roomName>/messages
The method accepts query parameters identical to the standard Ably REST API.
An array of V2 Message structs
The method accepts query parameters identical to the standard Ably REST API.
An array of V4 Message structs
Below is the full REST payload format for the V4 endpoint. The count field is optional, defaults to 1, and it is only accepted for reactions of type multiple.
POST /chat/v4/rooms/<roomId>/messages/<serial>/reactions
{
"type": "multiple",
"name": "🔥",
"count": 5
}
A successful request shall result in status code 201 Created.
The response body is as follows showing the serial of the new annotation.
{
"serial": "01746631786947-000@cbfVqJopQBorrt95348450",
}
Note this is an Annotation not a Message. ChannelMessage action is 21. The annotations are under the annotations key.
{
"action" : 0,
"clientId" : "user-1",
"messageSerial" : "01746631762878-000@cbfVqJopQBorrt95348450:000",
"name" : "🔥",
"serial" : "01746631786947-000@cbfVqJopQBorrt95348450:000",
"type" : "multiple",
"count": 5
}
A message summary event is also broadcast to the channel after adding or removing one or more annotations.
Below is the full REST payload format for the V4 endpoint. The name param is ignored for reactions of type unique but it is required for all other reaction types.
DELETE /chat/v4/rooms/<roomId>/messages/<serial>/reactions?type=<reactionType>&name=<reaction>
A successful request shall result in status code 200 OK.
The response body is as follows showing the serial of the new annotation (annotation delete is also an annotation with annotation action=1).
{
"serial": "01746632464399-000@cbfVqJopQBorrt95348450",
}
Note this is an Annotation not a Message. ChannelMessage action is 21. The annotations are under the annotations key.
{
"action" : 1,
"clientId" : "user-1",
"messageSerial" : "01746631762878-000@cbfVqJopQBorrt95348450:000",
"name" : "❤️",
"serial" : "01746632464399-000@cbfVqJopQBorrt95348450:000",
"type" : "distinct"
}
A message summary event is also broadcast to the channel after adding or removing one or more annotations.
GET /chat/v4/rooms/<roomName>/messages/<serial>/client-reactions
Accepts the following query parameters:
forClientId(optional): The client ID for which the message summary should be fetched. Defaults to the client ID of the authenticated user.
A single V4 Message.reactions struct or status 404 if not found.
This section describes the message formats for chat events that occur over a Realtime connection.
This was valid until the Chat v1 API review.
{
"name": "roomReaction"
"encoding": "json"
"data": {
"type": ":heart:",
"metadata": {
"foo": {
"bar": 1
}
}
},
"timestamp": "1726232498871", // Only on incoming messages
"extras": {
"headers": {
"baz": "qux"
},
"ephemeral": true, // This is set by the Chat SDK
}
}
As part of the Chat v1 API review, the “type” field was renamed to “name”.
{
"name": "roomReaction"
"encoding": "json"
"data": {
"name": ":heart:",
"metadata": {
"foo": {
"bar": 1
}
}
},
"timestamp": "1726232498871", // Only on incoming messages
"extras": {
"headers": {
"baz": "qux"
},
"ephemeral": true, // This is set by the Chat SDK
}
}
{
"name": "typing.started" // Required, must be either "typing.started" or "typing.stopped"
"clientId": "who-is-typing", // Required, cannot be empty string or null - This is automatically set by the underlying pubsub SDK.
"extras": { "ephemeral": true } // Required, must be set to true
}
GET /chat/v4/rooms/<roomName>/occupancy
This section contains an overview of the key types in Chat. This is not intended to be prescriptive to implementation and different ecosystems may expose the underlying properties
in whichever format is most idiomatic to the platform.
The RoomOptions struct describes configuration options for a Chat room. It is an optional argument when getting the room, with defaults provided. Below describes the structure, along with the default values.
{
"presence": {
"enableEvents": true
},
"typing": {
"heartbeatThrottleMs": 10000
},
"occupancy": {
"enableEvents": false
},
"messages": {
"rawMessageReactions": false,
"defaultMessageReactionType": MessageReactionType.Distinct
}
}
Messages V1 (deprecated)
{
“timeserial”: “cbfqxperABgItU52203559@1726232498871-0”,
“clientId”: “who-sent-the-message”,
“text”: “my-message”,
“createdAt”: DateTime(),
“metadata”: {
“foo”: {
“bar”: 1
}
},
“headers”: {
“baz”: “qux”
}
}
{
“timeserial”: “cbfqxperABgItU52203559@1726232498871-0”,
“clientId”: “who-sent-the-message”,
“text”: “my-message”,
“createdAt”: DateTime(),
“metadata”: {
“foo”: {
“bar”: 1
}
},
“headers”: {
“baz”: “qux”
}
}
(deprecated) Determining the global order of messages may be achieved by comparing the timeserials. See CHA-M2 for more information.
{
"serial": "01726585978590-001@abcdefghij:001",
"clientId": "who-sent-the-message",
"text": "my-message",
"createdAt": DateTime(),
"metadata": {
"foo": {
"bar": 1
}
},
"headers": {
"baz": "qux"
}
"action": "message.created",
"version": "01726585978590-001@abcdefghij:001",
"timestamp": DateTime(),
"operation": {
"clientId": "who-performed-the-action",
"description": "why-the-action-was-performed"
"metadata": {
"foo": {
"bar": 1
}
},
},
"reactions": {
"unique": {"like": {"total": 2, "clientIds": ["userOne", "userTwo"]}, "love": {"total": 1, "clientIds": ["userThree"]}},
"distinct": {"like": {"total": 2, "clientIds": ["userOne", "userTwo"]}, "love": {"total": 1, "clientIds": ["userOne"]}},
"multiple": {"like": {"total": 5, "clientIds": {"userOne": 3, "userTwo": 2}}, "love": {"total": 10, "clientIds": {"userOne": 10}}},
}
}
{
"serial": "01726585978590-001@abcdefghij:001",
"clientId": "who-sent-the-message",
"text": "my-message",
"timestamp": DateTime(),
"metadata": {
"foo": {
"bar": 1
}
},
"headers": {
"baz": "qux"
}
"action": "message.created",
"version": {
"serial": "01726585978590-001@abcdefghij:001",
"timestamp": DateTime(),
"clientId": "who-performed-the-action",
"description": "why-the-action-was-performed",
"metadata": {
"foo": {
"bar": 1
}
}
},
"reactions": {
"unique": {"like": {"total": 2, "clientIds": ["userOne", "userTwo"]}, "love": {"total": 1, "clientIds": ["userThree"]}},
"distinct": {"like": {"total": 2, "clientIds": ["userOne", "userTwo"]}, "love": {"total": 1, "clientIds": ["userOne"]}},
"multiple": {"like": {"total": 5, "clientIds": {"userOne": 3, "userTwo": 2}}, "love": {"total": 10, "clientIds": {"userOne": 10}}},
}
}
Determining the global order of messages may be achieved by lexicographically comparing the serials. See CHA-M2 for more information.
Determining the global order of message versions may be achieved by lexicographically comparing the version.serial. See CHA-M10 for more information.
When keeping a Message updated, use the Message.with(event) method to apply events to ensure correctness. The with method returnes message instances with the event applied to them. If the event is not applicable due to being for an older version of the message, the method will return the message unchanged. If the event is applicable, a new instance will be returned with the event applied. Message.with() correctly handles message reactions. See CHA-M11 for more information.
The MessageReactionSummaryEvent interface provides information about reaction summaries for messages.
- The event must include a
typefield set to indicate it’s a reaction summary event. - The event must include a
summaryobject containing:- The
messageSerialidentifying which message the reactions belong to. - A
uniquefield containing reaction summaries for unique-type reactions. - A
distinctfield containing reaction summaries for distinct-type reactions. - A
multiplefield containing reaction summaries for multiple-type reactions.
- The
Example:
{
type: "reaction.summary",
messageSerial: "01726585978590-001@abcdefghij:001",
reactions: {
"unique": {
"like": {
"total": 2,
"clientIds": ["userOne", "userTwo"]
},
"love": {
"total": 1,
"clientIds": ["userThree"]
}
},
"distinct": {
"like" : {
"clientIds" : [
"userOne",
"userTwo"
],
"total" : 2
},
"love" : {
"clientIds" : [
"userOne"
],
"total" : 1
}
},
"multiple": {
"like" : {
"clientIds" : {
"userOne" : 3,
"userTwo" : 2
},
"total" : 5
},
"love" : {
"clientIds" : {
"userOne" : 10
},
"total" : 10
}
}
}
}
The MessageReactionRawEvent interface provides information about individual message reaction events.
- The event must include a
typefield indicating whether a reaction was added or removed. - The event must include a
timestampfield containing the date/time when the event occurred. - The event must include a
reactionobject containing:- A
messageSerialidentifying which message the reaction belongs to. - A
typefield indicating the reaction type (unique, distinct, or multiple). - A
namefield containing the name of the reaction (e.g. emoji string). - An optional
countfield for reactions of typemultiple. - A
clientIdidentifying the user who added or removed the reaction.
- A
{
type: "reaction.create",
timestamp: DateTime(),
reaction: {
messageSerial: "01726585978590-001@abcdefghij:001",
type: "multiple",
name: ":like:",
count: 3,
clientId: "user1",
};
}
Event type can be reaction.update or reaction.delete.
Event reaction.type can be unique, distinct or multiple.
reaction.count is optional and only set for multiple.
This was valid until the Chat v1 API review.
{
"type": ":heart:",
"clientId": "who-sent-the-message",
"createdAt": DateTime(),
"metadata": {
"foo": {
"bar": 1
}
},
"headers": {
"baz": "qux"
}
}
This was valid until the Chat v1 API review.
{
"type": RoomReactionEventType.Reaction,
"reaction": {
"type": ":heart:",
"clientId": "who-sent-the-message",
"createdAt": DateTime(),
"metadata": {
"foo": {
"bar": 1
}
},
"headers": {
"baz": "qux"
}
}
}
As part of the Chat v1 API review, the “type” field was renamed to “name”.
{
"name": ":heart:",
"clientId": "who-sent-the-message",
"createdAt": DateTime(),
"metadata": {
"foo": {
"bar": 1
}
},
"headers": {
"baz": "qux"
}
}
As part of the Chat v1 API review, the “type” field was renamed to “name”.
{
"type": RoomReactionEventType.Reaction,
"reaction": {
"name": ":heart:",
"clientId": "who-sent-the-message",
"createdAt": DateTime(),
"metadata": {
"foo": {
"bar": 1
}
},
"headers": {
"baz": "qux"
}
}
}
{
"clientId": "who-sent-the-message",
"updatedAt": DateTime(),
"data": { … }, // A presence data object containing any user-provided data. The structure of this object is described by CHA-PR2.
"extras": {
"headers": {
"baz": "qux"
}
}
}
{
"clientId": "who-is-in-presence",
"action": "enter",
"timestamp": DateTime(),
"data": { … }, // A presence data object containing any user-provided data. The structure of this object is described by CHA-PR2.
}
{
"type": PresenceEventType,
"member": { // PresenceMember
"clientId": "who-is-in-presence",
"updatedAt": DateTime(),
"data": { … },
"extras": {},
"connectionId": "foo",
"encoding": "json"
}
}
{
"currentlyTyping": ["clientId-1", "clientID-2"]
}
{
"change": {
"type": "typing.started",
"clientId": "clientId-2",
},
"currentlyTyping": ["clientId-1", "clientId-2"],
}
{
"type": TypingSetEventType.SetChanged
"change": {
"type": TypingEventType,
"clientId": "clientId-2",
},
"currentlyTyping": ["clientId-1", "clientId-2"],
}
{
"connections": 5,
"presenceMembers" 2
}
{
"type": OccupancyEventType.Updated,
"occupancy": { // Of type OccupancyData
"connections": 5,
"presenceMembers" 2
}
}
{
"connections": 5,
"presenceMembers" 2
}
This section contains error codes that are common across Ably, but the Chat SDK makes use of. The status code for the error should align with the error code (i.e. 4xxxx and 5xxxx shall have statuses 400 and 500 respectively).
The codes listed here shall be defined in any error enums that exist in client library.
// The request was invalid.
// To be accompanied by status code 400.
BadRequest = 40000,
// Invalid argument provided.
// To be accompanied by status code 400.
InvalidArgument = 40003,
// Invalid client ID.
// To be accompanied by status code 400.
InvalidClientId = 40012,
// Resource has been disposed.
// To be accompanied by status code 400.
ResourceDisposed = 40014,
// The message was rejected before publishing by a rule on the chat room.
// To be accompanied by status code 422.
MessageRejectedByBeforePublishRule = 42211,
// The message was rejected before publishing by a moderation rule on the chat room.
// To be accompanied by status code 422.
MessageRejectedByModeration = 42213,
// The client is not connected to Ably.
// To be accompanied by status code 400.
Disconnected = 80003,
// Could not re-enter presence automatically after a room re-attach occurred.
// To be accompanied by status code 400.
PresenceAutoReentryFailed = 91004,
This section contains error codes that are specific to Chat. If a specific error code is not listed for a given circumstance, the most appropriate general error code shall be used according to the guidelines of CHA-GP5. For example 400xx for client errors or 500xx for server errors.
The chat reserved error code range is 102000 - 102999.
The codes listed here shall be defined in any error enums that exist in client library.
// The room has experienced a discontinuity.
// To be accompanied by status code 500.
RoomDiscontinuity = 102100,
// Cannot perform operation because the room is in an invalid state.
// To be accompanied by status code 400.
RoomInInvalidState = 102112,
// Room was released before the operation could complete.
// To be accompanied by status code 400.
RoomReleasedBeforeOperationCompleted = 102106,
// A room already exists with different options.
// To be accompanied by status code 400.
RoomExistsWithDifferentOptions = 102107,
// Feature is not enabled in room options.
// To be accompanied by status code 400.
FeatureNotEnabledInRoom = 102108,
// Failed to enforce sequential execution of the operation.
// To be accompanied by status code 500.
OperationSerializationFailed = 102113,
// 102200 - 102300 are reserved for React errors
// React hook must be used within the appropriate provider.
// To be accompanied by status code 400.
ReactHookMustBeUsedWithinProvider = 102200,
// React component has been unmounted.
// To be accompanied by status code 400.
ReactComponentUnmounted = 102201,
// Failed to fetch presence data after maximum retries.
// To be accompanied by status code 500.
PresenceFetchFailed = 102202,