diff --git a/src/pages/docs/liveobjects/rest-api-usage.mdx b/src/pages/docs/liveobjects/rest-api-usage.mdx index 5b03d5b4db..f7d97a78be 100644 --- a/src/pages/docs/liveobjects/rest-api-usage.mdx +++ b/src/pages/docs/liveobjects/rest-api-usage.mdx @@ -39,360 +39,164 @@ The key in the `data` object indicates the type of the value: `bytes` values use base64 string encoding in JSON request and response bodies. -## Fetching objects +## Fetch the channel object -### List objects +### Get the entire object -`GET main.realtime.ably.net/channels//objects` - -Fetch a flat list of objects on the channel: +The following request returns the entire channel object. ```shell - curl "https://main.realtime.ably.net/channels/my-channel/objects" \ + curl "https://main.realtime.ably.net/channels/my-channel/object" \ -u {{API_KEY}} ``` -The response includes the IDs of the objects on the channel: +This endpoint returns the compact representation of the channel object. -```json -[ - "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669" - "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269", - "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519", - "root", -] -``` + ```json + { + "votes": { + "down": 10, + "up": 5, + } + } + ``` -This request is billed per object ID returned - each object ID counts as one billable message. - -#### Including values +Each instance of an [object type](/docs/liveobjects/concepts/objects#object-types) included is the response is counted as one billable message. -To include values of the objects in the response, set the `values=true` query parameter: +#### Get the entire object with metadata +You can optionally include additional object [metadata](/docs/liveobjects/concepts/objects#metadata) in the response by specifying the `compact=false` query option: -```shell - curl "https://main.realtime.ably.net/channels/my-channel/objects?values=true" \ - -u {{API_KEY}} -``` + ```shell + curl "https://main.realtime.ably.net/channels/my-channel/object?compact=false" \ + -u {{API_KEY}} + ``` -This request is billed per object returned in the result - each object counts as one billable message. - -```json -[ - { - "objectId": "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669", - "counter": { - "data": { - "number": 10 - } - } - }, - { - "objectId": "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269", - "counter": { - "data": { - "number": 5 - } - } - }, - { - "objectId": "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519", - "map": { - "entries": { - "down": { - "data": { - "objectId": "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669" - } - }, - "up": { - "data": { - "objectId": "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269" - } - } - } - } - }, + ```json { "objectId": "root", "map": { + "semantics": "LWW", "entries": { "votes": { "data": { - "objectId": "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519" + "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 + } + } + } + } + } + } } } } } - } -] -``` +} + + ``` -#### Including metadata +### Query the object by path -You can optionally include additional object [metadata](/docs/liveobjects/concepts/objects#metadata) with the `metadata` query option: +You can return a subset of the channel object by specifying the `path` query param. For example, to return just the `votes` `LiveMap` instance from the root object: -```shell - curl "https://main.realtime.ably.net/channels/my-channel/objects?values=true&metadata=true" \ - -u {{API_KEY}} -``` + ```shell + curl "https://main.realtime.ably.net/channels/my-channel/object?path=votes" \ + -u {{API_KEY}} + ``` -```json -[ - { - "objectId": "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669", - "counter": { - "data": { - "number": 10 - } - }, - "siteTimeserials": { - "e02": "01745828651671-000@e025VxXLABoR0C19591332:000" - }, - "tombstone": false - }, - { - "objectId": "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269", - "counter": { - "data": { - "number": 5 - } - }, - "siteTimeserials": { - "e02": "01745828645271-000@e025VxXLABoR0C19591332:000" - }, - "tombstone": false - }, + ```json { - "objectId": "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519", - "map": { - "mapSemantics": "LWW", - "entries": { - "down": { - "timeserial": "01745828651671-000@e025VxXLABoR0C19591332:001", - "tombstone": false, - "data": { - "objectId": "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669" - } - }, - "up": { - "timeserial": "01745828645271-000@e025VxXLABoR0C19591332:001", - "tombstone": false, - "data": { - "objectId": "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269" - } - } - } - }, - "siteTimeserials": { - "e02": "01745828651671-000@e025VxXLABoR0C19591332:001" - }, - "tombstone": false - }, - { - "objectId": "root", - "map": { - "mapSemantics": "LWW", - "entries": { - "votes": { - "timeserial": "01745828596522-000@e025VxXLABoR0C19591332:001", - "tombstone": false, - "data": { - "objectId": "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519" - } - } - } - }, - "siteTimeserials": { - "e02": "01745828596522-000@e025VxXLABoR0C19591332:001" - }, - "tombstone": false + "down": 5, + "up": 10 } -] -``` + ``` - +Each instance of an [object type](/docs/liveobjects/concepts/objects#object-types) included is the response is counted as one billable message. -#### Pagination +### Get an object by id -The response can be [paginated](/docs/api/rest-api#pagination) with `cursor` and `limit` query params using relative links. - -Use the `limit` query parameter to specify the maximum number of objects to include in the response: +To fetch a specific instance of an object type on the channel you can specify the object ID in the URL path: ```shell - curl -v "https://main.realtime.ably.net/channels/my-channel/objects?values=true&limit=2" \ + curl "https://main.realtime.ably.net/channels/my-channel/object/map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692" \ -u {{API_KEY}} ``` +This endpoint supports the `path` and `compact` query options. Note that the path is evaluated relative to the specified object instance. + -```json -[ - { - "objectId": "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669", - "counter": { - "data": { - "number": 10 - } - } - }, + ```json { - "objectId": "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269", - "counter": { - "data": { - "number": 5 - } - } + "down": 5, + "up": 10 } -] -``` + ``` -The response includes `Link` headers which provide relative links to the first, current and next pages of the response: - - -```json -link: ; rel="first" -link: ; rel="current" -link: ; rel="next" -``` - - -The list objects endpoints returns objects ordered lexicographically by object ID. The object ID of the first object in the next page is used as the `cursor` value for the next request: +The full object response can be retrieved by providing the `compact=false` query param. -```shell - curl "https://main.realtime.ably.net/channels/my-channel/objects?cursor=map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519&limit=2&values=true" \ - -u {{API_KEY}} -``` + ```shell + curl "https://main.realtime.ably.net/channels/my-channel/object/map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692?compact=false" \ + -u {{API_KEY}} + ``` -```json -[ + ```json { - "objectId": "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519", + "objectId": "map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692", "map": { + "semantics": "LWW", "entries": { "down": { "data": { - "objectId": "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669" + "objectId": "counter:Yj1F_aEX3T2rRkTkra7Aifmlr8PxUWSR3kO3MzxtQto@1760448653393", + "counter": { + "data": { + "number": 5 + } + } } }, "up": { "data": { - "objectId": "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269" - } - } - } - } - }, - { - "objectId": "root", - "map": { - "entries": { - "votes": { - "data": { - "objectId": "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519" - } - } - } - } - } -] -``` - - -### Get objects - -`GET main.realtime.ably.net/channels//objects/` - -#### Get a single object - -To fetch a single object on the channel, specify the object ID in the URL path: - - -```shell - curl "https://main.realtime.ably.net/channels/my-channel/objects/root" \ - -u {{API_KEY}} -``` - - -The response contains a single object referencing any nested child objects by their object ID: - - -```json -{ - "objectId": "root", - "map": { - "entries": { - "votes": { - "data": { "objectId": "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519" } - } - } - } -} -``` - - -This request is counted as one billable message. - -#### Get an object and its children - -To fetch the objects on the channel in a tree structure use the `children` query parameter: - - -```shell - curl "https://main.realtime.ably.net/channels/my-channel/objects/root?children=true" \ - -u {{API_KEY}} -``` - - -The response includes the object tree starting from the specified object ID. Nested child objects are resolved and their values are included in the response: - - -```json -{ - "objectId": "root", - "map": { - "entries": { - "votes": { - "data": { - "objectId": "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519", - "map": { - "entries": { - "down": { - "data": { - "objectId": "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669", - "counter": { - "data": { - "number": 10 - } - } - } - }, - "up": { - "data": { - "objectId": "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269", - "counter": { - "data": { - "number": 5 - } - } - } + "objectId": "counter:ibxddWpDjH8R3cvXWWacfe4IVd3DxT_oqkAafhaS68s@1760448646413", + "counter": { + "data": { + "number": 10 } } } @@ -400,163 +204,45 @@ The response includes the object tree starting from the specified object ID. Nes } } } -} -``` + ``` -Use `root` as the object ID in the URL to get the full object tree, or any other object ID to fetch a subset of the tree using that object as the entrypoint. -This request is billed per non-tombstoned object returned in the result - each object counts as one billable message. - -##### Including metadata - -You can optionally include additional object [metadata](/docs/liveobjects/concepts/objects#metadata) for all objects included in the response with the `metadata` query option: +The `path` param can also be provided here to return a subset of the channel object starting from the specified object ID. -```shell - curl "https://main.realtime.ably.net/channels/my-channel/objects/root?children=true&metadata=true" \ - -u {{API_KEY}} -``` + ```shell + curl "https://main.realtime.ably.net/channels/my-channel/object/map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692?path=votes" \ + -u {{API_KEY}} + ``` -```json -{ - "objectId": "root", - "map": { - "mapSemantics": "LWW", - "entries": { - "votes": { - "timeserial": "01745828596522-000@e025VxXLABoR0C19591332:001", - "tombstone": false, - "data": { - "objectId": "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519", - "map": { - "mapSemantics": "LWW", - "entries": { - "down": { - "timeserial": "01745828651671-000@e025VxXLABoR0C19591332:001", - "tombstone": false, - "data": { - "objectId": "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669", - "counter": { - "data": { - "number": 10 - } - }, - "siteTimeserials": { - "e02": "01745828651671-000@e025VxXLABoR0C19591332:000" - }, - "tombstone": false - } - }, - "up": { - "timeserial": "01745828645271-000@e025VxXLABoR0C19591332:001", - "tombstone": false, - "data": { - "objectId": "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269", - "counter": { - "data": { - "number": 5 - } - }, - "siteTimeserials": { - "e02": "01745828645271-000@e025VxXLABoR0C19591332:000" - }, - "tombstone": false - } - } - } - }, - "siteTimeserials": { - "e02": "01745828651671-000@e025VxXLABoR0C19591332:001" - }, - "tombstone": false - } - } - } - }, - "siteTimeserials": { - "e02": "01745828596522-000@e025VxXLABoR0C19591332:001" - }, - "tombstone": false -} -``` - - -#### Pagination - -When fetching objects in a tree structure, the response can be paginated using the `limit` query parameter to specify the maximum number of objects to include in the response. If a nested child object exists which cannot be included in the response because the limit has been reached, it will be included by reference to its object ID rather than its value: - - -```shell - curl "https://main.realtime.ably.net/channels/my-channel/objects/root?children=true&limit=1" \ - -u {{API_KEY}} -``` - - - -```json -{ - "objectId": "root", - "map": { - "entries": { - "votes": { - "data": { - "objectId": "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519" - } - } - } + ```json + { + "down": 5, + "up": 10 } -} -``` - - -To obtain the next page, make a subsequent query specifying the referenced object ID as the entrypoint: - - -```shell - curl "https://main.realtime.ably.net/channels/my-channel/objects/map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519?children=true&limit=1" \ - -u {{API_KEY}} -``` + ``` - -```json -{ - "objectId": "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519", - "map": { - "entries": { - "down": { - "data": { - "objectId": "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669" - } - }, - "up": { - "data": { - "objectId": "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269" - } - } - } - } -} -``` - +Each instance of an [object type](/docs/liveobjects/concepts/objects#object-types) included is the response is counted as one billable message. -#### Cyclic references +### Cyclic references -When using the `children` query parameter, cyclic references in the object tree will be included as a reference to the object ID rather than including the same object in the response more than once. +For both the full object and the compact response formats cyclic references in the channel object will be included as a +reference to the object ID rather than including the same object instance in the response more than once. -For example, if we created a cycle in the object tree by adding a reference to the root object in the `votes` `LiveMap` instance with the following operation: +For example, if we created a cycle in the channel object by adding a reference to the root object in the `votes` `LiveMap` instance with the following operation: ```shell - curl -X POST "https://main.realtime.ably.net/channels/my-channel/objects" \ + curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \ -u {{API_KEY}} \ -H "Content-Type: application/json" \ -d '{ "operation": "MAP_SET", - "objectId": "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519", + "objectId": "map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692", "data": {"key": "myRoot", "value": {"objectId": "root"}} }' ``` @@ -566,114 +252,27 @@ The response will handle the cyclic reference by including the `myRoot` key in t ```shell - curl "https://main.realtime.ably.net/channels/my-channel/objects/root?children=true" \ + curl "https://main.realtime.ably.net/channels/my-channel/object" \ -u {{API_KEY}} ``` -```json -{ - "objectId": "root", - "map": { - "entries": { - "votes": { - "data": { - "objectId": "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519", - "map": { - "entries": { - "down": { - "data": { - "objectId": "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669", - "counter": { - "data": { - "number": 10 - } - } - } - }, - "up": { - "data": { - "objectId": "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269", - "counter": { - "data": { - "number": 5 - } - } - } - }, - "myRoot": { - "data": { - "objectId": "root" - } - } - } - } - } - } + ```json + { + "votes": { + "down": 5, + "up": 10 + "myRoot": { + "objectId": "root" + }, } } -} -``` - - -### Get a compact view of objects - -`GET main.realtime.ably.net/channels//objects//compact` - -To fetch the objects on the channel in a tree structure in a concise format: - - -```shell - curl "https://main.realtime.ably.net/channels/my-channel/objects/root/compact" \ - -u {{API_KEY}} -``` - - -The response includes a compact representation of the object tree that is easy to read: - - -```json -{ - "votes": { - "up": 5, - "down": 10 - } -} -``` - - -When using the compact format: - -* `LiveMap` instances will be represented as a JSON representation of its entries -* `LiveCounter` instances will be represented as its numeric value - -[Cyclic references](#fetching-objects-get-cycles) are handled in the same way as for the tree-structured response. In the example below, the `myRoot` key references the root object, which is already included in the response: - - -```json -{ - "votes": { - "up": 5, - "down": 10, - "myRoot": { "objectId": "root" } - } -} -``` + ``` -The compact format inlines object ID references under the `objectId` key, allowing references to other objects to be differentiated from string values. - -This request is billed per non-tombstoned object used to create the resulting compact view - each object counts as one billable message. - - - ## Publishing operations -`POST main.realtime.ably.net/channels//objects` - All operations are published to the same endpoint. The request body specifies: * The type of operation to publish @@ -693,6 +292,25 @@ The request body is of the form: ``` +Example setting a key on the channel object: + + + ```shell + curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \ + -u {{API_KEY}} \ + -H "Content-Type: application/json" \ + -d '{ + "operation": "MAP_SET", + "path": "", + "data": { + "key" : "myKey", + "value": {"string": "myValue"} + } + }' + ``` + + + The `operationType` is a string that specifies the type of operation to publish and must be one of: | Operation | Description | @@ -719,7 +337,7 @@ To perform operations on a specific object instance, you need to provide its obj ```shell - curl -X POST "https://main.realtime.ably.net/channels/my-channel/objects" \ + curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \ -u {{API_KEY}} \ -H "Content-Type: application/json" \ -d '{ @@ -744,17 +362,17 @@ The response includes the ID of the published operation message, the channel and ### Update an object by path -Path operations provide a convenient way to target objects based on their location in the object tree. +Path operations provide a convenient way to 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](#fetching-objects-compact) view of the object tree. +Paths are expressed relative to the structure of the object as defined by the [compact](#fetching-object-get) view of the channel object. -For example, given the following compact view of the object tree: +For example, given the following compact view of the channel object: -The following example increments the `LiveCounter` instance stored at the `up` key on the `votes` `LiveMap` instance on the root object: +The following example increments the `LiveCounter` instance stored at the `up` key on the `votes` `LiveMap` object: ```shell - curl -X POST "https://main.realtime.ably.net/channels/my-channel/objects" \ + curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \ -u {{API_KEY}} \ -H "Content-Type: application/json" \ -d '{ @@ -775,7 +393,7 @@ You can use wildcards in paths to target multiple objects at once. To increment ```shell - curl -X POST "https://main.realtime.ably.net/channels/my-channel/objects" \ + curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \ -u {{API_KEY}} \ -H "Content-Type: application/json" \ -d '{ @@ -801,7 +419,7 @@ The response includes the IDs of each of the affected object instances: ``` -Wildcards can be included at the end or in the middle of paths and will match exactly one level in the object tree. For example, given the following compact view of the object tree: +Wildcards can be included at the end or in the middle of paths and will match exactly one level in the channel object. For example, given the following compact view of the channel object: ```json @@ -828,7 +446,7 @@ The following example increments the upvote `LiveCounter` instances for all post ```shell - curl -X POST "https://main.realtime.ably.net/channels/my-channel/objects" \ + curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \ -u {{API_KEY}} \ -H "Content-Type: application/json" \ -d '{ @@ -843,7 +461,7 @@ If your `LiveMap` keys contain periods, you can escape them with a backslash. Th ```shell - curl -X POST "https://main.realtime.ably.net/channels/my-channel/objects" \ + curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \ -u {{API_KEY}} \ -H "Content-Type: application/json" \ -d '{ @@ -884,15 +502,15 @@ For `COUNTER_CREATE`, the `data` field should be a JSON object that contains the ``` -When you create a new object it is important that the new object is assigned to the object tree so that it is [reachable](/docs/liveobjects/concepts/objects#reachability) from the root object. +When you create a new object it is important that the new object instance is assigned to the channel object so that it is [reachable](/docs/liveobjects/concepts/object#reachability). -The simplest way to do this is to use the `path` field in the request body. The path is relative to the root object and specifies where in the object tree the new object should be created. +The simplest way to do this is to use the `path` field in the request body. The path is relative to the root object and specifies where in the channel object the new object should be created. The following example creates a new `LiveMap` instance and assigns it to the `posts` `LiveMap` instance on the root object under the key `post1`: ```shell - curl -X POST "https://main.realtime.ably.net/channels/my-channel/objects" \ + curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \ -u {{API_KEY}} \ -H "Content-Type: application/json" \ -d '{ @@ -907,6 +525,24 @@ The following example creates a new `LiveMap` instance and assigns it to the `po ``` +The following example shows how to update a key and value in a `LiveMap`: + + +```shell + curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \ + -u {{API_KEY}} \ + -H "Content-Type: application/json" \ + -d '{ + "operation": "MAP_SET", + "path": "posts.post1", + "data": { + "key" : "title", + "value": {"string": "LiveObjects is still awesome"} + } + }' +``` + + When using the `path` specifier with a `COUNTER_CREATE` or `MAP_CREATE` operation, the server constructs *two* operations which are published as a [batch](#batch-operations): * A `MAP_CREATE` or `COUNTER_CREATE` operation used to create the new object @@ -935,7 +571,7 @@ Remove a reference to a nested object in a `LiveMap` instance using the `MAP_REM ```shell - curl -X POST "https://main.realtime.ably.net/channels/my-channel/objects" \ + curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \ -u {{API_KEY}} \ -H "Content-Type: application/json" \ -d '{ @@ -962,7 +598,7 @@ The following example shows how to increment two distinct `LiveCounter` instance ```shell - curl -X POST "https://main.realtime.ably.net/channels/my-channel/objects" \ + curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \ -u {{API_KEY}} \ -H "Content-Type: application/json" \ -d '[ @@ -986,7 +622,7 @@ Publish operations idempotently in the same way as for [idempotent message publi ```shell - curl -X POST "https://main.realtime.ably.net/channels/my-channel/objects" \ + curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \ -u {{API_KEY}} \ -H "Content-Type: application/json" \ -d '{ @@ -1002,7 +638,7 @@ For batch operations, use the format `:` where the index is the z ```shell - curl -X POST "https://main.realtime.ably.net/channels/my-channel/objects" \ + curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \ -u {{API_KEY}} \ -H "Content-Type: application/json" \ -d '[ diff --git a/static/open-specs/liveobjects.yaml b/static/open-specs/liveobjects.yaml index bc3ac4443f..d2a1bbcd4f 100644 --- a/static/open-specs/liveobjects.yaml +++ b/static/open-specs/liveobjects.yaml @@ -14,13 +14,691 @@ servers: description: Production server for Ably REST API paths: + /channels/{channelId}/object: + post: + summary: Publishing operations + description: | + Publish operations to create or update objects on the channel.

+ + For more information, see the usage documentation for [publishing operations](/docs/liveobjects/rest-api-usage#publishing-operations). + parameters: + - name: channelId + in: path + required: true + description: The channel name. + schema: + type: string + example: "my-channel" + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/OperationRequest' + examples: + incObjectId: + summary: Increment a specific LiveCounter object + value: + operation: "COUNTER_INC" + objectId: "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269" + data: + number: 1 + incPath: + summary: Increment a LiveCounter object by path + value: + operation: "COUNTER_INC" + path: "votes.up" + data: + number: 1 + incPathWildcard: + summary: Increment multiple LiveCounter objects by wildcard path + value: + operation: "COUNTER_INC" + path: "votes.*" + data: + number: 1 + remove: + summary: Remove a LiveMap entry + value: + operation: "MAP_REMOVE" + objectId: "root" + data: + key: "posts" + createMap: + summary: Create a new LiveMap object + value: + operation: "MAP_CREATE" + data: + title: + string: "LiveObjects is awesome" + createdAt: + number: 1745835181122 + isPublished: + boolean: true + createCounter: + summary: Create a new LiveCounter object + value: + operation: "COUNTER_CREATE" + data: + number: 5 + createMapPath: + summary: Create a new LiveMap object at a specific path + value: + operation: "MAP_CREATE" + path: "posts.post1" + data: + title: + string: "LiveObjects is awesome" + createdAt: + number: 1745835181122 + isPublished: + boolean: true + responses: + '200': + description: Successfully published operation. + content: + application/json: + schema: + $ref: '#/components/schemas/PublishResponse' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + badRequest: + summary: Bad request (40000) + value: + message: "Bad request" + code: 40000 + statusCode: 400 + href: "https://help.ably.io/error/40000" + details: null + invalidObjectMessage: + summary: Invalid object message (92000) + value: + message: "invalid object message: nonce is required when objectId is specified" + code: 92000 + statusCode: 400 + href: "https://help.ably.io/error/92000" + details: null + objectsLimitExceeded: + summary: Objects limit exceeded (92001) + value: + message: "objects limit max number 100 exceeded" + code: 92001 + statusCode: 400 + href: "https://help.ably.io/error/92001" + details: null + operationOnTombstone: + summary: Operation on tombstone object (92002) + value: + message: "unable to submit operation on tombstone object: root" + code: 92002 + statusCode: 400 + href: "https://help.ably.io/error/92002" + details: null + pathMatchedNoObjects: + summary: Operation path matched no objects (92005) + value: + message: "no objects matched path: votes.missing" + code: 92005 + statusCode: 400 + href: "https://help.ably.io/error/92005" + details: null + missingObjectIdAndPath: + summary: Object ID or path required (92006) + value: + message: "object id or path required" + code: 92006 + statusCode: 400 + href: "https://help.ably.io/error/92006" + details: null + pathNotProcessable: + summary: Path not processable (92007) + value: + message: "path not processable: wildcard paths not supported for counter create" + code: 92007 + statusCode: 400 + href: "https://help.ably.io/error/92007" + details: null + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + unauthorized: + summary: Unauthorized + value: + message: "Unauthorized" + code: 40100 + statusCode: 401 + href: "https://help.ably.io/error/40100" + details: null + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + forbidden: + summary: Forbidden + value: + message: "Forbidden" + code: 40300 + statusCode: 403 + href: "https://help.ably.io/error/40300" + details: null + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + notFound: + summary: Not found + value: + message: "Not found" + code: 40400 + statusCode: 404 + href: "https://help.ably.io/error/40400" + details: null + fetchUnknownObject: + summary: Object not found (92004) + value: + message: "unable to fetch objects tree for not found object: root" + code: 92004 + statusCode: 404 + href: "https://help.ably.io/error/92004" + details: null + '429': + description: Too many requests + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + rateLimitExceeded: + summary: Rate limit exceeded + value: + message: "Rate limit exceeded" + code: 42900 + statusCode: 429 + href: "https://help.ably.io/error/42900" + details: null + get: + summary: Get object + description: | + Fetch an object by path in the compact or full object format.

+ If the path param is not provided then the root object is returned. If the compact param is not provided + then the response defaults to the compact format.

+ + For more information, see the usage documentation for [get object](/docs/liveobjects/rest-api-usage#fetch-object). + + parameters: + - name: channelId + in: path + required: true + description: The channel name. + schema: + type: string + example: "my-channel" + - name: compact + in: query + required: false + description: | + If `true` or not provided, returns a compact view of the object. + If `false`, returns the full object structure. + schema: + type: boolean + default: true + - name: path + in: query + required: false + description: | + The path from the root to the object. If not provided, returns the root object. + schema: + type: string + example: "votes" + responses: + '200': + description: Object data in either compact or full format. + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/LiveObjectCompact' + - $ref: '#/components/schemas/LiveObjectV2' + examples: + compact: + summary: "Get a compact view of the object tree: default or ?compact=true" + value: + votes: + up: 5 + down: 10 + compactWithCycles: + summary: "Get a compact view with cycles" + value: + votes: + up: 5 + down: 10 + myRoot: + objectId: "root" + compactWithPath: + summary: "Get a compact by path: ?path=votes" + value: + up: 5 + down: 10 + fullObject: + summary: "Get full object: ?compact=false" + value: + objectId: "root" + map: + semantics: "LWW" + entries: + votes: + data: + objectId: "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519" + map: + semantics: "LWW" + entries: + down: + data: + objectId: "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669" + counter: + data: + number: 10 + up: + data: + objectId: "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269" + counter: + data: + number: 5 + fullObjectWithCycles: + summary: "Get full object with cycles: ?compact=false" + value: + objectId: "root" + map: + semantics: "LWW" + entries: + votes: + data: + objectId: "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519" + map: + semantics: "LWW" + entries: + down: + data: + objectId: "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669" + counter: + data: + number: 10 + up: + data: + objectId: "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269" + counter: + data: + number: 5 + myRoot: + data: + objectId: "root" + fullObjectWithPath: + summary: "Get full object by path: ?compact=false&path=votes" + value: + objectId: "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519" + map: + semantics: "LWW" + entries: + down: + data: + objectId: "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669" + counter: + data: + number: 10 + up: + data: + objectId: "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269" + counter: + data: + number: 5 + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + badRequest: + summary: Bad request (40000) + value: + message: "Bad request" + code: 40000 + statusCode: 400 + href: "https://help.ably.io/error/40000" + details: null + fetchTombstoneObject: + summary: Unable to fetch tombstone object (92003) + value: + message: "unable to fetch objects tree for tombstone object: root" + code: 92003 + statusCode: 400 + href: "https://help.ably.io/error/92003" + details: null + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + unauthorized: + summary: Unauthorized + value: + message: "Unauthorized" + code: 40100 + statusCode: 401 + href: "https://help.ably.io/error/40100" + details: null + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + forbidden: + summary: Forbidden + value: + message: "Forbidden" + code: 40300 + statusCode: 403 + href: "https://help.ably.io/error/40300" + details: null + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + notFound: + summary: Not found + value: + message: "Not found" + code: 40400 + statusCode: 404 + href: "https://help.ably.io/error/40400" + details: null + fetchUnknownObject: + summary: Object not found (92004) + value: + message: "unable to fetch objects tree for not found object: root" + code: 92004 + statusCode: 404 + href: "https://help.ably.io/error/92004" + details: null + '429': + description: Too many requests + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + rateLimitExceeded: + summary: Rate limit exceeded + value: + message: "Rate limit exceeded" + code: 42900 + statusCode: 429 + href: "https://help.ably.io/error/42900" + details: null + /channels/{channelName}/object/{objectId}: + get: + summary: Get object by ID + description: | + Fetch an object by ID in the compact or full object format.

+ If the compact param is not provided then the response defaults to the compact format. The path query + param can be used to request a value from the provided object ID's children. + + For more information, see the usage documentation for [get object by ID](/docs/liveobjects/rest-api-usage#fetch-object-get-by-id). + parameters: + - name: channelName + in: path + required: true + description: The channel name. + schema: + type: string + example: "my-channel" + - name: objectId + in: path + required: true + description: The [object ID](/docs/liveobjects/concepts/objects#object-ids) of the object to fetch. + schema: + $ref: '#/components/schemas/ObjectIdString' + - name: compact + in: query + required: false + description: | + If `true` or not provided, returns a compact view of the object. + If `false`, returns the full object structure. + schema: + type: boolean + default: true + - name: path + in: query + required: false + description: | + The path from the root of the object requested. If not provided, returns the whole object. + schema: + type: string + example: "votes" + responses: + '200': + description: Object data in either compact or full format. + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/LiveObjectCompact' + - $ref: '#/components/schemas/LiveObjectV2' + examples: + compact: + summary: "Get a compact view of the object tree: default or ?compact=true" + value: + votes: + up: 5 + down: 10 + compactWithCycles: + summary: "Get a compact view with cycles" + value: + votes: + up: 5 + down: 10 + myRoot: + objectId: "root" + compactWithPath: + summary: "Get a compact by path: ?path=votes" + value: + up: 5 + down: 10 + fullObject: + summary: "Get full object: ?compact=false" + value: + objectId: "root" + map: + semantics: "LWW" + entries: + votes: + data: + objectId: "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519" + map: + semantics: "LWW" + entries: + down: + data: + objectId: "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669" + counter: + data: + number: 10 + up: + data: + objectId: "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269" + counter: + data: + number: 5 + fullObjectWithCycles: + summary: "Get full object with cycles: ?compact=false" + value: + objectId: "root" + map: + semantics: "LWW" + entries: + votes: + data: + objectId: "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519" + map: + semantics: "LWW" + entries: + down: + data: + objectId: "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669" + counter: + data: + number: 10 + up: + data: + objectId: "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269" + counter: + data: + number: 5 + myRoot: + data: + objectId: "root" + fullObjectWithPath: + summary: "Get full object by path: ?path=votes" + value: + objectId: "map:ja7cjMUib2LmJKTRdoGAG9pbBYCnkMObAVpmojCOmek@1745828596519" + map: + semantics: "LWW" + entries: + down: + data: + objectId: "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669" + counter: + data: + number: 10 + up: + data: + objectId: "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269" + counter: + data: + number: 5 + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + badRequest: + summary: Bad request (40000) + value: + message: "Bad request" + code: 40000 + statusCode: 400 + href: "https://help.ably.io/error/40000" + details: null + fetchTombstoneObject: + summary: Unable to fetch tombstone object (92003) + value: + message: "unable to fetch objects tree for tombstone object: root" + code: 92003 + statusCode: 400 + href: "https://help.ably.io/error/92003" + details: null + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + unauthorized: + summary: Unauthorized + value: + message: "Unauthorized" + code: 40100 + statusCode: 401 + href: "https://help.ably.io/error/40100" + details: null + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + forbidden: + summary: Forbidden + value: + message: "Forbidden" + code: 40300 + statusCode: 403 + href: "https://help.ably.io/error/40300" + details: null + '404': + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + notFound: + summary: Not found + value: + message: "Not found" + code: 40400 + statusCode: 404 + href: "https://help.ably.io/error/40400" + details: null + fetchUnknownObject: + summary: Object not found (92004) + value: + message: "unable to fetch objects tree for not found object: root" + code: 92004 + statusCode: 404 + href: "https://help.ably.io/error/92004" + details: null + '429': + description: Too many requests + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + rateLimitExceeded: + summary: Rate limit exceeded + value: + message: "Rate limit exceeded" + code: 42900 + statusCode: 429 + href: "https://help.ably.io/error/42900" + details: null /channels/{channelName}/objects: post: - summary: Publishing operations + deprecated: true + summary: Publishing operations (deprecated) description: | Publish operations to create or update objects on the channel.

- - For more information, see the usage documentation for [publishing operations](/docs/liveobjects/rest-api-usage#updating-objects). + + DEPRECATED: please use `POST /channels/{channelName}/object` instead.

+ + For more information, see the usage documentation for [publishing operations](/docs/liveobjects/rest-api-usage#publishing-operations). parameters: - name: channelName in: path @@ -232,11 +910,12 @@ paths: href: "https://help.ably.io/error/42900" details: null get: + deprecated: true summary: List objects description: | Fetch a flat list of objects on the channel.

- For more information, see the usage documentation for [list objects](/docs/liveobjects/rest-api-usage#fetching-objects). + DEPRECATED: please use `GET /channels/{channelName}/object` instead.

parameters: - name: channelName in: path @@ -248,27 +927,27 @@ paths: - name: values in: query required: false - description: If `true`, includes the values of objects in the response. See [including values](/docs/liveobjects/rest-api-usage#fetching-objects-list-values). + description: If `true`, includes the values of objects in the response. schema: type: boolean default: false - name: metadata in: query required: false - description: If `true`, includes additional object [metadata](/docs/liveobjects/concepts/objects#metadata) in the response. See [including metadata](/docs/liveobjects/rest-api-usage#fetching-objects-list-metadata). + description: If `true`, includes additional object [metadata](/docs/liveobjects/concepts/objects#metadata) in the response. schema: type: boolean default: false - name: limit in: query required: false - description: Specifies the maximum number of objects to include in the response. See [pagination](/docs/liveobjects/rest-api-usage#fetching-objects-list-pagination). + description: Specifies the maximum number of objects to include in the response. schema: type: integer - name: cursor in: query required: false - description: The object ID to use as a cursor for pagination. See [pagination](/docs/liveobjects/rest-api-usage#fetching-objects-list-pagination). + description: The object ID to use as a cursor for pagination. schema: $ref: '#/components/schemas/ObjectIdString' responses: @@ -474,10 +1153,11 @@ paths: /channels/{channelName}/objects/{objectId}: get: summary: Get objects + deprecated: true description: | Fetch an object by ID, optionally including its children.

- - For more information, see the usage documentation for [get objects](/docs/liveobjects/rest-api-usage#fetching-objects-get). + DEPRECATED: please use GET /channels/{channelName}/object?compact=false or + /channels/{channelName}/object/{objectId}?compact=false instead.

parameters: - name: channelName in: path @@ -496,15 +1176,15 @@ paths: in: query required: false description: | - Fetch the objects on the channel in a tree structure starting from the specified [object ID](/docs/liveobjects/concepts/objects#object-ids). - Nested child objects are resolved and their values are included in the response. See [Get an object and its children](/docs/liveobjects/rest-api-usage#fetching-objects-get-children). + Fetch the objects on the channel in a tree structure starting from the specified [object ID](/docs/liveobjects/concepts/objects#fetch-object-by-id). + Nested child objects are resolved and their values are included in the response. schema: type: boolean default: false - name: metadata in: query required: false - description: If `true`, includes additional object [metadata](/docs/liveobjects/concepts/objects#metadata) in the response. See [including metadata](/docs/liveobjects/rest-api-usage#fetching-objects-get-metadata). + description: If `true`, includes additional object. schema: type: boolean default: false @@ -686,10 +1366,10 @@ paths: /channels/{channelName}/objects/{objectId}/compact: get: summary: Get a compact view of objects + deprecated: true description: | Fetch the objects on the channel in a tree structure in a concise format.

- - For more information, see the usage documentation for [get a compact view of objects](/docs/liveobjects/rest-api-usage#fetching-objects-compact). + DEPRECATED: please use GET /channels/{channelName}/object or /channels/{channelName}/object/{objectId} instead.

parameters: - name: channelName in: path @@ -867,7 +1547,7 @@ components: string: type: string description: A [primitive](/docs/liveobjects/concepts/objects#primitive-types) string value. - example: "LiveObjects is awesome" + example: "LiveObjects is awesome" BytesValue: title: Bytes @@ -1060,6 +1740,9 @@ components: - type: object required: [map] properties: + semantics: + type: string + enum: [LWW] map: $ref: '#/components/schemas/LiveMap' - $ref: '#/components/schemas/LiveObjectMetadata' @@ -1079,12 +1762,12 @@ components: - $ref: '#/components/schemas/LiveCounterObject' LiveCounterCompact: - title: LiveCounter + title: LiveCounter (compact) description: The value of a [LiveCounter](/docs/liveobjects/counter) object. type: number LiveMapCompact: - title: LiveMap + title: LiveMap (compact) type: object description: A compact view of a [LiveMap](/docs/liveobjects/map) object. additionalProperties: @@ -1095,12 +1778,78 @@ components: - $ref: '#/components/schemas/ObjectIdValue' LiveObjectCompact: - title: LiveObject + title: LiveObject (compact) description: A compact view of a [LiveObject](/docs/liveobjects/concepts/objects) instance, which is either a [LiveMap](/docs/liveobjects/map) or a [LiveCounter](/docs/liveobjects/counter). oneOf: - $ref: '#/components/schemas/LiveCounterCompact' - $ref: '#/components/schemas/LiveMapCompact' - + + LiveObjectV2: + title: LiveObject + description: A [LiveObject](/docs/liveobjects/concepts/objects) instance, which is either a [LiveMap](/docs/liveobjects/map) or a [LiveCounter](/docs/liveobjects/counter). + oneOf: + - $ref: '#/components/schemas/LiveMapObjectV2' + - $ref: '#/components/schemas/LiveCounterObjectV2' + + LiveMapObjectV2: + title: LiveMap + description: A [LiveMap](/docs/liveobjects/map) object. + allOf: + - $ref: '#/components/schemas/LiveObjectBase' + - type: object + required: [ map ] + properties: + map: + $ref: '#/components/schemas/LiveMapV2' + + LiveCounterObjectV2: + title: LiveCounter + description: A [LiveCounter](/docs/liveobjects/counter) object. + allOf: + - $ref: '#/components/schemas/LiveObjectBase' + - type: object + required: [ counter ] + properties: + counter: + description: A [LiveCounter](/docs/liveobjects/counter) object. + allOf: + - $ref: '#/components/schemas/LiveCounter' + + LiveMapV2: + title: LiveMap + allOf: + - type: object + description: A [LiveMap](/docs/liveobjects/map) object. + required: [ entries ] + properties: + semantics: + type: string + enum: [ LWW ] + entries: + type: object + description: | + An object describing the entries in the [LiveMap](/docs/liveobjects/map) object. + The properties in the `entries` object describe the map *keys*. + additionalProperties: + $ref: '#/components/schemas/LiveMapEntryV2' + + + LiveMapEntryV2: + title: LiveMapEntry + allOf: + - type: object + description: | + The value of an entry in a [LiveMap](/docs/liveobjects/map). + required: [data] + properties: + data: + description: | + The value of the entry, which is either a [primitive value](/docs/liveobjects/concepts/objects#primitive-types) another [LiveObject](/docs/liveobjects/concepts/objects). + oneOf: + - $ref: '#/components/schemas/PrimitiveValue' + - $ref: '#/components/schemas/LiveMapObjectV2' + - $ref: '#/components/schemas/LiveCounterObjectV2' + # Operations OperationBase: type: object @@ -1113,7 +1862,7 @@ components: $ref: '#/components/schemas/ObjectIdString' path: type: string - description: The [path](/docs/liveobjects/rest-api-usage#updating-objects-by-path) from the root to the object. + description: The [path](/docs/liveobjects/rest-api-usage#fetch-object-with-path) from the root to the object. example: "votes.up" OperationMapSet: