Skip to main content
Questions or issues? Contact us at api-support@manus.ai. v2 supersedes the v1 Team User Management surface. v1 is not removed — existing integrations keep working — but new integrations should target v2 directly, and existing ones benefit from a smaller blast radius for credential management, a unified response envelope, the profile-delegation workflow, and stronger field validation.

What’s new in v2

  • One-step auth. X-API-Key issued from Compliance settings — no DevTools-extracted session token, no separate OAuth token endpoint, no 1-hour token rotation.
  • Single REST surface. Every operation is GET/POST /v2/team.user.* with a unified {ok, request_id, ...} envelope. Connect RPC is also available at /team.v2.TeamUserManagementApiV2Service/<Method>.
  • Profile delegation. delegate / reclaim / rename / remove lets an active member take over a deactivated colleague’s data.
  • Stable identifier. Every team member has a team_user_id you can cache; safer than email for delegated profiles whose email is rewritten to a synthetic delegate-<id>@... address.
  • Filters. status_filter and delegation_state on team.user.list.
  • Server-side validation. Email format, length limits, and enum membership are enforced at the wire level via protovalidate; bad inputs fail fast with invalid_argument.

Authentication

v1 used a two-step credential dance (session token → CreateApiCredential → client_id + client_secret → OAuth token endpoint → 1-hour Bearer access token). v2 collapses this to a single header.
Stepv1v2
1DevTools → copy session token(none)
2RPC CreateApiCredentialclientId + clientSecret (shown once)Compliance settings → create key (type Team User Management)
3POST /api/user/manage/v1/oauth/tokenaccess_token (1 h)(none)
4Authorization: Bearer <access_token>X-API-Key: <your-api-key>
See v2 Authentication for the full flow.

Endpoint mapping

v1 endpointv2 endpointNotes
GET /api/user/manage/v1/users?limit=&offset=GET /v2/team.user.list?limit=&offset=Plus optional status_filter / delegation_state
GET /api/user/manage/v1/users/{email}GET /v2/team.user.detail?email=...Or ?team_user_id=...
POST /api/user/manage/v1/usersPOST /v2/team.user.createBody shape similar (snake_case)
PATCH /api/user/manage/v1/users/{email}POST /v2/team.user.updateIdentifier moves into the body; method becomes POST
(none)POST /v2/team.user.delegateNew: hand a deactivated profile to an active member
(none)POST /v2/team.user.reclaimNew: return a delegated profile to the deactivated pool
(none)POST /v2/team.user.renameNew: change a profile’s display name
(none)POST /v2/team.user.removeNew: hard-delete a member (cascades reclaim)
RPC CreateApiCredential / ListApiCredentials / DeleteApiCredentialCompliance settings UICredential CRUD moves out of the API surface

Field naming and enums

Wire format. v1 uses camelCase (userName, firstName, lastName); v2 uses snake_case (user_name, first_name, last_name). Role.
v1 stringv2 enum
"super_admin"TEAM_MEMBER_ROLE_SUPER_ADMIN
"admin"TEAM_MEMBER_ROLE_ADMIN
"member"TEAM_MEMBER_ROLE_MEMBER
"free_tier_member"TEAM_MEMBER_ROLE_GUEST
"owner"TEAM_MEMBER_ROLE_OWNER (read-only in both)
free_tier_member was renamed to GUEST in v2 to drop billing-internal jargon. Same semantics — does not consume a paid Stripe seat. Promoting GUEST → MEMBER/ADMIN/SUPER_ADMIN triggers a Stripe seat-quantity bump in both v1 and v2. Status.
v1 stringv2 enum
"active"USER_STATUS_ACTIVE
"inactive"USER_STATUS_INACTIVE
(UI-only in v1)USER_STATUS_REMOVEDnew, irreversible
v2 adds USER_STATUS_REMOVED which behaves like calling team.user.remove — hard-deletes the user and cascades reclaim of any profiles delegated to them. User response shape.
Fieldv1v2
email / email
userName / user_name
firstName / lastNamenot returned (use user_name)
statusstringUserStatus enum
rolestringTeamMemberRole enum
team_user_id✓ stable handle
delegated_to
delegated_profiles[]✓ (single-record responses only — list omits to avoid N+1)
original_email✓ (pre-delegation email)

Response envelope

v1 returns the payload directly. v2 wraps every response in {ok, request_id, ...}.
{
  "users": [...],
  "total": 50,
  "limit": 100,
  "offset": 0
}

Error code mapping

v1 (UPPER_SNAKE)v2 (lower_snake)
USER_NOT_FOUNDnot_found
USER_ALREADY_EXISTSalready_exists
INVALID_REQUESTinvalid_argument
UNAUTHORIZED / INVALID_CLIENTpermission_denied
INTERNAL_ERRORinternal
(none)failed_precondition (e.g. owner-immutable, source-not-deactivated)

Migration checklist

1

Issue a v2 API key

Create a Team User Management key in Compliance settings. Copy it immediately — it is shown only once. See v2 Authentication.
2

Pilot a read-only call against v2

v1 and v2 hit the same backing tables, so you can exercise team.user.list against your live team without affecting v1 traffic. Confirm field names, role/status enum values, and pagination behavior.
3

Rewrite request bodies

Convert camelCase → snake_case (userNameuser_name, etc.). Replace role/status string literals with the new enum constants. Move identifiers from path to body for team.user.update.
4

Adopt the response envelope

Read ok / error instead of relying on HTTP status codes alone. Persist request_id in your logs to make support requests actionable.
5

Cache `team_user_id`

Whenever you create a member or list members, store the returned team_user_id. Use it for subsequent updates — it survives renames and delegation, unlike email.
6

Cut over and revoke v1 credentials

Once your integration is fully on v2, delete the old client_id / client_secret via team.credential.delete so the v1 OAuth surface can no longer be used by stale callers.

What stays the same

  • Pagination defaults (limit 100, max 1000; offset 0-based)
  • Owner is read-only via API
  • Auto-provisioning of personal accounts during create
  • All operations are audit-logged
  • The Stripe seat-sync behavior on guest↔paid promotions

SailPoint / Okta connector notes

If your IDP connector is wired against v1, the migration is mostly:
  1. Swap the Token URL (/api/user/manage/v1/oauth/token) for an X-API-Key static header — no token endpoint to call.
  2. Update endpoint paths and method for update (PATCH → POST).
  3. Remap attribute paths under users[] (camelCase → snake_case) and any role/status string lookups to the new enum constants.
  4. Optionally map team_user_id as a secondary identifier alongside email so account aggregation continues to work for delegated profiles.