openapi: 3.0.0
info:
  title: Ably LiveObjects REST API
  version: 1.0.0
  description: |
    # LiveObjects REST API

    LiveObjects provides a comprehensive REST API that allows you to directly work with objects over HTTP.<br><br>

    Whilst you can interact with these endpoints directly, it is recommended where possible to use the Ably Pub/Sub SDKs - which provide a convenient `request` method for interacting with Ably APIs.
    By using this method, you can take advantage of built-in support for Ably's authentication, pagination, and link headers, as well as automatic retry and fallback mechanics. This approach helps ensure consistent error handling, reduces setup overhead, and simplifies performing common operations against the LiveObjects REST API. Using this method, instead of implementing your own request handling, will lead to more robust and maintainable client-side integrations.
servers:
  - url: https://main.realtime.ably.net/
    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.<br><br>

        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:
                  counterInc:
                    number: 1
                  objectId: "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269"
              incPath:
                summary: Increment a LiveCounter object by path
                value:
                  counterInc:
                    number: 1
                  path: "votes.up"
              incPathWildcard:
                summary: Increment multiple LiveCounter objects by wildcard path
                value:
                  counterInc:
                    number: 1
                  path: "votes.*"
              remove:
                summary: Remove a LiveMap entry
                value:
                  mapRemove:
                    key: "posts"
                  objectId: "root"
              createMap:
                summary: Create a new LiveMap object
                value:
                  mapCreate:
                    semantics: 0
                    entries:
                      title:
                        data:
                          string: "LiveObjects is awesome"
                      createdAt:
                        data:
                          number: 1745835181122
                      isPublished:
                        data:
                          boolean: true
              createCounter:
                summary: Create a new LiveCounter object
                value:
                  counterCreate:
                    count: 5
              createMapPath:
                summary: Create a new LiveMap object at a specific path
                value:
                  mapCreate:
                    semantics: 0
                    entries:
                      title:
                        data:
                          string: "LiveObjects is awesome"
                      createdAt:
                        data:
                          number: 1745835181122
                      isPublished:
                        data:
                          boolean: true
                  path: "posts.post1"
      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.<br><br>
        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.<br><br>

        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.<br><br>
        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

components:
  schemas:

    # Fragments
    Encoding:
      type: object
      properties:
        encoding:
            type: string
            example: "json"
            description: A client-supplied encoding which clients can use to interpret raw string or bytes values.

    ObjectIdString:
      title: ObjectId
      type: string
      description: An [object ID](/docs/liveobjects/concepts/objects#object-ids).
      example: "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669"

    NumberValue:
      title: Number
      type: object
      required: [number]
      properties:
        number:
          type: number
          description: A [primitive](/docs/liveobjects/concepts/objects#primitive-types) numeric value.
          example: 42

    BooleanValue:
      title: Boolean
      type: object
      required: [boolean]
      properties:
        boolean:
          type: boolean
          description: A [primitive](/docs/liveobjects/concepts/objects#primitive-types) boolean value.
          example: true

    StringValue:
      title: String
      type: object
      allOf:
        - $ref: '#/components/schemas/Encoding'
        - type: object
          required: [string]
          properties:
            string:
              type: string
              description: A [primitive](/docs/liveobjects/concepts/objects#primitive-types) string value.
              example: "LiveObjects is awesome"

    BytesValue:
      title: Bytes
      type: object
      allOf:
        - $ref: '#/components/schemas/Encoding'
        - type: object
          required: [bytes]
          properties:
            bytes:
              type: string
              format: byte
              description: A [primitive](/docs/liveobjects/concepts/objects#primitive-types) base64-encoded bytes value.
              example: "TGl2ZU9iamVjdHMgaXMgYXdlc29tZQo="

    ObjectIdValue:
      title: ObjectId
      type: object
      required: [objectId]
      properties:
        objectId:
          $ref: '#/components/schemas/ObjectIdString'

    PrimitiveValue:
      oneOf:
        - $ref: '#/components/schemas/ObjectIdValue'
        - $ref: '#/components/schemas/NumberValue'
        - $ref: '#/components/schemas/BooleanValue'
        - $ref: '#/components/schemas/StringValue'
        - $ref: '#/components/schemas/BytesValue'

    SiteTimeserials:
      type: object
      description: A map of site ID to [timeserial](/docs/liveobjects/concepts/objects#timeserials).
      additionalProperties:
        type: string
        description: The [timeserial](/docs/liveobjects/concepts/objects#timeserials) of the latest operation applied in this site.
        example: "01745828651671-000@e025VxXLABoR0C19591332:000"

    LiveMapEntryMetadata:
      type: object
      properties:
        timeserial:
          type: string
          description: A [timeserial](/docs/liveobjects/concepts/objects#timeserials) value.
          example: "01745828651671-000@e025VxXLABoR0C19591332:000"
        tombstone:
          type: boolean
          description: Indicates whether this entry was deleted and is a [tombstone](/docs/liveobjects/concepts/objects#tombstones).
          example: false

    LiveMapMetadata:
      type: object
      properties:
        mapSemantics:
          type: string
          description: The semantics used for conflict resolution on the [LiveMap](/docs/liveobjects/map).
          example: "LWW"

    LiveObjectMetadata:
      type: object
      properties:
        siteTimeserials:
          $ref: '#/components/schemas/SiteTimeserials'
        tombstone:
          type: boolean
          description: Indicates whether this object was deleted as is a [tombstone](/docs/liveobjects/concepts/objects#tombstones).
          example: false

    LiveObjectBase:
      type: object
      required: [objectId]
      properties:
        objectId:
          $ref: '#/components/schemas/ObjectIdString'

    # Object types.
    # Shallow definitions do not resolve the values of child objects.
    # Some duplication in these definitions as unfortunately OpenAPI
    # does not support merging definitions, only replacements.
    LiveCounter:
      title: LiveCounter
      type: object
      required: [data]
      properties:
        data:
          description: |
            The value of the [LiveCounter](/docs/liveobjects/counter) object.
          allOf:
            - $ref: '#/components/schemas/NumberValue'

    LiveCounterObject:
      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'
        - $ref: '#/components/schemas/LiveObjectMetadata'

    LiveMapEntryShallow:
      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).
                If `?children=false`, child objects will be included by reference to their [ObjectId](/docs/liveobjects/concepts/objects#object-ids). Otherwise, the child object will be resolved and its value will be included in the response, unless resolving the child object would exceed the specified `limit`, in which case it will be included by reference.
              allOf:
                - $ref: '#/components/schemas/PrimitiveValue'
        - $ref: '#/components/schemas/LiveMapEntryMetadata'

    LiveMapEntry:
      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).
                If `?children=false`, child objects will be included by reference to their [ObjectId](/docs/liveobjects/concepts/objects#object-ids). Otherwise, the child object will be resolved and its value will be included in the response, unless resolving the child object would exceed the specified `limit`, in which case it will be included by reference.
              oneOf:
                - $ref: '#/components/schemas/PrimitiveValue'
                - $ref: '#/components/schemas/LiveMapObject'
                - $ref: '#/components/schemas/LiveCounterObject'
        - $ref: '#/components/schemas/LiveMapEntryMetadata'

    LiveMapShallow:
      title: LiveMap
      allOf:
        - type: object
          description: A [LiveMap](/docs/liveobjects/map) object.
          required: [entries]
          properties:
            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/LiveMapEntryShallow'
        - $ref: '#/components/schemas/LiveMapMetadata'

    LiveMap:
      title: LiveMap
      allOf:
        - type: object
          description: A [LiveMap](/docs/liveobjects/map) object.
          required: [entries]
          properties:
            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/LiveMapEntry'
        - $ref: '#/components/schemas/LiveMapMetadata'

    # Wrappers
    LiveMapObjectShallow:
      title: LiveMap
      description: A [LiveMap](/docs/liveobjects/map) object.
      allOf:
        - $ref: '#/components/schemas/LiveObjectBase'
        - type: object
          required: [map]
          properties:
            map:
              $ref: '#/components/schemas/LiveMapShallow'
        - $ref: '#/components/schemas/LiveObjectMetadata'

    LiveMapObject:
      title: LiveMap
      description: A [LiveMap](/docs/liveobjects/map) object.
      allOf:
        - $ref: '#/components/schemas/LiveObjectBase'
        - type: object
          required: [map]
          properties:
            semantics:
              type: string
              enum: [LWW]
            map:
              $ref: '#/components/schemas/LiveMap'
        - $ref: '#/components/schemas/LiveObjectMetadata'

    LiveObjectShallow:
      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/LiveMapObjectShallow'
        - $ref: '#/components/schemas/LiveCounterObject'

    LiveObject:
      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/LiveMapObject'
        - $ref: '#/components/schemas/LiveCounterObject'

    LiveCounterCompact:
      title: LiveCounter (compact)
      description: The value of a [LiveCounter](/docs/liveobjects/counter) object.
      type: number

    LiveMapCompact:
      title: LiveMap (compact)
      type: object
      description: A compact view of a [LiveMap](/docs/liveobjects/map) object.
      additionalProperties:
        description: A compact view of an entry in a [LiveMap](/docs/liveobjects/map).
        oneOf:
          - $ref: '#/components/schemas/LiveCounterCompact'
          - $ref: '#/components/schemas/LiveMapCompact'
          - $ref: '#/components/schemas/ObjectIdValue'

    LiveObjectCompact:
      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
      properties:
        id:
          type: string
          description: An idempotency key used for [idempotent operations](/docs/liveobjects/rest-api-usage#idempotent-operations).
          example: "my-idempotency-key"
        objectId:
          $ref: '#/components/schemas/ObjectIdString'
        path:
          type: string
          description: The [path](/docs/liveobjects/rest-api-usage#fetch-object-with-path) from the root to the object.
          example: "votes.up"

    MapSetData:
      type: object
      description: The key and value to set in the [LiveMap](/docs/liveobjects/map) object.
      required: [key, value]
      properties:
        key:
          type: string
          description: The name of the key.
        value:
          description: The value to set at the specified `key`, which is either a [primitive value](/docs/liveobjects/concepts/objects#primitive-types) or the ID of another [LiveObject](/docs/liveobjects/concepts/objects).
          allOf:
            - $ref: '#/components/schemas/PrimitiveValue'

    MapRemoveData:
      type: object
      description: The key to remove from the [LiveMap](/docs/liveobjects/map) object.
      required: [key]
      properties:
        key:
          type: string
          description: The name of the key.

    MapCreateData:
      type: object
      description: The initial data for the [LiveMap](/docs/liveobjects/map) object.
      required: [semantics, entries]
      properties:
        semantics:
          type: integer
          enum: [0]
          description: The conflict resolution semantics. Must be 0 for Last-Writer-Wins.
        entries:
          type: object
          description: The initial entries for the map, where each value is wrapped in a data object.
          additionalProperties:
            type: object
            required: [data]
            properties:
              data:
                description: The initial value for this key, which is either a [primitive value](/docs/liveobjects/concepts/objects#primitive-types) or the ID of another [LiveObject](/docs/liveobjects/concepts/objects).
                allOf:
                  - $ref: '#/components/schemas/PrimitiveValue'

    CounterIncData:
      type: object
      description: The value to increment the [LiveCounter](/docs/liveobjects/counter) object by.
      required: [number]
      properties:
        number:
          type: number
          description: The amount to increment (or decrement if negative).

    CounterCreateData:
      type: object
      description: The initial value for the [LiveCounter](/docs/liveobjects/counter) object.
      required: [count]
      properties:
        count:
          type: number
          description: The initial count value.

    OperationMapSet:
      allOf:
        - $ref: '#/components/schemas/OperationBase'
        - type: object
          required: [mapSet]
          properties:
            mapSet:
              $ref: '#/components/schemas/MapSetData'

    OperationMapRemove:
      allOf:
        - $ref: '#/components/schemas/OperationBase'
        - type: object
          required: [mapRemove]
          properties:
            mapRemove:
              $ref: '#/components/schemas/MapRemoveData'

    OperationMapCreate:
      allOf:
        - $ref: '#/components/schemas/OperationBase'
        - type: object
          required: [mapCreate]
          properties:
            mapCreate:
              $ref: '#/components/schemas/MapCreateData'

    OperationCounterInc:
      allOf:
        - $ref: '#/components/schemas/OperationBase'
        - type: object
          required: [counterInc]
          properties:
            counterInc:
              $ref: '#/components/schemas/CounterIncData'

    OperationCounterCreate:
      allOf:
        - $ref: '#/components/schemas/OperationBase'
        - type: object
          required: [counterCreate]
          properties:
            counterCreate:
              $ref: '#/components/schemas/CounterCreateData'

    Operation:
      type: object
      oneOf:
        - $ref: '#/components/schemas/OperationMapSet'
        - $ref: '#/components/schemas/OperationMapRemove'
        - $ref: '#/components/schemas/OperationMapCreate'
        - $ref: '#/components/schemas/OperationCounterCreate'
        - $ref: '#/components/schemas/OperationCounterInc'

    OperationRequest:
      oneOf:
        - $ref: '#/components/schemas/Operation'
        - type: array
          title: BatchOperation
          items:
            $ref: '#/components/schemas/Operation'

    PublishResponse:
      type: object
      required:
        - messageId
        - channel
        - objectIds
      properties:
        messageId:
          type: string
          description: The message ID of the published operation.
          example: "TJPWHhMTrF:0"
        channel:
          type: string
          description: The channel name.
          example: "my-channel"
        objectIds:
          type: array
          items:
            $ref: '#/components/schemas/ObjectIdString'
          description: The [object IDs](/docs/liveobjects/concepts/objects#object-ids) of the objects that were created or updated by the operation.
          example:
            - "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669"
            - "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269"

    # Errors
    ErrorResponse:
      type: object
      description: Base error response structure used by all API errors.
      properties:
        message:
          type: string
          description: The error message.
        code:
          type: integer
          description: The HTTP status code returned.
        statusCode:
          type: integer
          description: The Ably error code.
          nullable: true
        href:
          type: string
          description: The URL to documentation about the error code.
          nullable: true
        details:
          type: object
          nullable: true
          description: Any additional details about the error message.
      required:
        - message
        - code

