---
title: "Move members to another group"
method: POST
path: "/groups/{id}/members/move"
tags: ["groups"]
---

# Move members to another group

`POST /groups/{id}/members/move`

## Request

- Base URL: `https://api.instruxi.dev/api/v1/enforcer`
- URL: `https://api.instruxi.dev/api/v1/enforcer/groups/{id}/members/move`
- Auth: one of:
  - API key in header `Authorization`
  - API key in header `X-API-Key`

Moves accounts from this group to `to_group_id` in ONE transaction, so nobody is ever in neither group (reorganising teams no longer needs a remove and an add per person). The route authorizes a write on THIS group; the target is checked separately with the same write decision, so the caller must be able to add members to both (403 target_forbidden otherwise). The target must be a different, non-archived group in the same tenant (400 invalid_target). Every id reports an outcome: `moved`, `not_member` (not in this group), `not_found`, `cross_tenant`, or the agent fence's `agent_consent_required` / `not_group_member` for a service account entering the target. An account already in the target just leaves this group and reports `moved`. role sets the target role; omitted, each member keeps their role. Each mover gets a removed + added membership event and audit record pair (metadata.via = move). 409 group_archived if either group is archived. At most 1000 ids per call.

## Path parameters

- `id` string, required

## Request body

- InternalDeliveryHttpHandlerMoveMembersRequest
  - `account_ids` string[]
  - `role` 'member' | 'manager': Role in the target group; omitted = each member keeps their source role.
  - `to_group_id` string

## Response `200`

OK

- InternalDeliveryHttpHandlerBulkMembersResponse
  - `data` EnforcerV3InternalUsecaseGroupsMemberBulkResult
    - `added` integer
    - `agent_consent_required` integer
    - `already_member` integer
    - `cross_tenant` integer
    - `moved` integer
    - `not_found` integer
    - `not_group_member` integer
    - `not_member` integer
    - `removed` integer
    - `results` EnforcerV3InternalUsecaseGroupsMemberOutcome[]
      - `account_id` string
      - `status` string: Status is one of: added they are in the group, and were not before already_member they were already in it — an import re-run, not an error not_found no such account cross_tenant the account belongs to a different tenant agent_consent_required the account is a service account (AGENTS_CONTRACT §6.5) and the caller is not a manager() of its bound agent (or TenantManage, for a plain service account with no agent) not_group_member the account is a service account and the caller — short of TenantManage/CrossTenant — is not themselves a member of this group
  - `success` boolean

## Other responses

- `400`: Bad Request
- `401`: Unauthorized
- `403`: Forbidden
- `409`: Conflict

---

[API](https://skmtc.dev/instruxi/apis/enforcer-api.md) · [All operations](https://skmtc.dev/instruxi/apis/enforcer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/instruxi/apis/enforcer-api/revisions/fa82bc1becc0?raw)
