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

# team.user.update

> Updates a team member's status and/or role. The target is identified by either `email` or `team_user_id` — when both are provided, `team_user_id` wins. At least one of `status` / `role` should be provided. When `status` becomes `INACTIVE` or `REMOVED` and the user has profiles delegated to them, those profiles are reclaimed and listed in `cascade_affected[]`. `status=REMOVED` cannot be combined with a `role` update. Promoting a `TEAM_MEMBER_ROLE_GUEST` member into a paid role triggers a Stripe seat-quantity bump; a downstream Stripe failure surfaces as `internal`.

<sup>Questions or issues? Contact us at [api-support@manus.ai](mailto:api-support@manus.ai).</sup>

<Tip>
  **Identifier:** Provide either `email` or `team_user_id`. When both are supplied, `team_user_id` wins. **Use `team_user_id` for delegated profiles** — their `email` is rewritten to a synthetic `delegate-<id>@...` address that no longer matches what your IDP knows.

  **Status transitions:**

  * `USER_STATUS_ACTIVE` ↔ `USER_STATUS_INACTIVE` — reversible.
  * `USER_STATUS_REMOVED` — irreversible. Equivalent to calling [team.user.remove](/enterprise/v2/team.user.remove) on the underlying `team_user_id`. Cannot be combined with a `role` update.

  **Cascade:** When `status` becomes `INACTIVE` or `REMOVED` and the user has profiles delegated to them, those profiles are returned to the deactivated pool and listed in `cascade_affected[]`.

  **Role values:** `TEAM_MEMBER_ROLE_SUPER_ADMIN`, `TEAM_MEMBER_ROLE_ADMIN`, `TEAM_MEMBER_ROLE_MEMBER`, or `TEAM_MEMBER_ROLE_GUEST`. Delegated (and formerly-delegated) profiles can be promoted freely — `TEAM_MEMBER_ROLE_OWNER` is the only role API cannot set; transfer ownership in the UI.

  **Seat sync side effect:** Promoting `TEAM_MEMBER_ROLE_GUEST → MEMBER/ADMIN/SUPER_ADMIN` triggers a Stripe seat-quantity bump so the subscription covers the new paid occupant. The reverse direction does not call Stripe — the seat is implicitly released the next time the team's count is computed. If Stripe rejects the upgrade, the API returns `internal` and the role change is rolled back to keep DB and billing in sync.

  **Owner is API-immutable:** Returns `failed_precondition` for the team owner.

  **Protocols:** Also callable via Connect RPC. See [Protocols](/enterprise/v2/protocols).
</Tip>


## OpenAPI

````yaml POST /v2/team.user.update
openapi: 3.1.0
info:
  title: Manus Team User Management OpenAPI v2
  description: >-
    API for enterprise tenant administrators to manage team members
    programmatically (SailPoint, Okta, custom IDP integrations). All responses
    are wrapped with `{"ok": true, "request_id": "...", ...}` for success and
    `{"ok": false, "request_id": "...", "error": {"code": "...", "message":
    "..."}}` for errors. Every operation is callable via REST (paths below) or
    via Connect RPC at `/team.v2.TeamUserManagementApiV2Service/<Method>`.
  version: 2.0.0
servers:
  - url: https://api.manus.im
security:
  - ApiKeyAuth: []
paths:
  /v2/team.user.update:
    post:
      summary: UpdateUser
      description: >-
        Updates a team member's status and/or role. The target is identified by
        either `email` or `team_user_id` — when both are provided,
        `team_user_id` wins. At least one of `status` / `role` should be
        provided. When `status` becomes `INACTIVE` or `REMOVED` and the user has
        profiles delegated to them, those profiles are reclaimed and listed in
        `cascade_affected[]`. `status=REMOVED` cannot be combined with a `role`
        update. Promoting a `TEAM_MEMBER_ROLE_GUEST` member into a paid role
        triggers a Stripe seat-quantity bump; a downstream Stripe failure
        surfaces as `internal`.
      operationId: team.v2.TeamUserManagementApiV2Service.UpdateUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                  maxLength: 254
                  description: >-
                    Email of the team member to update. Either `email` or
                    `team_user_id` must be provided. Must be a valid RFC 5321
                    address when present.
                  example: user@example.com
                team_user_id:
                  type: string
                  maxLength: 64
                  description: >-
                    Team member ID (stable identifier). Either `email` or
                    `team_user_id` must be provided; when both are supplied,
                    `team_user_id` wins. Recommended for delegated profiles,
                    whose `email` is rewritten to a synthetic
                    `delegate-<id>@...` address.
                  example: '123456'
                status:
                  $ref: '#/components/schemas/UserStatus'
                  description: >-
                    New status. Accepts `USER_STATUS_ACTIVE`,
                    `USER_STATUS_INACTIVE`, or `USER_STATUS_REMOVED`. `REMOVED`
                    is irreversible and triggers the same cascade as
                    [team.user.remove](/enterprise/v2/team.user.remove).
                role:
                  $ref: '#/components/schemas/TeamMemberRole'
                  description: >-
                    New role. Accepts `TEAM_MEMBER_ROLE_SUPER_ADMIN`,
                    `TEAM_MEMBER_ROLE_ADMIN`, `TEAM_MEMBER_ROLE_MEMBER`, or
                    `TEAM_MEMBER_ROLE_GUEST`. Delegated (or formerly-delegated)
                    profiles can be promoted freely — `TEAM_MEMBER_ROLE_OWNER`
                    is the only role the API cannot set (transfer ownership in
                    the UI). Promoting `GUEST → MEMBER/ADMIN/SUPER_ADMIN` calls
                    Stripe to bump the team's seat quantity; the reverse
                    direction requires no Stripe call.
      responses:
        '200':
          description: Team member updated successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    example: true
                  request_id:
                    type: string
                  user:
                    $ref: '#/components/schemas/User'
                  cascade_affected:
                    type: array
                    items:
                      $ref: '#/components/schemas/CascadeAffected'
                    description: >-
                      Profiles returned to the deactivated pool because the
                      target user was deactivated or removed. Empty when no
                      cascade occurred.
        4XX:
          description: Error response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    UserStatus:
      type: string
      description: Status of a team member.
      enum:
        - USER_STATUS_ACTIVE
        - USER_STATUS_INACTIVE
        - USER_STATUS_REMOVED
    TeamMemberRole:
      type: string
      description: >-
        Team role. `TEAM_MEMBER_ROLE_OWNER` is read-only and cannot be granted
        via API (transfer ownership in the UI). Guest members do not consume a
        paid seat.
      enum:
        - TEAM_MEMBER_ROLE_OWNER
        - TEAM_MEMBER_ROLE_ADMIN
        - TEAM_MEMBER_ROLE_MEMBER
        - TEAM_MEMBER_ROLE_SUPER_ADMIN
        - TEAM_MEMBER_ROLE_GUEST
    User:
      type: object
      description: A team member.
      properties:
        email:
          type: string
          description: >-
            Email address. After delegation this field becomes
            `delegate-xxx@manus.im` — the pre-delegation address is preserved in
            `original_email`.
        status:
          $ref: '#/components/schemas/UserStatus'
        role:
          $ref: '#/components/schemas/TeamMemberRole'
        user_name:
          type: string
          description: >-
            Display name. Sourced from `team_user_relation.Name` (set via
            [team.user.rename](/enterprise/v2/team.user.rename)) when present,
            otherwise falls back to the underlying user's name. Omitted when
            neither is set.
        team_user_id:
          type: string
          description: >-
            Stable identifier for the team member, unique within the team. Use
            this (not `email`) for any subsequent profile-management call.
        delegated_to:
          type: string
          description: >-
            If this profile is currently delegated, the `team_user_id` of the
            assignee.
        delegated_profiles:
          type: array
          items:
            $ref: '#/components/schemas/DelegatedProfile'
          description: >-
            Profiles delegated to this user. Only populated by single-record
            responses ([team.user.detail](/enterprise/v2/team.user.detail),
            [team.user.update](/enterprise/v2/team.user.update), etc.).
            [team.user.list](/enterprise/v2/team.user.list) omits this field to
            avoid N+1 fan-out.
        original_email:
          type: string
          description: Pre-delegation email. Only present for currently-delegated profiles.
    CascadeAffected:
      type: object
      description: >-
        A profile that was returned to the deactivated pool as a side effect of
        deactivating or removing its assignee.
      properties:
        team_user_id:
          type: string
          description: ID of the affected profile.
        display_name:
          type: string
          description: >-
            Display name. Falls back to the original email if the profile has no
            name.
        action:
          type: string
          description: Always `returned_to_pool`.
        new_status:
          $ref: '#/components/schemas/UserStatus'
          description: Status the profile now has. Always `USER_STATUS_INACTIVE`.
    ErrorResponse:
      type: object
      description: Standard error response format returned when a request fails.
      properties:
        ok:
          type: boolean
          example: false
          description: Always false for error responses.
        request_id:
          type: string
          description: >-
            Unique identifier for this API request, useful for debugging with
            support.
        error:
          type: object
          description: Error details.
          properties:
            code:
              type: string
              description: >-
                Machine-readable error code: `invalid_argument`, `not_found`,
                `already_exists`, `permission_denied`, `failed_precondition`, or
                `internal`.
            message:
              type: string
              description: Human-readable error description.
    DelegatedProfile:
      type: object
      description: A profile delegated to another team member.
      properties:
        team_user_id:
          type: string
          description: ID of the delegated profile.
        display_name:
          type: string
          description: >-
            Display name. Falls back to the original (pre-delegation) email when
            the profile has no name set.
        role:
          $ref: '#/components/schemas/TeamMemberRole'
        delegated_at:
          type: string
          format: date-time
          description: When the delegation was created.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key

````