> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mirurobotics.com/llms.txt
> Use this file to discover all available pages before exploring further.

# List

> List groups.

| Scope         | Required |
| ------------- | -------- |
| `groups:read` | Yes      |


## OpenAPI

````yaml /references/platform-api/2026-08-17.yaml get /groups
openapi: 3.0.3
info:
  title: Miru Platform API
  description: The API between Miru and any external client
  version: 2026-08-17.everglades
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT
  x-release-version: 2026-08-17.everglades
  x-git-commit:
    sha: b0d008b18d840e3f7ad65ae4beb60b19863b665e
    url: >-
      https://github.com/mirurobotics/openapi/commit/b0d008b18d840e3f7ad65ae4beb60b19863b665e
    message: >-
      refactor(platform): remove bulk device move (#251)


      ## What


      Removes device→group *movement* from the platform audience. Two things

      go, together:


      1. **`POST /devices/move/bulk` (`bulkMoveDevices`)** — the path item in

      `apis/apps/backend-server/platform/paths/devices.yaml`, its entry in

      `platform/openapi.yaml`, and the `devices.bulk_move` method in

      `tools/stainless/platform.yml`.

      2. **`group_id` on the platform `CreateDeviceRequest`** — the only place

      `group_id` appeared in a platform request body.


      Device→group membership is now **fully read-only** on the platform API.


      ## What deliberately stays


      Nothing on the *read* side moved, and group structure stays writable:


      | Kept | Why |

      |---|---|

      | `group_id` on the `Device` schema | read field, unaffected |

      | `expand=group` on `Device`, and `group` in `DeviceExpansion` /

      `DeviceListExpansion` | read surface |

      | `group_id` filter on `GET /devices` (`device_search_group_id`) | read

      surface |

      | `GET /groups`, `GET /groups/{group_id}`, `POST /groups`, `PATCH

      /groups/{group_id}` | group CRUD is orthogonal to device membership |

      | **`POST /groups/{group_id}/move` (`moveGroup`)** | reparents a

      **group** in the tree, not devices between groups — different resource,

      different scope, and reversible by another reparent |

      | `apis/configs/components/requests/device.yaml#/BulkMoveDevicesRequest`

      | still referenced by `frontend` and `cli`, which keep their `bulk_move`

      methods |


      `moveGroup` surviving is the thing worth double-checking in review,

      since "move" appears on both resources. Verification below gates on it

      explicitly.


      ## Why create-time `group_id` goes with the endpoint


      This is the load-bearing part, not an incidental tidy-up.


      Without a move operation, a create-time `group_id` is a **one-way

      door**. A device could be placed into a group at provision time and then

      never moved out, reassigned, or relocated in the tree — through the API,

      ever. Shipping a write that can be exercised exactly once per resource

      lifetime is worse than shipping no write at all, because it hands

      customers a state they cannot get out of without the dashboard.


      So both go together, and the result is coherent: you can *see* which

      group a device is in, expand the group object, and filter a device list

      by group. You cannot set or change it.


      ## Not breaking


      `platform/2026-05-06.rainier` — the last **stable** platform release —

      has **no bulk-move endpoint and no groups resource at all**. `POST

      /devices/move/bulk` and create-time `group_id` existed only in the

      prereleases `platform/2026-08-17.everglades-beta.3` and

      `everglades-beta.4`.


      Withdrawing beta-only surface before everglades goes stable is free;

      after it goes stable it would not be. Same reasoning as #244 (upload

      collections) and #250 (the `devices:move` scope).


      ## Verification


      `./scripts/regen.sh`, `./scripts/lint.sh`, and `./scripts/preflight.sh`

      (`=== All checks passed ===`) all clean; `./scripts/is-stale.sh` reports

      `All generated specs are up to date.`


      - **Platform route count 25 → 24.**

      - **Scope histogram: `devices:write` 3 → 2**, `groups:write` unchanged

      at **3**, everything else unchanged.

      - Zero hits for `move/bulk`, `bulkMoveDevices`, or `BulkMoveDevices`

      anywhere in the platform tree or bundle; zero `bulk` in

      `tools/stainless/platform.yml`.

      - `CreateDeviceRequest` in the bundle is down to `name` alone — no

      `group_id`.

      - The generated `Device` block still carries `group_id` (required +

      property) and the `group` expandable; `group` present in **both**

      expansion enums; two `name: group_id` query params still in the bundle.

      - All five group routes present, `moveGroup` included.

      - Platform bundle diff is `0 55` — a pure deletion. No other audience's

      bundle changed and `apis/configs/` is untouched.


      A bare `grep group_id` is useless here — it matches the group path

      param, the `Device` read field, the `DvcSearch` enum member, and the

      list filter, all of which survive. Every check above is scoped to a

      specific schema block or uses a precise pattern.


      Plan: `plans/active/20260817-remove-platform-bulk-device-move.md`.


      ## Follow-ups


      **Release.** A new `platform/2026-08-17.everglades-beta.5` must be cut

      after merge — **tag the squash-merge commit on `main`**, not the branch

      head. Tagging a branch head makes the release job skip silently and

      publish nothing.


      **Backend.** Three changes:

      - Remove the `bulkMoveDevices` HTTP wiring.

      - Re-stub `CreateDevice.GetGroupID()` back to `optnull.None[string]()`.

      - Revert `Perms.Devices.Move` out of **both** `devices:write` and

      `devices:manage` — it was added to `devices:write` in #624 solely for

      this endpoint.


      **Docs.** docs#158 must drop the endpoint from the everglades changelog

      and from `authz.mdx`.


      <!-- codesmith:footer -->

      ---

      <a

      href="https://app.blacksmith.sh/mirurobotics/codesmith/openapi/pr/251"><picture><source

      media="(prefers-color-scheme: dark)"

      srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source

      media="(prefers-color-scheme: light)"

      srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img

      alt="View with [code]smith"

      src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>

      <a

      href="https://backend.blacksmith.sh/track/enable-autofix?expires=1789530880&installation_model_id=425273&pr_number=251&repository=mirurobotics%2Fopenapi&return_to=https%3A%2F%2Fgithub.com%2Fmirurobotics%2Fopenapi%2Fpull%2F251&signature=2eb2605a3305593f4e312c9f2a422d741a1a5991955adf57e51041a329830e09"><picture><source

      media="(prefers-color-scheme: dark)"

      srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source

      media="(prefers-color-scheme: light)"

      srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img

      alt="Autofix with [code]smith"

      src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>

      <sup>Need help on this PR? Tag <code>@codesmith</code> with what you

      need. Autofix is disabled.</sup>


      <!-- codesmith:autofix:disabled -->

      <!-- /codesmith:footer -->


      ---------


      Co-authored-by: Claude <noreply@anthropic.com>
    author: ben-miru
    branch: HEAD
    dirty: false
  x-build:
    built_at: '2026-08-17T04:08:03.448835+00:00'
servers:
  - url: https://api.mirurobotics.com/beta
    description: Miru Platform API
security:
  - ApiKeyAuth: []
paths:
  /groups:
    parameters:
      - $ref: '#/components/parameters/MiruVersion'
    get:
      tags:
        - Groups
      summary: List
      description: List groups.
      operationId: listGroups
      parameters:
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/group_order_bys'
        - $ref: '#/components/parameters/group_list_expansions'
        - $ref: '#/components/parameters/group_search_id'
        - $ref: '#/components/parameters/group_search_name'
        - $ref: '#/components/parameters/group_search_parent_id'
      responses:
        '200':
          description: Successfully listed the groups.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GroupList'
components:
  parameters:
    MiruVersion:
      name: Miru-Version
      in: header
      required: true
      schema:
        type: string
        example: 2026-08-17.everglades
      description: The API version the client was built against.
    offset:
      name: offset
      in: query
      required: false
      description: >-
        The offset of the items to return. An offset of 10 with a limit of 10
        returns items 11-20.
      schema:
        type: integer
        default: 0
        minimum: 0
        example: 0
    limit:
      name: limit
      in: query
      required: false
      description: >-
        The maximum number of items to return. A limit of 15 with an offset of 0
        returns items 1-15.
      schema:
        type: integer
        default: 10
        minimum: 1
        maximum: 100
        example: 10
    group_order_bys:
      name: order_by
      in: query
      required: false
      style: form
      explode: false
      description: Sort order for the group results.
      schema:
        type: array
        items:
          $ref: '#/components/schemas/GroupOrderBy'
        example:
          - created_at:desc
    group_list_expansions:
      name: expand
      in: query
      required: false
      description: Fields to expand on each group in the list.
      schema:
        type: array
        items:
          $ref: '#/components/schemas/GroupListExpansion'
        example:
          - parent
    group_search_id:
      name: id
      in: query
      required: false
      style: form
      explode: false
      description: The group IDs to filter by.
      schema:
        type: array
        items:
          type: string
        example:
          - grp_123
    group_search_name:
      name: name
      in: query
      required: false
      style: form
      explode: false
      description: The group names to filter by.
      schema:
        type: array
        items:
          type: string
        example:
          - production
    group_search_parent_id:
      name: parent_id
      in: query
      required: false
      style: form
      explode: false
      description: The parent group IDs to filter by.
      schema:
        type: array
        items:
          type: string
        example:
          - grp_456
  schemas:
    GroupList:
      title: Group List
      allOf:
        - $ref: '#/components/schemas/PaginatedList'
        - type: object
          required:
            - data
          properties:
            data:
              type: array
              items:
                $ref: '#/components/schemas/Group'
              description: The list of groups.
    GroupOrderBy:
      type: string
      enum:
        - id:asc
        - id:desc
        - created_at:asc
        - created_at:desc
        - updated_at:asc
        - updated_at:desc
        - name:asc
        - name:desc
      x-enum-varnames:
        - GROUP_ORDER_BY_ID_ASC
        - GROUP_ORDER_BY_ID_DESC
        - GROUP_ORDER_BY_CREATED_AT_ASC
        - GROUP_ORDER_BY_CREATED_AT_DESC
        - GROUP_ORDER_BY_UPDATED_AT_ASC
        - GROUP_ORDER_BY_UPDATED_AT_DESC
        - GROUP_ORDER_BY_NAME_ASC
        - GROUP_ORDER_BY_NAME_DESC
      default: created_at:desc
    GroupListExpansion:
      type: string
      enum:
        - total_count
        - parent
      x-enum-varnames:
        - GROUP_LIST_EXPAND_TOTAL_COUNT
        - GROUP_LIST_EXPAND_PARENT
    PaginatedList:
      title: Paginated List
      type: object
      required:
        - object
        - limit
        - offset
        - has_more
      properties:
        object:
          type: string
          enum:
            - list
          example: list
          x-stainless-const: true
          description: The object type, which is always `list`.
        total_count:
          type: integer
          format: int64
          description: >-
            The total number of items in the list. By default the total count is
            not returned. The total count must be expanded (using
            expand=total_count) to get the total number of items in the list.
        limit:
          type: integer
          description: >-
            The maximum number of items to return. A limit of 15 with an offset
            of 0 returns items 1-15.
          default: 10
          minimum: 1
          maximum: 100
        offset:
          type: integer
          description: >-
            The offset of the items to return. An offset of 10 with a limit of
            10 returns items 11-20.
          default: 0
          minimum: 0
        has_more:
          type: boolean
          description: >-
            True if there are more items in the list to return. False if there
            are no more items to return.
          example: false
    Group:
      title: Group
      allOf:
        - $ref: '#/components/schemas/BaseGroup'
        - type: object
          properties:
            parent:
              allOf:
                - $ref: '#/components/schemas/Group'
              nullable: true
              description: >-
                The parent group. Null for top-level groups. Expand using
                `expand=parent` in the query string.
      example:
        object: group
        id: grp_789
        name: line-3
        parent_id: grp_456
        created_at: '2026-01-01T00:00:00Z'
        updated_at: '2026-01-01T00:00:00Z'
    BaseGroup:
      title: Base Group
      type: object
      required:
        - object
        - id
        - name
        - parent_id
        - created_at
        - updated_at
      properties:
        object:
          type: string
          enum:
            - group
          example: group
          x-stainless-const: true
          description: The object type, which is always `group`.
        id:
          type: string
          example: grp_123
          description: ID of the group.
        name:
          type: string
          example: production
          description: Name of the group.
        parent_id:
          type: string
          nullable: true
          example: grp_456
          description: ID of the parent group. Null for top-level groups.
        created_at:
          type: string
          format: date-time
          example: '2026-01-01T00:00:00Z'
          description: Timestamp of when the group was created.
        updated_at:
          type: string
          format: date-time
          example: '2026-01-01T00:00:00Z'
          description: Timestamp of when the group was last updated.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: The API key to use for authentication.

````