Group members
Manage group members from your server. For member roles, ways of joining and versions, see Group management.
| You want to | API |
|---|---|
| List members or the muted list | List group members |
| Put people straight into the group | Add group members, without their confirmation |
| Remove people | Remove group members; to keep them out, use the group block list |
| Change group nicknames or member attributes (such as student number or job title) | Update a member |
| Make or unmake admins | Set a member's role |
| Mute one member | Mute a member; for mute-all see Turn on mute-all |
For example, add zhaoliu and zhouba to a group:
curl -X POST "$IM_API/$ORG/$APP/groups/99582624883802112/members/batch" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "usernames": ["zhaoliu", "zhouba"] }'- Server additions need no confirmation: the users' group settings and the group's invitation setting are ignored. To let users decide for themselves, have a member invite them from a client, see Join requests and invitations.
- Notifications: when members join or leave, or their role, group nickname or mute status changes, the group's online members are notified in real time; joining and leaving also add notices to the group conversation. Each change increments
member_version, and a batch counts once.
List group members
Get a group's members page by page, each with the user's nickname and avatar so you don't need to look up profiles separately. You can list only one role, or only muted members (the muted list).
- Members come in a fixed order (unrelated to join time).
- Members whose accounts were just deleted and haven't been removed yet aren't listed, so a page may have fewer than
limititems; rely only onnext_cursor. - A dissolved group can still be queried until its members are removed (about an hour after dissolution).
/{org_name}/{app_name}/groups/{group_id}/membersPath parameters
For org_name and app_name, see the server overview.
| Parameter | Type | Description |
|---|---|---|
group_id | String | The group ID |
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
role | String | No | Only members with this role: owner, admin or member |
muted | Boolean | No | true lists only currently muted members; default false |
known_version | Number | No | The member list version you saved. On the first page (no cursor), if it equals the current member_version, only the version and "not_modified": true are returned, without the list |
limit | Number | No | Page size, default 100, 1 to 500 |
cursor | String | No | The cursor for the next page, see Paging |
Request example
curl -X GET "$IM_API/$ORG/$APP/groups/99582624883802112/members?limit=2" \
-H "Authorization: Bearer $APP_TOKEN"Response
On success you get 200 OK:
| Field | Type | Description |
|---|---|---|
member_version | Number | The current member list version |
not_modified | Boolean | true means the list equals known_version, and the response has only member_version and this field |
member_count | Number | Total members including the owner, regardless of filters |
items | Array<Object> | Members on this page, each a group member object |
next_cursor | String | The next page's cursor; null when there's no next page |
{
"member_version": 18,
"not_modified": false,
"member_count": 3,
"items": [
{
"username": "zhangsan",
"nickname": "张三",
"avatar_url": "https://cdn.example.com/avatar/zhangsan.png",
"role": "owner",
"group_nickname": "",
"attributes": null,
"muted": false,
"muted_until": null,
"joined_via": "create",
"inviter": null,
"joined_at": "2026-10-02T19:05:47.929Z"
},
{
"username": "lisi",
"nickname": "李四",
"avatar_url": "https://cdn.example.com/avatar/lisi.png",
"role": "member",
"group_nickname": "李四爸爸",
"attributes": {
"relation": "父亲",
"student_no": "20230512"
},
"muted": false,
"muted_until": null,
"joined_via": "create",
"inviter": null,
"joined_at": "2026-10-02T19:05:47.929Z"
}
],
"next_cursor": "9H7GjqY3MBC44LVD3-8KK34h7wBQEruWHZtZcFgxf11xvlhOrIFavUbSflQL6KuoIRlUZ_8q3QsEV7gmWm-j_WxPu_kFPboldMeSWhLbkx0BKBBE"
}With known_version and no changes:
{
"member_version": 18,
"not_modified": true
}Errors
| HTTP status | code | Description |
|---|---|---|
| 400 | invalid_argument | role, muted or known_version has an invalid value, or limit is out of range |
| 404 | not_found | The group doesn't exist |
Add group members
Put a batch of users straight into a group, up to 100 at a time.
| Checked? | What |
|---|---|
| Not checked | The user's confirmation, their group settings, the group's invitation setting, block lists between users, the before-join-group callback |
| Checked | Not on the group block list; the group's size within max_members (group object); the user's group count within max_groups_per_user (runtime policy, 500 by default, and no more than the plan quota) |
- Handled one by one: one failure doesn't affect the others, and
resultslists each outcome, see Batch APIs. Existing members count as success. - After joining:
joined_viaisserver; their pending join requests and invitations for this group become invalid. - Quota: counts toward the app's call quota once per username, see Rate limits and app status.
/{org_name}/{app_name}/groups/{group_id}/members/batchPath parameters
For org_name and app_name, see the server overview.
| Parameter | Type | Description |
|---|---|---|
group_id | String | The group ID |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
usernames | Array<String> | Yes | Users to add, 1 to 100 (case-insensitive; duplicates handled once) |
Request example
curl -X POST "$IM_API/$ORG/$APP/groups/99582624883802112/members/batch" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"usernames": ["zhaoliu", "zhouba", "lisi", "sunqi", "nobody"]
}'Response
On success you get 200 OK:
| Field | Type | Description |
|---|---|---|
changed | Boolean | Whether anyone joined |
member_version | Number | The member list version: the new version if anyone joined, otherwise the current one |
results | Array<Object> | Each user's result in request order, see Batch results. Successful status is joined or already_member |
{
"changed": true,
"member_version": 24,
"results": [
{ "username": "zhaoliu", "status": "joined" },
{ "username": "zhouba", "status": "joined" },
{ "username": "lisi", "status": "already_member" },
{
"username": "sunqi",
"code": "permission_denied",
"message": "该用户在群黑名单中",
"details": { "reason": "group_blacklisted" }
},
{ "username": "nobody", "code": "not_found", "message": "用户不存在" }
]
}Errors
| HTTP status | code | Description |
|---|---|---|
| 400 | invalid_argument | usernames is empty or has more than 100 entries |
| 403 | group_disabled | The group is banned and nobody can be added; details.disabled_by says who banned it |
| 403 | app_unavailable | The app is read-only and nobody can be added |
| 404 | not_found | The group doesn't exist or was dissolved |
Remove group members
Remove a batch of members from a group, up to 100 at a time.
- The owner can't be removed; transfer ownership first. Users who aren't members count as removed.
- They can come back: removed users can be added or invited again later. To keep them out, add them to the group block list (which removes them automatically, so you don't need this API first).
- The removed user: is notified in real time along with online members, with a notice in the group conversation; gets a "You were removed from the group" offline push when the app isn't in the foreground; loses their conversation Do Not Disturb for the group; and can no longer see or download group files, while files they uploaded stay in the group.
- Banned groups can remove members too. Counts toward the call quota once per username.
/{org_name}/{app_name}/groups/{group_id}/members/batch-deletePath parameters
For org_name and app_name, see the server overview.
| Parameter | Type | Description |
|---|---|---|
group_id | String | The group ID |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
usernames | Array<String> | Yes | Users to remove, 1 to 100 (case-insensitive; duplicates handled once) |
Request example
curl -X POST "$IM_API/$ORG/$APP/groups/99582624883802112/members/batch-delete" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"usernames": ["zhouba", "zhangsan", "wujiu"]
}'Response
On success you get 200 OK with the same fields as Add group members, where changed means whether anyone was removed. Successful status is removed or not_member.
{
"changed": true,
"member_version": 25,
"results": [
{ "username": "zhouba", "status": "removed" },
{ "username": "zhangsan", "code": "permission_denied", "message": "不能移出群主" },
{ "username": "wujiu", "status": "not_member" }
]
}Errors
| HTTP status | code | Description |
|---|---|---|
| 400 | invalid_argument | usernames is empty or has more than 100 entries |
| 404 | not_found | The group doesn't exist or was dissolved |
Get a member
Get one user's info in a group.
/{org_name}/{app_name}/groups/{group_id}/members/{username}Path parameters
For org_name and app_name, see the server overview.
| Parameter | Type | Description |
|---|---|---|
group_id | String | The group ID |
username | String | The username |
Request example
curl -X GET "$IM_API/$ORG/$APP/groups/99582624883802112/members/wangwu" \
-H "Authorization: Bearer $APP_TOKEN"Response
On success you get 200 OK with a group member object.
{
"username": "wangwu",
"nickname": "王五",
"avatar_url": "https://cdn.example.com/avatar/wangwu.png",
"role": "admin",
"group_nickname": "",
"attributes": null,
"muted": false,
"muted_until": null,
"joined_via": "create",
"inviter": null,
"joined_at": "2026-10-02T19:05:47.929Z"
}Errors
| HTTP status | code | Description |
|---|---|---|
| 403 | not_group_member | The user isn't a member of the group |
| 404 | not_found | The group or user doesn't exist |
Update a member
Change a member's group nickname and member attributes in this group; only fields in the request change. Both are visible to every member, and users can change their own in clients. Banned groups can be updated too, so violating group nicknames can be cleared.
Moderation: group nicknames go through moderation (platform rules only by default, see Server-submitted content), except when unchanged or cleared; member attributes aren't checked. If it fails, nothing changes and you get 403 content_rejected; a nickname matching a replacement rule is saved with the replacement.
/{org_name}/{app_name}/groups/{group_id}/members/{username}Path parameters
For org_name and app_name, see the server overview.
| Parameter | Type | Description |
|---|---|---|
group_id | String | The group ID |
username | String | The member's username |
Request body
At least one field.
| Field | Type | Required | Description |
|---|---|---|---|
group_nickname | String | No | Group nickname, up to 64 characters without line breaks or other control characters, leading and trailing whitespace trimmed; an empty string clears it |
attributes | Object | No | Member attributes, string key-value pairs such as student number or job title. Merged entry by entry: keys present are added or overwritten, keys set to null are deleted, keys absent are unchanged; null for the whole field clears everything. After merging, up to 16 entries and 1 KB of JSON; keys are 1 to 32 characters of letters, digits and _, -, . |
Request example
curl -X PATCH "$IM_API/$ORG/$APP/groups/99582624883802112/members/lisi" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"group_nickname": "李四爸爸",
"attributes": { "student_no": "20230512", "relation": "父亲" }
}'Response
On success you get 200 OK:
| Field | Type | Description |
|---|---|---|
member | Object | The updated group member object |
changed | Boolean | Whether this request changed the member. false when values are the same, in which case member_version doesn't change and nobody is notified |
member_version | Number | The member list version: the new version if something changed, otherwise the current one |
{
"member": {
"username": "lisi",
"nickname": "李四",
"avatar_url": "https://cdn.example.com/avatar/lisi.png",
"role": "member",
"group_nickname": "李四爸爸",
"attributes": {
"relation": "父亲",
"student_no": "20230512"
},
"muted": false,
"muted_until": null,
"joined_via": "create",
"inviter": null,
"joined_at": "2026-10-02T19:05:47.929Z"
},
"changed": true,
"member_version": 23
}Errors
| HTTP status | code | Description |
|---|---|---|
| 400 | invalid_argument | Nothing to change, or a field is invalid (nickname too long, attributes over the limit); the request contains other fields (such as role; use Set a member's role) |
| 403 | not_group_member | The user isn't a member of the group |
| 403 | app_unavailable | The app is read-only and members can't be updated |
| 403 | content_rejected | The group nickname failed moderation; details.field is group_nickname and details.reason gives the reason, see Errors callers see |
| 404 | not_found | The group doesn't exist or was dissolved, or the user doesn't exist |
Set a member's role
Make a member an admin, or turn an admin back into a regular member. Each group has at most 20 admins. The owner can only change through Transfer ownership, not this API.
When an admin goes back to being a regular member, invite links they created stop working. Banned groups can set roles too.
/{org_name}/{app_name}/groups/{group_id}/members/{username}/rolePath parameters
For org_name and app_name, see the server overview.
| Parameter | Type | Description |
|---|---|---|
group_id | String | The group ID |
username | String | The member's username |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
role | String | Yes | admin or member. If it's already the current role, nothing changes and changed is false |
Request example
curl -X PUT "$IM_API/$ORG/$APP/groups/99582624883802112/members/wangwu/role" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"role": "admin"
}'Response
On success you get 200 OK with the same fields as Update a member.
{
"member": {
"username": "wangwu",
"nickname": "王五",
"avatar_url": "https://cdn.example.com/avatar/wangwu.png",
"role": "admin",
"group_nickname": "",
"attributes": null,
"muted": false,
"muted_until": null,
"joined_via": "create",
"inviter": null,
"joined_at": "2026-10-02T19:05:47.929Z"
},
"changed": true,
"member_version": 7
}Errors
| HTTP status | code | Description |
|---|---|---|
| 400 | invalid_argument | role isn't admin or member; the user is the owner |
| 403 | not_group_member | The user isn't a member of the group |
| 404 | not_found | The group doesn't exist or was dissolved, or the user doesn't exist |
| 409 | limit_exceeded | details.reason admin_limit: the group already has 20 admins |
Mute a member
Stop a member posting in this group, for a set time or permanently, ending automatically. Muted members can still read group messages; sending returns 403 group_muted.
- Who can be muted: admins can, and they don't get the admin exemption from mute-all; the owner can't be muted.
- Only this group: to stop them posting everywhere, use a global mute. Your server sending messages as them isn't limited.
- Muting again: the end time is recalculated from the new duration (longer or shorter). Muting permanently when already permanent, or a new end time within 1 minute of the old one (such as a retry), counts as a repeat and
changedisfalse. - Banned groups can mute members too.
/{org_name}/{app_name}/groups/{group_id}/members/{username}/mutePath parameters
For org_name and app_name, see the server overview.
| Parameter | Type | Description |
|---|---|---|
group_id | String | The group ID |
username | String | The member's username |
Request body
The body is optional; without it the mute is permanent.
| Field | Type | Required | Description |
|---|---|---|---|
duration_seconds | Number | No | Mute length in seconds, 1 to 315360000 (10 years). Omitted or null means permanent |
Request example
curl -X PUT "$IM_API/$ORG/$APP/groups/99582624883802112/members/sunqi/mute" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"duration_seconds": 3600
}'Response
On success you get 200 OK with the same fields as Update a member. For permanent mutes, muted is true and muted_until is null.
{
"member": {
"username": "sunqi",
"nickname": "孙七",
"avatar_url": "https://cdn.example.com/avatar/sunqi.png",
"role": "member",
"group_nickname": "",
"attributes": null,
"muted": true,
"muted_until": "2026-10-02T20:07:35.934Z",
"joined_via": "server",
"inviter": null,
"joined_at": "2026-10-02T19:06:42.699Z"
},
"changed": true,
"member_version": 8
}Errors
| HTTP status | code | Description |
|---|---|---|
| 400 | invalid_argument | duration_seconds is out of range |
| 403 | permission_denied | The user is the owner and can't be muted |
| 403 | not_group_member | The user isn't a member of the group |
| 404 | not_found | The group doesn't exist or was dissolved, or the user doesn't exist |
Unmute a member
Lift a member's mute in this group early. If they aren't muted (or it already ended), nothing changes and changed is false. Mute-all is turned off separately. Banned groups can unmute too.
/{org_name}/{app_name}/groups/{group_id}/members/{username}/mutePath parameters
For org_name and app_name, see the server overview.
| Parameter | Type | Description |
|---|---|---|
group_id | String | The group ID |
username | String | The member's username |
Request example
curl -X DELETE "$IM_API/$ORG/$APP/groups/99582624883802112/members/zhaoliu/mute" \
-H "Authorization: Bearer $APP_TOKEN"Response
On success you get 200 OK with the same fields as Update a member.
{
"member": {
"username": "zhaoliu",
"nickname": "赵六",
"avatar_url": "https://cdn.example.com/avatar/zhaoliu.png",
"role": "member",
"group_nickname": "",
"attributes": null,
"muted": false,
"muted_until": null,
"joined_via": "server",
"inviter": null,
"joined_at": "2026-10-02T19:06:42.699Z"
},
"changed": true,
"member_version": 10
}Errors
| HTTP status | code | Description |
|---|---|---|
| 403 | not_group_member | The user isn't a member of the group |
| 404 | not_found | The group doesn't exist or was dissolved, or the user doesn't exist |
Data structures
Group member object
| Field | Type | Description |
|---|---|---|
username | String | The username |
nickname | String | The nickname from their profile; empty string if none |
avatar_url | String | The avatar address from their profile; empty string if none |
role | String | owner, admin or member |
group_nickname | String | Group nickname in this group; empty string if none |
attributes | Object | Member attributes, string key-value pairs; null if none |
muted | Boolean | Whether they're currently muted (not counting mute-all) |
muted_until | String | When the mute ends; null for permanent mutes or when not muted |
joined_via | String | How they joined: create, server, invite, apply or link, see Ways to join |
inviter | String | Who invited them, or who created the invite link they used; null for other ways or when that person was deleted |
joined_at | String | When they joined |
{
"username": "sunqi",
"nickname": "孙七",
"avatar_url": "https://cdn.example.com/avatar/sunqi.png",
"role": "member",
"group_nickname": "",
"attributes": null,
"muted": false,
"muted_until": null,
"joined_via": "invite",
"inviter": "lisi",
"joined_at": "2026-10-02T19:10:46.913Z"
}Batch results
In results from Create a group, Add group members and Remove group members, each item is one user's result. Successful items have only username and status; failed items have no status but code and message, and some also details.
| Field | Type | Description |
|---|---|---|
username | String | The username (lowercase) |
status | String | On success: joined, already_member (already a member), removed, or not_member (wasn't a member) |
code | String | On failure: the error code, see below |
message | String | On failure: the error text |
details | Object | Details of the failure; only some errors have it |
code | details.reason | Description |
|---|---|---|
not_found | The user doesn't exist | |
permission_denied | group_blacklisted | The user is on the group block list and can't join |
permission_denied | The owner can't be removed | |
limit_exceeded | group_member_limit | The group is full |
limit_exceeded | user_group_limit | The user has joined the maximum number of groups |
