diff --git a/src/data/nav/liveobjects.ts b/src/data/nav/liveobjects.ts
index 11e38a69e2..87732773d5 100644
--- a/src/data/nav/liveobjects.ts
+++ b/src/data/nav/liveobjects.ts
@@ -98,6 +98,10 @@ export default {
name: 'Object storage',
link: '/docs/liveobjects/storage',
},
+ {
+ name: 'Using the REST SDK',
+ link: '/docs/liveobjects/rest-sdk-usage',
+ },
{
name: 'Using the REST API',
link: '/docs/liveobjects/rest-api-usage',
diff --git a/src/pages/docs/api/rest-sdk/channels.mdx b/src/pages/docs/api/rest-sdk/channels.mdx
index c9863086d1..713b2b9526 100644
--- a/src/pages/docs/api/rest-sdk/channels.mdx
+++ b/src/pages/docs/api/rest-sdk/channels.mdx
@@ -82,6 +82,12 @@ Provides access to the [REST Presence](/docs/presence-occupancy/presence) object
Provides access to the [PushChannel](/docs/api/realtime-sdk/push#push-channel) object for this channel which can be used to access members present on the channel, or participate in presence.
+
+#### object
+
+Provides access to the [RestObject](/docs/liveobjects/rest-sdk-usage) for this channel which can be used to read and modify LiveObjects on a channel using the REST SDK.
+
+
### Channel Methods
#### publish Publish
diff --git a/src/pages/docs/liveobjects/rest-api-usage.mdx b/src/pages/docs/liveobjects/rest-api-usage.mdx
index 6a7c997148..1cf72680c1 100644
--- a/src/pages/docs/liveobjects/rest-api-usage.mdx
+++ b/src/pages/docs/liveobjects/rest-api-usage.mdx
@@ -632,7 +632,7 @@ There are additional operations for creating objects with client-generated IDs:
{
"objectId": "map:Qj2kkvprTybCY5mkNMcm31hhNKZCDWqcz45LjYvCABs@1769079911168",
"mapCreateWithObjectId": {
- "initialValue": "{\"entries\":{\"name\":{\"data\":{\"string\":\"Alice\"}},\"age\":{\"data\":{\"number\":30}}}}",
+ "initialValue": "{\"semantics\":0,\"entries\":{\"name\":{\"data\":{\"string\":\"Alice\"}},\"age\":{\"data\":{\"number\":30}}}}",
"nonce": "random-nonce-abc123"
}
}
@@ -677,7 +677,7 @@ For example:
-d '{
"objectId": "map:Qj2kkvprTybCY5mkNMcm31hhNKZCDWqcz45LjYvCABs@1769079911168",
"mapCreateWithObjectId": {
- "initialValue": "{\"entries\":{\"name\":{\"data\":{\"string\":\"Alice\"}},\"age\":{\"data\":{\"number\":30}}}}",
+ "initialValue": "{\"semantics\":0,\"entries\":{\"name\":{\"data\":{\"string\":\"Alice\"}},\"age\":{\"data\":{\"number\":30}}}}",
"nonce": "random-nonce-abc123"
}
}'
@@ -697,7 +697,7 @@ Create a map and immediately link it to root in a single atomic operation:
{
"objectId": "map:Qj2kkvprTybCY5mkNMcm31hhNKZCDWqcz45LjYvCABs@1769079911168",
"mapCreateWithObjectId": {
- "initialValue": "{\"entries\":{\"name\":{\"data\":{\"string\":\"Alice\"}}}}",
+ "initialValue": "{\"semantics\":0,\"entries\":{\"name\":{\"data\":{\"string\":\"Alice\"}}}}",
"nonce": "nonce-1"
}
},
diff --git a/src/pages/docs/liveobjects/rest-sdk-usage.mdx b/src/pages/docs/liveobjects/rest-sdk-usage.mdx
new file mode 100644
index 0000000000..66d8cda94d
--- /dev/null
+++ b/src/pages/docs/liveobjects/rest-sdk-usage.mdx
@@ -0,0 +1,937 @@
+---
+title: Using the REST SDK
+meta_description: "Learn how to work with Ably LiveObjects using the REST SDK"
+---
+
+
+LiveObjects JavaScript REST SDK is in Public Preview. We are committed to supporting the LiveObjects Javascript API and welcome adoption and feedback.
+
+**Building with LiveObjects?** Help shape its future by [sharing your use case](https://44qpp.share.hsforms.com/2fZobHQA1ToyRfB9xqZYQmQ).
+
+
+LiveObjects provides a JavaScript REST SDK that enables you to directly work with objects without using a realtime connection.
+
+## Setup
+
+Create an [`Ably.Rest`](/docs/api/rest-sdk) client instance with the `LiveObjects` plugin:
+
+
+```javascript
+import { LiveObjects } from 'ably/liveobjects';
+
+const rest = new Ably.Rest({ plugins: { LiveObjects }, /* other ClientOptions */ });
+```
+
+
+The LiveObjects REST SDK is then available on a channel via `channel.object`:
+
+
+```javascript
+const channel = rest.channels.get('my-channel');
+channel.object // LiveObjects REST SDK
+```
+
+
+## Authentication
+
+Authentication is configured when instantiating the REST client. Pass an API key or use [token authentication](/docs/auth/token) via the [`ClientOptions`](/docs/api/rest-sdk#client-options). See the [REST SDK authentication](/docs/api/rest-sdk/authentication) documentation for details.
+
+To use LiveObjects, an API key must have at least the `object-subscribe` capability. With only this capability, clients will have read-only access, preventing them from publishing operations.
+
+In order to create or update objects, make sure your API key includes both `object-subscribe` and `object-publish` [capabilities](/docs/auth/capabilities) to allow full read and write access.
+
+## Fetch objects
+
+### Use `channel.object.get`
+
+`get(GetObjectParams params?): Promise`
+
+Reads object data from the channel. Uses the channel's root object as the entrypoint when no `objectId` is provided. Makes a request to the [`GET /channels/{channelId}/object`](/docs/liveobjects/rest-api-usage#fetch-channel-object) REST API endpoint. The return type depends on the `compact` parameter: when `compact` is `true` (default), returns a JSON value; when `compact` is `false`, returns a [`RestLiveObject`](#rest-live-object).
+
+| Parameter | Description | Type |
+|-----------|-------------|------|
+| params | An optional object containing the query parameters | [`GetObjectParams`](#get-object-params) |
+
+Objects can be fetched in two formats:
+
+| Format | Parameter | Description |
+|--------|----------------|-------------|
+| **Compact** (default) | `compact: true` | Values-only representation without metadata. Ideal for reading data values. |
+| **Non-compact** | `compact: false` | Full structure including object IDs and type metadata. Useful for debugging. |
+
+**Compact format** returns the logical structure of your data as a JSON object. [LiveMap](/docs/liveobjects/map) instances appear as JSON objects with their entries, and [LiveCounter](/docs/liveobjects/counter) instances appear as numbers.
+
+**Non-compact format** includes additional [metadata](/docs/liveobjects/concepts/objects#metadata) for each object:
+- Object IDs for each instance
+- Object type metadata (map semantics, counter values)
+- Complete object hierarchy
+
+### Fetch the channel object
+
+Fetch the entire channel object:
+
+
+```javascript
+const data = await channel.object.get();
+```
+
+
+Example compact result:
+
+
+```json
+{
+ "votes": {
+ "down": 5,
+ "up": 10
+ }
+}
+```
+
+
+This example shows a `LiveMap` stored on the "votes" key of the channel object, which contains two `LiveCounter` instances on the "down" and "up" keys.
+
+Set `compact: false` to include object metadata:
+
+
+```javascript
+const data = await channel.object.get({ compact: false });
+```
+
+
+
+```json
+{
+ "objectId": "root",
+ "map": {
+ "semantics": "lww",
+ "entries": {
+ "votes": {
+ "data": {
+ "objectId": "map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692",
+ "map": {
+ "semantics": "lww",
+ "entries": {
+ "down": {
+ "data": {
+ "objectId": "counter:Yj1F_aEX3T2rRkTkra7Aifmlr8PxUWSR3kO3MzxtQto@1760448653393",
+ "counter": {
+ "data": {
+ "number": 5
+ }
+ }
+ }
+ },
+ "up": {
+ "data": {
+ "objectId": "counter:ibxddWpDjH8R3cvXWWacfe4IVd3DxT_oqkAafhaS68s@1760448646413",
+ "counter": {
+ "data": {
+ "number": 10
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+
+### Fetch by path
+
+Return a subset of the channel object by specifying the `path` parameter. For example, to return only the `votes` `LiveMap` instance from the channel object:
+
+
+```javascript
+const data = await channel.object.get({ path: 'votes' });
+```
+
+
+Example result:
+
+
+```json
+{
+ "down": 5,
+ "up": 10
+}
+```
+
+
+### Fetch by object ID
+
+Fetch a specific [object instance](/docs/liveobjects/concepts/instance) by specifying its `objectId`:
+
+
+```javascript
+const data = await channel.object.get({
+ objectId: 'map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692'
+});
+```
+
+
+Example compact result:
+
+
+```json
+{
+ "down": 5,
+ "up": 10
+}
+```
+
+
+Set `compact: false` to include object metadata:
+
+
+```javascript
+const data = await channel.object.get({
+ objectId: 'map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692',
+ compact: false
+});
+```
+
+
+
+```json
+{
+ "objectId": "map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692",
+ "map": {
+ "semantics": "lww",
+ "entries": {
+ "down": {
+ "data": {
+ "objectId": "counter:Yj1F_aEX3T2rRkTkra7Aifmlr8PxUWSR3kO3MzxtQto@1760448653393",
+ "counter": {
+ "data": {
+ "number": 5
+ }
+ }
+ }
+ },
+ "up": {
+ "data": {
+ "objectId": "counter:ibxddWpDjH8R3cvXWWacfe4IVd3DxT_oqkAafhaS68s@1760448646413",
+ "counter": {
+ "data": {
+ "number": 10
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+
+You can also specify a `path` parameter alongside `objectId`. The path is evaluated relative to the specified object instance:
+
+
+```javascript
+const data = await channel.object.get({
+ objectId: 'map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692',
+ path: 'down'
+});
+```
+
+
+
+```json
+5
+```
+
+
+## Publish operations
+
+### Use `channel.object.publish`
+
+`publish(RestObjectOperation operation): Promise`
+
+`publish(RestObjectOperation[] operations): Promise`
+
+Publishes one or more operations to modify objects on the channel. Makes a request to the [`POST /channels/{channelId}/object`](/docs/liveobjects/rest-api-usage#publishing-operations) REST API endpoint. When an array is provided, all operations are published as an atomic [batch](#batch-operations).
+
+| Parameter | Description | Type |
+|-----------|-------------|------|
+| operation | The operation or array of operations to publish | [`RestObjectOperation`](#rest-object-operation) or [`RestObjectOperation[]`](#rest-object-operation) |
+
+Returns a [`RestObjectPublishResult`](#rest-object-publish-result) containing the message ID and affected object IDs.
+
+Each operation includes:
+1. A reference to an object using either `objectId` or `path`. Create operations (`mapCreate`, `counterCreate`) can omit both to create a [standalone object](#create-standalone).
+2. An **operation-specific field** (`mapSet`, `counterInc`, etc.) containing the operation parameters
+
+Operations can target objects using `objectId`, `path`, or neither (for create operations that create [standalone objects](#create-standalone)):
+- `objectId` (string): The unique identifier of the object instance to create or update
+- `path` (string): The path to the object instance within the channel object
+
+Use dot-separated notation for paths (for example `votes.up`), relative to the channel object. An empty path `""` refers to the channel object itself. Paths can contain wildcards (`*`) to target multiple objects.
+
+
+When using path operations, the server resolves object IDs at the time it receives the request. Ably applies the operation to these specific object instances. If the object instances at the specified path change due to concurrent updates before Ably processes the operation, Ably does not apply the operation to the new object instances.
+
+
+
+See [PathObject](/docs/liveobjects/concepts/path-object) for details on path-based access in client SDKs.
+
+
+### Available operations
+
+LiveObjects supports the following operations:
+
+| Operation | Description |
+| --------- | ----------- |
+| `mapSet` | Sets a key/value pair in a `LiveMap`. |
+| `mapRemove` | Removes a key from a `LiveMap`. |
+| `counterInc` | Increments or decrements a `LiveCounter`. |
+| `mapCreate` | Creates a new `LiveMap` instance. |
+| `counterCreate` | Creates a new `LiveCounter` instance. |
+| `mapCreateWithObjectId` | Creates a new `LiveMap` with a [client-generated ID](#client-generated-ids). |
+| `counterCreateWithObjectId` | Creates a new `LiveCounter` with a [client-generated ID](#client-generated-ids). |
+
+To create an object, see [Create objects](#create-objects).
+
+Each operation has specific required and optional fields:
+
+#### mapSet
+
+
+```javascript
+await channel.object.publish({
+ path: 'user',
+ mapSet: {
+ key: 'username',
+ value: { string: 'alice' }
+ }
+});
+```
+
+
+Map values can be any of the supported [data value types](#data-values), including references to other objects.
+
+#### mapRemove
+
+
+```javascript
+await channel.object.publish({
+ path: 'user',
+ mapRemove: {
+ key: 'username'
+ }
+});
+```
+
+
+#### counterInc
+
+
+```javascript
+await channel.object.publish({
+ path: 'votes.up',
+ counterInc: {
+ number: 5
+ }
+});
+```
+
+
+
+There is no explicit `counterDec` operation. To decrement a counter, use `counterInc` with a negative number.
+
+
+#### mapCreate
+
+Optionally omit the `path` or `objectId` fields when creating an object with `mapCreate`.
+For the object to be [reachable](/docs/liveobjects/concepts/objects#reachability) in the state tree, assign it to a key in a `LiveMap` that is reachable from the channel object.
+
+
+```javascript
+await channel.object.publish({
+ path: 'posts.post1',
+ mapCreate: {
+ semantics: 'lww',
+ entries: {
+ title: { data: { string: 'LiveObjects is awesome' } },
+ createdAt: { data: { number: 1745835181122 } },
+ isPublished: { data: { boolean: true } }
+ }
+ }
+});
+```
+
+
+#### counterCreate
+
+Optionally omit the `path` or `objectId` fields when creating an object with `counterCreate`.
+For the object to be [reachable](/docs/liveobjects/concepts/objects#reachability) in the state tree, assign it to a key in a `LiveMap` that is reachable from the channel object.
+
+
+```javascript
+await channel.object.publish({
+ path: 'visits',
+ counterCreate: {
+ count: 0
+ }
+});
+```
+
+
+### Update by object ID
+
+To perform operations on a specific object instance, provide its `objectId` in the operation:
+
+
+```javascript
+const result = await channel.object.publish({
+ objectId: 'counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269',
+ counterInc: {
+ number: 1
+ }
+});
+```
+
+
+### Update by path
+
+Path operations target objects based on their location in the channel object.
+
+Paths are expressed relative to the structure of the object as defined by the [compact](#fetch-channel-object) view of the channel object.
+
+The following example increments the `LiveCounter` instance stored at the `up` key on the `votes` `LiveMap` object:
+
+
+```javascript
+const result = await channel.object.publish({
+ path: 'votes.up',
+ counterInc: {
+ number: 1
+ }
+});
+```
+
+
+### Publish result
+
+The result includes the ID of the published operation message, the channel and a list of object IDs that were affected by the operation:
+
+
+```json
+{
+ "messageId": "TJPWHhMTrF:0",
+ "channel": "my-channel",
+ "objectIds": ["counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269"]
+}
+```
+
+
+### Path operations
+
+Path operations provide flexibility when targeting objects.
+
+#### Path wildcards
+
+Use wildcards in paths to target multiple objects at once. To increment all `LiveCounter` instances in the `votes` `LiveMap` instance:
+
+
+```javascript
+const result = await channel.object.publish({
+ path: 'votes.*',
+ counterInc: {
+ number: 1
+ }
+});
+```
+
+
+The result includes the IDs of each of the affected object instances:
+
+
+```json
+{
+ "messageId": "0Q1w-LpA11:0",
+ "channel": "my-channel",
+ "objectIds": [
+ "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269",
+ "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669"
+ ]
+}
+```
+
+
+Wildcards match exactly one level in the channel object and can appear at the end or middle of paths. For example, given the following compact view of the channel object:
+
+
+```json
+{
+ "posts": {
+ "post1": {
+ "votes": {
+ "down": 5,
+ "up": 10
+ }
+ },
+ "post2": {
+ "votes": {
+ "down": 5,
+ "up": 10
+ }
+ }
+ }
+}
+```
+
+
+The following example increments the upvote `LiveCounter` instances for all posts in the `posts` `LiveMap` instance:
+
+
+```javascript
+const result = await channel.object.publish({
+ path: 'posts.*.votes.up',
+ counterInc: {
+ number: 1
+ }
+});
+```
+
+
+#### Escape special characters
+
+If your `LiveMap` keys contain periods, escape them with a backslash. The following example increments the upvote `LiveCounter` instance for a post with the key `post.123`:
+
+
+```javascript
+const result = await channel.object.publish({
+ path: 'posts.post\\.123.votes.up',
+ counterInc: {
+ number: 1
+ }
+});
+```
+
+
+### Remove objects
+
+Remove an object from the channel using `mapRemove` to delete the key referencing it:
+
+
+```javascript
+await channel.object.publish({
+ objectId: 'root',
+ mapRemove: {
+ key: 'posts'
+ }
+});
+```
+
+
+Map keys can be removed by issuing a `mapRemove` operation targeting either an `objectId` or a `path`.
+
+If no other references to the object exist, it becomes unreachable and is eligible for [garbage collection](#object-reachability).
+
+
+Removing a key may also make nested child objects unreachable. These objects are also eligible for garbage collection.
+
+
+## Create objects
+
+Use `mapCreate` and `counterCreate` operations to create new LiveObjects [instances](/docs/liveobjects/concepts/instance).
+
+### Create objects with paths
+
+The simplest way to create an object is to specify a `path` where it should be created. The server automatically creates the object and assigns it to that path in a single atomic operation.
+
+The following example creates a new `LiveMap` instance and assigns it to the `posts` `LiveMap` instance on the channel object under the key `post1`:
+
+
+```javascript
+const result = await channel.object.publish({
+ path: 'posts.post1',
+ mapCreate: {
+ semantics: 'lww',
+ entries: {
+ title: { data: { string: 'LiveObjects is awesome' } },
+ createdAt: { data: { number: 1745835181122 } },
+ isPublished: { data: { boolean: true } }
+ }
+ }
+});
+```
+
+
+When using `path` with a create operation, the server constructs two operations published as a [batch](#batch-operations):
+
+1. A `mapCreate` or `counterCreate` operation to create the new object
+2. A `mapSet` operation to assign the new object to the parent `LiveMap` at the specified path
+
+This ensures the new object is immediately [reachable](#object-reachability) from the root.
+
+The result will include the object IDs of all objects affected by the resulting set of operations.
+The newly created object's ID will be the first item in the list:
+
+
+```json
+{
+ "messageId": "mkfjWU2jju:0",
+ "channel": "my-channel",
+ "objectIds": [
+ "map:cRCKx-eev7Tl66jGfl1SkZh_uEMo6F5jyV0B7mUn4Zs@1745835549101",
+ "map:a_oQqPYUGxi95_Cn0pWcsoeBlHZZtVW5xKIw0hnJCZs@1745835547258"
+ ]
+}
+```
+
+
+### Create standalone objects
+
+Create objects without immediately assigning them by omitting both `objectId` and `path` from the create operation.
+
+
+```javascript
+const result = await channel.object.publish({
+ mapCreate: {
+ semantics: 'lww',
+ entries: {
+ name: { data: { string: 'Alice' } }
+ }
+ }
+});
+```
+
+
+The result includes the generated object ID:
+
+
+```json
+{
+ "messageId": "TJPWHhMTrF:0",
+ "channel": "my-channel",
+ "objectIds": ["map:abc123def456...@1745835549101"]
+}
+```
+
+
+
+Standalone objects are not reachable until assigned to the object tree. Unreachable objects are eligible for garbage collection. See [Object reachability and garbage collection](#object-reachability) for details.
+
+
+### Client-generated object IDs
+
+Client-generated object IDs enable atomic batch operations with cross-references between newly created objects. See the [REST API documentation](/docs/liveobjects/rest-api-usage#client-generated-ids) for details on how to generate object IDs.
+
+#### Publish with client-generated IDs
+
+Use `mapCreateWithObjectId` or `counterCreateWithObjectId` to create an object with a pre-computed ID:
+
+
+```javascript
+const result = await channel.object.publish({
+ objectId: 'map:Qj2kkvprTybCY5mkNMcm31hhNKZCDWqcz45LjYvCABs@1769079911168',
+ mapCreateWithObjectId: {
+ initialValue: '{"semantics":0,"entries":{"name":{"data":{"string":"Alice"}},"age":{"data":{"number":30}}}}',
+ nonce: 'random-nonce-abc123'
+ }
+});
+```
+
+
+#### Atomic batch with cross-references
+
+Create a map and immediately link it to root in a single atomic batch:
+
+
+```javascript
+const result = await channel.object.publish([
+ {
+ objectId: 'map:Qj2kkvprTybCY5mkNMcm31hhNKZCDWqcz45LjYvCABs@1769079911168',
+ mapCreateWithObjectId: {
+ initialValue: '{"semantics":0,"entries":{"name":{"data":{"string":"Alice"}}}}',
+ nonce: 'nonce-1'
+ }
+ },
+ {
+ objectId: 'root',
+ mapSet: {
+ key: 'alice',
+ value: { objectId: 'map:Qj2kkvprTybCY5mkNMcm31hhNKZCDWqcz45LjYvCABs@1769079911168' }
+ }
+ }
+]);
+```
+
+
+Both operations execute atomically. The second operation references the object created in the first because you pre-computed the ID.
+
+
+Only use client-generated IDs when you need atomic batch operations with cross-references. For most use cases, use simple operations or path-based operations instead.
+
+
+## Cyclic references
+
+For both the full object and the compact formats, cyclic references in the channel object are included as a reference to the object ID rather than including the same object instance in the result more than once.
+
+For example, if you create a cycle in the channel object by adding a reference to the channel object in the `votes` `LiveMap` instance with the following operation:
+
+
+```javascript
+await channel.object.publish({
+ objectId: 'map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692',
+ mapSet: {
+ key: 'myRoot',
+ value: { objectId: 'root' }
+ }
+});
+```
+
+
+The result will handle the cyclic reference by including the `myRoot` key as a reference to the object ID of the channel object:
+
+
+```javascript
+const data = await channel.object.get();
+```
+
+
+
+```json
+{
+ "votes": {
+ "down": 5,
+ "up": 10,
+ "myRoot": {
+ "objectId": "root"
+ }
+ }
+}
+```
+
+
+## Object reachability and garbage collection
+
+Objects that are not reachable from the channel object are automatically garbage collected. See the [REST API documentation](/docs/liveobjects/rest-api-usage#object-reachability) and [reachability and object lifecycle](/docs/liveobjects/concepts/objects#reachability) for details.
+
+## Batch operations
+
+Group multiple operations into a single call by passing an array of operations to `publish()`. All operations are published as a single message and processed as a single atomic unit. Learn more about [batch operations](/docs/liveobjects/batch).
+
+The following example increments two distinct `LiveCounter` instances in a single batch operation:
+
+
+```javascript
+await channel.object.publish([
+ {
+ objectId: 'counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269',
+ counterInc: {
+ number: 1
+ }
+ },
+ {
+ objectId: 'counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669',
+ counterInc: {
+ number: 1
+ }
+ }
+]);
+```
+
+
+## Idempotent operations
+
+Publish operations idempotently by specifying an `id` for the operation, using the same approach as [idempotent message publishing](/docs/api/liveobjects-rest#idempotent-publish):
+
+
+```javascript
+await channel.object.publish({
+ id: 'my-idempotency-key',
+ objectId: 'counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269',
+ counterInc: {
+ number: 1
+ }
+});
+```
+
+
+For batch operations, use the format `:` where the index is the zero-based index of the operation in the array:
+
+
+```javascript
+await channel.object.publish([
+ {
+ id: 'my-idempotency-key:0',
+ objectId: 'counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269',
+ counterInc: {
+ number: 1
+ }
+ },
+ {
+ id: 'my-idempotency-key:1',
+ objectId: 'counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669',
+ counterInc: {
+ number: 1
+ }
+ }
+]);
+```
+
+
+## Data values reference
+
+When working with objects via the REST SDK, [primitive types](/docs/liveobjects/concepts/objects#primitive-types) and [object references](/docs/liveobjects/concepts/objects#composability) are represented as typed value objects.
+
+The key in the value object indicates the type of the value. Examples of data value formats:
+
+
+```javascript
+{ number: 42 }
+{ string: 'LiveObjects is awesome' }
+{ boolean: true }
+{ bytes: new Uint8Array([76, 105, 118, 101]) }
+{ objectId: 'counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669' }
+{ json: { someKey: 'someValue' } }
+```
+
+
+
+Over the wire, `bytes` values are base64-encoded and `json` values are JSON-stringified strings, matching the [REST API format](/docs/liveobjects/rest-api-usage#data-values). The SDK automatically encodes these values when publishing operations and decodes them when fetching data, so you can work with native types directly: `ArrayBuffer` `Buffer` for `bytes`, and plain objects or arrays for `json`.
+
+
+## Related types
+
+### GetObjectParams
+
+Parameters for the [`get()`](#get) method:
+
+| Property | Description | Type |
+|----------|-------------|------|
+| objectId | The unique identifier of the [object instance](/docs/liveobjects/concepts/instance) to fetch. If omitted, fetches from the channel's root object | `String` (optional) |
+| path | A dot-separated path to return a subset of the object. Evaluated relative to the root or the specified `objectId` | `String` (optional) |
+| compact | When `true` (default), returns a values-only representation. When `false`, includes object IDs and type metadata | `Boolean` (optional) |
+
+### RestObjectOperation
+
+An operation passed to the [`publish()`](#publish) method. Each operation contains an optional `id` for [idempotent publishing](#idempotent-operations), an optional target (`objectId` or `path`), and exactly one operation-specific field containing the operation parameters:
+
+| Property | Description | Type |
+|----------|-------------|------|
+| id | Identifier for [idempotent publishing](#idempotent-operations) | `String` (optional) |
+| objectId | The unique identifier of the [object instance](/docs/liveobjects/concepts/instance) to target | `String` (optional) |
+| path | The dot-separated path to the object instance within the channel object. Evaluated relative to the root | `String` (optional) |
+| mapSet | Parameters for setting a key to a value in a `LiveMap` | [`MapSet`](#map-set) (optional) |
+| mapRemove | Parameters for removing a key from a `LiveMap` | [`MapRemove`](#map-remove) (optional) |
+| counterInc | Parameters for incrementing a `LiveCounter` | [`CounterInc`](#counter-inc) (optional) |
+| mapCreate | Parameters for creating a new `LiveMap` | [`MapCreate`](#map-create) (optional) |
+| counterCreate | Parameters for creating a new `LiveCounter` | [`CounterCreate`](#counter-create) (optional) |
+| mapCreateWithObjectId | Parameters for creating a new `LiveMap` with a [client-generated ID](#client-generated-ids) | [`CreateWithObjectId`](#create-with-object-id) (optional) |
+| counterCreateWithObjectId | Parameters for creating a new `LiveCounter` with a [client-generated ID](#client-generated-ids) | [`CreateWithObjectId`](#create-with-object-id) (optional) |
+
+### MapSet
+
+| Property | Description | Type |
+|----------|-------------|------|
+| key | The key to set | `String` |
+| value | The value to assign to the key | [`ObjectData`](#object-data) |
+
+### MapRemove
+
+| Property | Description | Type |
+|----------|-------------|------|
+| key | The key to remove | `String` |
+
+### CounterInc
+
+| Property | Description | Type |
+|----------|-------------|------|
+| number | The amount to increment by. Use a negative value to decrement | `Number` |
+
+### MapCreate
+
+| Property | Description | Type |
+|----------|-------------|------|
+| semantics | The conflict-resolution semantics for the map. One of: `'lww'` | `String` |
+| entries | Initial key-value pairs, keyed by string | `Record` |
+
+### CounterCreate
+
+| Property | Description | Type |
+|----------|-------------|------|
+| count | The initial value of the counter | `Number` |
+
+### RestDataMapEntry
+
+| Property | Description | Type |
+|----------|-------------|------|
+| data | The value for this entry | [`ObjectData`](#object-data) |
+
+### CreateWithObjectId
+
+Used by `mapCreateWithObjectId` and `counterCreateWithObjectId`:
+
+| Property | Description | Type |
+|----------|-------------|------|
+| initialValue | JSON-encoded string of the object data, matching the format from the corresponding create operation | `String` |
+| nonce | Random string used to generate the object ID | `String` |
+
+### RestLiveObject
+
+Returned by [`get()`](#get) when `compact` is `false`. An expanded representation that includes object metadata. Can be a [`RestLiveMap`](#rest-live-map), [`RestLiveCounter`](#rest-live-counter), or a generic object with an `objectId` property.
+
+#### RestLiveMap
+
+| Property | Description | Type |
+|----------|-------------|------|
+| objectId | The ID of the map object | `String` |
+| map | The map data | [`RestLiveMapValue`](#rest-live-map-value) |
+
+#### RestLiveMapValue
+
+| Property | Description | Type |
+|----------|-------------|------|
+| semantics | The conflict-resolution semantics. One of: `'lww'` | `String` |
+| entries | The map entries, indexed by key. Each entry is either a [`RestDataMapEntry`](#rest-data-map-entry) (leaf value) or a [`RestLiveObjectMapEntry`](#rest-live-object-map-entry) (nested object) | `Record` |
+
+#### RestLiveObjectMapEntry
+
+| Property | Description | Type |
+|----------|-------------|------|
+| data | A nested object | [`RestLiveObject`](#rest-live-object) |
+
+#### RestLiveCounter
+
+| Property | Description | Type |
+|----------|-------------|------|
+| objectId | The ID of the counter object | `String` |
+| counter | The counter data | [`RestLiveCounterValue`](#rest-live-counter-value) |
+
+#### RestLiveCounterValue
+
+| Property | Description | Type |
+|----------|-------------|------|
+| data | Holds the counter value | `{ number: Number }` |
+
+### ObjectData
+
+Represents a leaf data value for an object. Either a primitive value or a reference to another object. Exactly one property is set, indicating the type:
+
+| Property | Description | Type |
+|----------|-------------|------|
+| string | A string value | `String` |
+| number | A numeric value | `Number` |
+| boolean | A boolean value | `Boolean` |
+| bytes | A binary value | `ArrayBuffer` `Buffer` |
+| json | A JSON value (array or object) | `Array` / `Object` |
+| objectId | A reference to another object by its ID | `String` |
+
+### RestObjectPublishResult
+
+Result returned by the [`publish()`](#publish) method:
+
+| Property | Description | Type |
+|----------|-------------|------|
+| messageId | The ID of the message containing the published operations | `String` |
+| channel | The name of the channel the operations were published to | `String` |
+| objectIds | Array of object IDs affected by the operations. May include multiple IDs for wildcard paths and batch operations | `String[]` |