Groups
Groups have a fixed membership and suit teams, parent groups, interest groups and the like. Users can create groups from the client, invite others, apply to join public groups, and join through join links. Owners and admins can edit group profiles, manage members and approve join requests.
The Web SDK stores "My groups" and any member lists it has read locally, and exposes them as live lists. They sync automatically after sign-in and reconnects, and changes made by other members or on other devices show up in real time. Data you only page through, such as join requests and invitations, is queried page by page.
This page covers operations on groups and their members. To send group messages, see Messages (use the target { to_group: group_id }); for group files, see Files. Group concepts (types, roles, settings, status) are the same as on the server; for the full rules, see Group management in the server docs.
| You want to | See |
|---|---|
| Create groups and show "My groups" | Create a group, My groups |
| Change the group name, announcement or avatar | Edit group profile and settings |
| Show, invite and remove members | Member list, Invite members, Remove members |
| Set admins, mute, group blocklist | Set admins, Mute, Group blocklist |
| Let users find and apply to public groups, and approve applications | Public groups and join requests |
| Handle invitations sent to the user | Whether invitations need confirmation |
| Add people with a link or QR code | Join links |
| Leave, dissolve or transfer ownership | Leave and dissolve, Transfer ownership |
Basics
Group types
type | Description |
|---|---|
private | Private group, the default. People join only when invited by a member, through a join link, or when added by your server. Non-members can't see the group, and any operation on it returns not_found |
public | Public group. Users can find it, see its name, avatar, description and member count, and apply to join |
Roles and permissions
| Action | Owner owner | Admin admin | Member member |
|---|---|---|---|
| Edit group profile and settings | Yes | Yes (except group type) | No |
| Invite others | Yes | Yes | Depends on the member_invite setting |
| Remove and mute members, manage the group blocklist | Yes | Regular members only | No |
| Mute everyone, approve join requests, create join links | Yes | Yes | No |
| Set admins, transfer ownership, dissolve the group | Yes | No | No |
| Change their own group nickname | Yes | Yes | Yes |
A group can have up to 20 admins. The owner can't be removed, muted or added to the group blocklist. To leave, the owner must transfer ownership first.
Group settings
| Field | Values | Description |
|---|---|---|
member_invite | free (default), approval, disabled | Whether regular members can invite others: directly, with owner or admin approval, or not at all |
join_mode | approval (default), free | How applications to a public group work: approval required, or join on applying. null for private groups and can't be set |
mute_all | true, false | Mute everyone; only the owner and admins can speak |
max_members | Read-only | The group's member limit; only your server or the console can set it |
Create a group
A user who creates a group from the client becomes its owner and can invite initial members at the same time (up to 100). Initial members are handled like invitations: if someone's invitation setting requires confirmation, they get an invitation and join only after accepting.
const group = await im.groups.create({
name: '产品研发群',
type: 'private',
description: '产品和研发日常沟通',
members: ['lisi', 'wangwu', 'zhaoliu'],
message: '拉你进项目群', // 给需要确认的被邀请人看的附言
});
for (const r of group.results) {
if ('code' in r) console.warn(`${r.username} 没有加入:${r.code}`);
else if (r.status === 'invited') console.log(`${r.username} 需要确认后入群`);
}- The return value is the new group's
GroupInfoplusresults, which lists each initial member's outcome in request order; seeGroupMemberResult. Initial members failing to join doesn't stop the group from being created. - The new group appears in "My groups" right away.
- If the app turns off client group creation in its runtime policies, the call rejects with
permission_denied. Readim.config?.client_group_create_enabledfirst to decide whether to show a "Create group" option. - If the user has joined the maximum number of groups, it rejects with
limit_exceeded(details.reasonisuser_group_limit) and no group is created. If the app has reached its plan's group limit, the reason isapp_group_limit. - The group name, description, announcement and avatar go through content moderation. If they fail, the call rejects with
content_rejectedanddetails.fieldnames the field.
Group creation is never retried automatically
After a network error or timeout, the SDK refreshes the user's group list. If it finds the new group (owned by the user and created after this call), it returns success with an empty results array; use members() to see who actually joined. If not, it rejects with timeout and the user decides whether to try again. On rate_limited, tell the user; don't retry automatically.
My groups
im.groups.list() returns a live list of every group the user has joined. Each item is a GroupView, sorted by group name. The SDK syncs it after sign-in; creating, joining, leaving, being removed, dissolution and group profile changes all show up in the list. Call dispose() when you no longer show it.
const groups = im.groups.list();
groups.subscribe(() => {
const { items, loading } = groups.getSnapshot();
if (!loading) render(items.map((g: GroupView) => `${g.group.name || '群聊'}(${g.group.member_count})`));
});The user's role, group nickname and mute state are in group.self:
const view = await im.groups.get('1840012345678901');
const self = view?.group.self;
const canManage = self?.role === 'owner' || self?.role === 'admin';im.groups.get(group_id)reads local storage first and returns the group if found. If it isn't stored locally (for example a public group someone shared) or you passrefresh: true, it queries the server. Groups the user can't see (nonexistent, or private groups they're not in) returnundefined.- For groups the user has left,
in_my_groupsisfalseandgroupholds the last known information from before they left, so the conversation list can still show the group's name and avatar. - To query several groups at once, use
im.groups.lookup(group_ids)(up to 100). Public groups the user isn't in return only public fields, and groups the user can't see are left out.
Changes to group profiles and to "My groups" are reported with groups.changed: upserted lists groups added or changed, and removed lists groups that left "My groups".
Edit group profile and settings
The owner and admins can edit the group profile and settings; only the fields passed are sent. Only the owner can change the group type. Online members receive the change and the local group information updates.
await im.groups.update('1840012345678901', {
name: '产品研发群(2 期)',
announcement: '本周五发布 2.3 版本',
attributes: { project_id: 'P-1024', old_key: null }, // 按项合并,null 删除这个键
member_invite: 'approval',
});- Changing a public group to private cancels all pending join applications. Changing a private group to public sets the application mode to approval required by default.
- Setting
join_modeon a private group rejects withinvalid_argument. - If several people may edit at once, pass the
group.info_versionyou read asversion. If the profile was changed by someone else, the call rejects withversion_conflictand nothing is changed. - New names, descriptions, announcements and avatars go through content moderation and reject with
content_rejectedif they fail. - If the group is disabled, the call rejects with
group_disabled.
Set a group avatar
Upload the group avatar first with purpose group_avatar, then write the returned public URL to the group profile (you can upload before the group exists):
async function changeGroupAvatar(file: File) {
const info = await im.files.upload(file, { purpose: 'group_avatar' });
await im.groups.update(groupId, { avatar_url: info.url });
}Member list
im.groups.members(group_id) returns a live list of group members: owner first, then admins, then regular members, each role ordered by join time. Only members can view it.
const members = im.groups.members(groupId);
members.subscribe(() => {
const { items, loading, error } = members.getSnapshot();
if (error) showToast(error.message);
else if (!loading) render(items.map((m: GroupMember) => m.group_nickname || m.nickname || m.username));
});
// 只看管理员、只看被禁言的成员
const admins = im.groups.members(groupId, { role: 'admin' });
const muted = im.groups.members(groupId, { muted: true });- The first time you open a group's member list, the SDK fetches all members (up to 5000) and stores them locally. Later it only checks for changes, including after a reload. Filtering happens locally.
- Members joining or leaving, and changes to roles, group nicknames and mutes, show up in the list in real time, and
groupMembers.changedis emitted. - Call
dispose()when you no longer show the list.
To show the group nicknames of just a few people (such as message senders), you don't need the whole list. Use lookupMembers (up to 100; non-members are left out):
const found = await im.groups.lookupMembers(groupId, ['lisi', 'wangwu']);The group's online count (requires presence to be on and presence_scope to be all; otherwise it rejects with permission_denied):
const { online_count } = await im.groups.onlineCount(groupId);Repeated queries for the same group within 30 seconds return the previous result.
Invite members
You can invite up to 100 people at once, with an optional message. Results come back one per person in request order, and one failure doesn't affect the others:
const results = await im.groups.invite(groupId, ['sunqi', 'zhouba'], { message: '欢迎加入' });
for (const r of results) {
if ('code' in r) showToast(`${r.username}:${r.code}`);
}Successful status values:
status | Description |
|---|---|
joined | Joined the group |
invited | The person must confirm; an invitation was sent. See Whether invitations need confirmation |
pending_review | The user is a regular member and the group requires approval for invitations; waiting for the owner or an admin |
already_member | Already a member |
Failed items carry a code. Common ones:
code | Cause |
|---|---|
permission_denied | The person is on the group blocklist (details.reason is group_blacklisted), or your server rejected it through the before-join-group callback |
limit_exceeded | The group is full (group_member_limit), or the person has joined the maximum number of groups (user_group_limit) |
user_blocked | The person has blocked the user |
not_friend | The app only allows inviting friends |
rate_limited | declined_cooldown: the invitation cooldown is on and the person declined this group's invitation or left this group within the last 24 hours. request_rate, group_join_rate: too many invitations or joins |
not_found | The user doesn't exist |
The invitation cooldown (group_invite_cooldown_enabled) is set in the console's group runtime policies:
- Off by default: after someone leaves or declines, you can invite them again right away.
- When on: for 24 hours after someone leaves or declines, this group can't invite them again.
- The cooldown only limits invitations. Being added by the server, applying and join links aren't affected; requirements like the person's confirmation and admin approval still apply.
- Separately, after a join application is declined, the applicant can't apply again for 24 hours. This always applies.
A successful HTTP request doesn't mean everyone joined. Check each item in results and tell the user who joined, who must confirm, who awaits admin approval, and why others failed.
The whole request fails when a regular member invites in a group whose member_invite is disabled (permission_denied), or when the group is disabled (group_disabled).
Remove members
The owner and admins can remove members (admins only regular members), up to 100 at once. In the results, removed means removed and not_member means they weren't a member. You can't remove yourself this way; use leave instead.
await im.groups.removeMembers(groupId, ['zhouba']);Set admins
The owner can make members admins or revoke admin. A group can have up to 20 admins (beyond that it rejects with limit_exceeded, details.reason admin_limit).
await im.groups.setRole(groupId, 'lisi', 'admin');
await im.groups.setRole(groupId, 'lisi', 'member'); // 取消管理员Group nicknames and member attributes
Every member can change their own group nickname. The owner and admins can also change the group nickname and member attributes of members below them. Member attributes are merged key by key, and keys set to null are deleted.
const me = im.auth.currentUser!.username;
await im.groups.updateMember(groupId, me, { group_nickname: '李四-研发' });
await im.groups.updateMember(groupId, 'wangwu', { attributes: { title: '后端' } });Group nicknames are up to 64 characters; an empty string clears it. They go through content moderation. To display names, use displayName(im, username, { group_id }) from @deeprespond/im-web/render, which takes the first non-empty value of friend remark, group nickname, nickname and username.
Mute
Mute a member
The owner and admins can mute members (admins only regular members). Omitting the duration mutes permanently. A muted member sending a group message gets group_muted (details.reason is member).
await im.groups.mute(groupId, 'zhouba', { duration_seconds: 3600 }); // 禁言 1 小时
await im.groups.unmute(groupId, 'zhouba');Durations range from 1 to 315360000 seconds (10 years). Whether the user is muted is in group.self.muted and muted_until.
Mute everyone
When on, only the owner and admins can speak. Regular members sending a group message get group_muted (details.reason is all). An admin who is individually muted still can't speak.
await im.groups.setMuteAll(groupId, true);
await im.groups.setMuteAll(groupId, false);Group blocklist
A user added to the group blocklist is removed from the group (if in it) and can no longer apply, be invited or join through a link. The owner and admins manage the group blocklist; admins can only remove entries added by admins. Each group's blocklist holds up to 1000 people.
await im.groups.blacklist.add(groupId, 'spammer', { reason: '发广告' });
const page = await im.groups.blacklist.list(groupId, { limit: 50 });
await im.groups.blacklist.remove(groupId, 'spammer');Each item in the list is a GroupBlacklistEntry. The group blocklist only affects this group and is unrelated to the user-to-user blocklist.
Public groups and join requests
Search public groups
im.groups.publicGroups() lists public groups, newest first, optionally searching by name prefix. Each item has only public fields (group_id, type, name, avatar_url, description, member_count, join_mode, status). self is the user's information in the group, or null if not a member.
const page = await im.groups.publicGroups({ name_prefix: '摄影', limit: 20 });
render(page.items.map((g) => `${g.name}(${g.member_count} 人)${g.self ? ' 已加入' : ''}`));Apply to join
const { status } = await im.groups.applications.apply(groupId, { message: '我也喜欢摄影' });
if (status === 'joined') showToast('已加入');
else if (status === 'pending') showToast('已提交申请,等待群主或管理员审批');If the group's application mode is free, the user joins immediately. If it's approval, a pending application is created, and the user joins once any owner or admin approves. The message is up to 256 characters. After an application is declined, the user can't apply to that group again for 24 hours (rate_limited, details.reason declined_cooldown), and a user can apply to the same group at most 5 times a day (pair_daily_limit). Private groups can't be applied to and return not_found; users on the group blocklist get permission_denied (group_blacklisted).
To withdraw a pending application and view the user's own applications:
await im.groups.applications.withdraw(groupId);
const mine = await im.groups.applications.mine();If the application has already been handled, withdraw rejects with version_conflict and details.status is its current status.
Approve applications and members' invitations
The owner and admins view and handle the group's requests with im.groups.requests. A request is identified by group + kind + username; there's no separate ID. Request kinds (kind):
kind | Description | Handled by |
|---|---|---|
application | A user applies to join a public group | Owner or admin |
invite_review | An invitation sent by a regular member in a group that requires approval | Owner or admin |
invitation | An invitation waiting for the invitee to confirm | The invitee; see the next section |
interface GroupRequest {
group_id: string;
kind: 'application' | 'invite_review' | 'invitation';
username: string;
nickname: string;
message: string;
status: 'pending' | 'accepted' | 'declined' | 'canceled' | 'expired';
}
const page = await im.groups.requests.list(groupId, { kind: 'application', status: 'pending' });
for (const r of page.items as GroupRequest[]) {
render(`${r.nickname || r.username} 申请加入:${r.message}`);
}
const { result } = await im.groups.requests.approve(groupId, 'application', 'sunqi');
await im.groups.requests.decline(groupId, 'application', 'zhouba', { reason: '仅限本校学生' });Return types
The return values of requests.list(), applications.mine(), invitations.received(), blacklist.list(), the joinLink methods and settings.get() are currently declared as unknown in TypeScript. Their fields are listed under Types on this page; declare a type yourself and cast, as in the example above.
resultfromapprove:joinedmeans the person joined;invitedmeans a member's invitation was approved and the invitee must confirm, so an invitation was sent to them;canceledmeans the invitee can no longer receive this group's invitation and the request lapsed.- Join requirements are checked again when handling (group active, not on the group blocklist, member and group limits not reached). If they aren't met, the call rejects with the matching error and the request stays pending, so you can handle it again later.
- If someone else already handled the request, the call rejects with
version_conflictanddetails.statusis its current status. Expired or nonexistent requests reject withnot_found. - Decline reasons are up to 256 characters and shown to the applicant or inviter. After an application is declined, the applicant can't apply again for 24 hours.
- Regular members calling
requests.list()see only the invitations they sent. The inviter, owner and admins can withdraw a pending invitation withrequests.withdrawInvite(group_id, kind, username)(kindisinvite_revieworinvitation).
When a new request arrives or a request is handled, groupRequests.changed is emitted (list is pending, group_id is the group). Use it to refresh an approval page or show a red dot. Pending requests expire 7 days after they were last made. For the full rules, see Join requests and invitations in the server docs.
Whether invitations need confirmation
Each user can choose how invitations to groups are handled: allow_any joins directly, and need_confirm creates an invitation that the user must accept first. If the user hasn't set it, the default from the app's runtime policies applies (allow_any by default).
const settings = (await im.groups.settings.get()) as { invite_mode: string | null; effective_invite_mode: string };
render(settings.effective_invite_mode);
await im.groups.settings.set('need_confirm');
await im.groups.settings.set(null); // 清除本人的设置,改用应用的默认值Handle received invitations
interface GroupInvitation {
group_id: string;
inviter: { username: string; nickname: string; avatar_url: string } | null;
message: string;
status: string;
group?: { group_id: string; name: string; avatar_url: string; member_count: number };
}
const page = await im.groups.invitations.received({ status: 'pending' });
for (const inv of page.items as GroupInvitation[]) {
render(`${inv.inviter?.nickname ?? '有人'} 邀请你加入 ${inv.group?.name ?? '群聊'}`);
}
const group = await im.groups.invitations.accept('1840012345678901');
await im.groups.invitations.decline('1840099999999999', { reason: '暂时不参加' });- Without
cursorandstatus,invitations.received()andapplications.mine()return the first page held in SDK memory without sending a request. The SDK refreshes that page on sign-in, on reconnect and when related notifications arrive, then emitsgroupRequests.changed(listisinvitationsorapplications). - Accepting joins the group and returns the group information; the group appears in "My groups" right away. If the user already joined another way, the group information is returned too.
- After declining, the user can by default be invited again right away. Only when the app turns on the invitation cooldown can this group not invite them for 24 hours.
Join links
The owner and admins can create join links, including for private groups. Users who open the link join after confirming, with no approval needed. A group has only one valid link at a time; creating a new one or revoking it invalidates the old one immediately. A link also becomes invalid when its creator is no longer the owner or an admin. Links can be shown as QR codes.
interface JoinLink { link: string | null; expires_at: string | null; created_by: string | null }
const created = (await im.groups.joinLink.create(groupId, { expires_in_seconds: 86400 })) as JoinLink;
render(created.link);
const current = (await im.groups.joinLink.get(groupId)) as JoinLink; // 没有有效链接时三项都为 null
await im.groups.joinLink.revoke(groupId);Validity ranges from 86400 to 604800 seconds (1 to 7 days), 7 days by default.
The person opening the link previews it first, then confirms:
interface JoinLinkPreview { group_id: string; name: string; avatar_url: string; member_count: number; is_member: boolean }
const preview = (await im.groups.joinLink.preview(link)) as JoinLinkPreview;
if (preview.is_member) {
showToast('你已在群中');
} else {
const group = await im.groups.joinLink.join(link);
render(group.name);
}If the link is invalid, expired or revoked, or the group is disabled or dissolved, both preview and join reject with not_found. If the user is already a member, join returns the group information. Preview and join share a limit of 20 calls per user per minute.
Leave and dissolve
await im.groups.leave(groupId); // 退出群
await im.groups.dismiss(groupId); // 解散群,只有群主可以- Leave. The group is removed from "My groups" and its local member list is deleted. The owner leaving rejects with
owner_transfer_required; transfer ownership first, or dissolve the group if the owner is the only member. After leaving, the user can by default be invited again right away; only when the app turns on the invitation cooldown can this group not invite them for 24 hours. - Dissolve. This can't be undone. All members are notified and the group is removed from their "My groups"; all group files are deleted. Groups disabled by the platform can't be dissolved (
group_disabled).
When the user is removed or the group is dissolved, their group list also updates automatically. The reason can be seen from the group notice message in the conversation (see Messages).
Transfer ownership
The owner can transfer ownership to another member. The previous owner becomes a regular member, and join links they created become invalid.
const info = await im.groups.transferOwner(groupId, 'wangwu');
console.log(info.owner); // wangwuAPI reference
Every group_id parameter is a group ID string. Private groups are invisible to non-members, so any operation on them rejects with not_found. When the user isn't a member of a public group, member-only operations reject with not_group_member. Insufficient roles reject with permission_denied, and operations blocked while a group is disabled reject with group_disabled. These errors aren't repeated for each method below.
im.groups.list()
Returns a live list of the groups the user has joined (LiveList<GroupView>), sorted by group name. The snapshot's hasMore is always false. Call dispose() when you're done with it. Throws not_signed_in when signed out.
im.groups.get()
Reads one group.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
opts.refresh | boolean | No | When true, skips local storage and queries the server |
Returns: Promise<GroupView | undefined>, undefined for groups the user can't see.
im.groups.lookup()
Queries group information in a batch.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_ids | string[] | Yes | 1 to 100 group IDs |
Returns: Promise<GroupInfo[]>. Groups the user can't see are left out; public groups the user isn't in have only public fields.
Errors: local_validation (empty or more than 100).
im.groups.publicGroups()
Lists public groups, newest first, excluding disabled and dissolved groups.
| Parameter | Type | Required | Description |
|---|---|---|---|
p.name_prefix | string | No | Search by group name prefix |
p.cursor | string | null | No | Cursor for the next page |
p.limit | number | No | Items per page; default 20, max 100 |
Returns: Promise<Page<GroupInfo>>.
im.groups.create()
Creates a group with the user as owner. Never retried automatically; see Create a group.
| Parameter | Type | Required | Description |
|---|---|---|---|
p.type | 'private' | 'public' | No | Group type; default private |
p.name | string | No | Group name, up to 64 characters |
p.avatar_url | string | No | Group avatar URL |
p.description | string | No | Description, up to 512 characters |
p.announcement | string | No | Announcement, up to 2048 characters |
p.attributes | Record<string, string | null> | null | No | Custom attributes, visible to all members; up to 32 keys and 4 KB in total |
p.member_invite | 'free' | 'approval' | 'disabled' | No | Whether regular members can invite others; default free |
p.join_mode | 'approval' | 'free' | No | Application mode for public groups; default approval. Can't be set for private groups |
p.members | string[] | No | Usernames of initial members, up to 100 |
p.message | string | No | Invitation message for initial members |
Returns: Promise<GroupInfo & { results: GroupMemberResult[] }>.
Errors: invalid_argument, permission_denied (the app turned off client group creation), limit_exceeded (user_group_limit, app_group_limit), content_rejected, rate_limited, app_unavailable (app is read-only), timeout.
im.groups.update()
Edits the group profile and settings (owner and admins; only the owner can change the type). Only the fields passed are sent.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
patch | Same as type through join_mode in create | Yes | Fields to change, at least one. attributes is merged key by key: keys set to null are deleted, and null for the whole field clears all |
patch.version | number | No | The info_version you read. Nothing is changed if it doesn't match |
Returns: Promise<GroupInfo>, the updated group.
Errors: invalid_argument (for example setting join_mode on a private group), content_rejected, version_conflict.
im.groups.transferOwner()
Transfers ownership (owner).
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
username | string | Yes | The new owner; must be a member |
Returns: Promise<GroupInfo>.
Errors: not_group_member (the person isn't a member).
im.groups.setMuteAll()
Turns mute-everyone on or off (owner and admins).
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
on | boolean | Yes | true to turn on, false to turn off |
Returns: Promise<GroupInfo>.
im.groups.leave()
Leaves a group.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
Returns: Promise<void>.
Errors: owner_transfer_required (the user is the owner).
im.groups.dismiss()
Dissolves a group (owner). Can't be undone.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
Returns: Promise<void>.
im.groups.onlineCount()
Queries the group's online count. Repeated calls for the same group within 30 seconds return the previous result.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
Returns: Promise<{ online_count: number; counted_at: string }>.
Errors: permission_denied (details.reason is presence_disabled: presence isn't on for the app; or presence_scope: presence visibility isn't all), rate_limited.
im.groups.members()
Returns a live list of group members (LiveList<GroupMember>). Only members can view it. Call dispose() when you're done with it.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
filter.role | GroupRole | No | List only members with this role |
filter.muted | boolean | No | true lists only muted members, false only unmuted ones |
If fetching fails, the snapshot's error is the matching DRError. Throws not_signed_in when signed out.
im.groups.lookupMembers()
Queries members by username (members only).
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
usernames | string[] | Yes | 1 to 100 usernames |
Returns: Promise<GroupMember[]>. Non-members are left out.
Errors: local_validation (empty or more than 100).
im.groups.invite()
Invites people to the group.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
usernames | string[] | Yes | 1 to 100 usernames |
p.message | string | No | Invitation message |
Returns: Promise<GroupMemberResult[]>; see Invite members for each person's result.
Errors: local_validation, permission_denied (a regular member inviting in a group that doesn't allow it), content_rejected (the message).
im.groups.removeMembers()
Removes members (owner and admins).
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
usernames | string[] | Yes | 1 to 100 usernames |
Returns: Promise<GroupMemberResult[]>; successful status is removed or not_member.
Errors: local_validation, invalid_argument (removing yourself).
im.groups.updateMember()
Changes a member's group nickname and member attributes: the member themself, or an owner or admin ranked above them.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
username | string | Yes | The member's username |
p.group_nickname | string | No | Group nickname, up to 64 characters. An empty string clears it |
p.attributes | Record<string, string | null> | null | No | Member attributes, merged key by key; null for the whole field clears all. Up to 16 keys and 1 KB in total |
Returns: Promise<GroupMember>.
Errors: content_rejected (group nickname), not_group_member (the person isn't a member).
im.groups.setRole()
Makes a member an admin or revokes admin (owner).
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
username | string | Yes | The member's username |
role | 'admin' | 'member' | Yes | The new role |
Returns: Promise<GroupMember>.
Errors: limit_exceeded (admin_limit), invalid_argument (applied to the owner).
im.groups.mute()
Mutes a member (owner and admins).
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
username | string | Yes | The member's username |
p.duration_seconds | number | No | Duration, 1 to 315360000 seconds. Omit to mute permanently |
Returns: Promise<GroupMember>.
im.groups.unmute()
Unmutes a member (owner and admins). You can't unmute yourself.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
username | string | Yes | The member's username |
Returns: Promise<GroupMember>.
im.groups.blacklist.list()
Queries the group blocklist (owner and admins).
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
page.cursor | string | null | No | Cursor for the next page |
page.limit | number | No | Items per page; default 20, max 100 |
Returns: Promise<Page<unknown>>; each item is a GroupBlacklistEntry.
im.groups.blacklist.add()
Adds a user to the group blocklist (owner and admins) and removes them from the group if they're in it. Succeeds if already on the list.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
username | string | Yes | Username |
p.reason | string | No | Reason |
Returns: Promise<void>.
Errors: limit_exceeded (group_blacklist_limit), invalid_argument (blocking yourself).
im.groups.blacklist.remove()
Removes a user from the group blocklist. Admins can only remove entries added by admins.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
username | string | Yes | Username |
Returns: Promise<void>.
im.groups.joinLink.get()
Gets the current valid join link (owner and admins).
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
Returns: Promise<unknown>, a JoinLink. All three fields are null when there's no valid link.
im.groups.joinLink.create()
Creates or recreates the join link (owner and admins); the old link stops working immediately.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
p.expires_in_seconds | number | No | Validity, 86400 to 604800 seconds; default 604800 |
Returns: Promise<unknown>, a JoinLink.
im.groups.joinLink.revoke()
Revokes the join link (owner and admins).
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
Returns: Promise<void>.
im.groups.joinLink.preview()
Previews a join link.
| Parameter | Type | Required | Description |
|---|---|---|---|
link | string | Yes | The join link |
Returns: Promise<unknown>, a JoinLinkPreview.
Errors: not_found (the link is invalid, expired or revoked, or the group is disabled or dissolved), rate_limited.
im.groups.joinLink.join()
Joins through a join link. Returns the group information if already a member.
| Parameter | Type | Required | Description |
|---|---|---|---|
link | string | Yes | The join link |
Returns: Promise<GroupInfo>.
Errors: not_found, permission_denied (on the group blocklist, or rejected by the before-join-group callback), limit_exceeded (the group is full or the user has joined the maximum number of groups), rate_limited.
im.groups.applications.apply()
Applies to join a public group.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
p.message | string | No | Message, up to 256 characters |
Returns: Promise<{ status: 'joined' | 'pending' | 'already_member' }>.
Errors: not_found (private group), permission_denied (group_blacklisted, or rejected by the before-join-group callback), limit_exceeded (user_group_limit, group_member_limit), rate_limited (declined_cooldown, pair_daily_limit, request_rate, group_join_rate), content_rejected.
im.groups.applications.withdraw()
Withdraws the user's pending application. Succeeds if already withdrawn.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
Returns: Promise<void>.
Errors: version_conflict (the application was already handled; details.status is its current status).
im.groups.applications.mine()
Queries the user's join applications, newest first.
| Parameter | Type | Required | Description |
|---|---|---|---|
p.status | string | No | List only applications with this status |
p.cursor | string | null | No | Cursor for the next page |
p.limit | number | No | Items per page |
Returns: Promise<Page<unknown>>; each item is a GroupRequest. Without cursor and status, returns the first page held in SDK memory.
im.groups.invitations.received()
Queries group invitations the user received, newest first. Parameters are the same as applications.mine().
Returns: Promise<Page<unknown>>; each item is a GroupRequest. Without cursor and status, returns the first page held in SDK memory.
im.groups.invitations.accept()
Accepts a group invitation.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
Returns: Promise<GroupInfo>.
Errors: not_found (the invitation doesn't exist or has expired), permission_denied (on the group blocklist), limit_exceeded, rate_limited (group_join_rate), version_conflict (for example the invitation was withdrawn).
im.groups.invitations.decline()
Declines a group invitation. By default the user can be invited again right away; only when the app turns on group_invite_cooldown_enabled can this group not invite them for 24 hours.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
p.reason | string | No | Reason, up to 256 characters |
Returns: Promise<void>.
im.groups.requests.list()
Queries the group's requests, newest first. The owner and admins see all kinds; regular members see only invitations they sent.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
p.kind | string | No | application, invite_review or invitation |
p.status | string | No | pending, accepted, declined, canceled or expired |
p.cursor | string | null | No | Cursor for the next page |
p.limit | number | No | Items per page |
Returns: Promise<Page<unknown>>; each item is a GroupRequest. Always queries the server.
im.groups.requests.approve()
Approves an application or a member's invitation (owner and admins).
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
kind | string | Yes | application or invite_review |
username | string | Yes | The applicant or invitee |
Returns: Promise<{ result: 'joined' | 'invited' | 'canceled' }>.
Errors: not_found (the request doesn't exist or has expired), version_conflict (someone else handled it), permission_denied (group_blacklisted), limit_exceeded, rate_limited (group_join_rate).
im.groups.requests.decline()
Declines an application or a member's invitation (owner and admins).
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
kind | string | Yes | application or invite_review |
username | string | Yes | The applicant or invitee |
p.reason | string | No | Reason, up to 256 characters |
Returns: Promise<void>.
Errors: not_found, version_conflict, content_rejected (the reason).
im.groups.requests.withdrawInvite()
Withdraws a pending invitation: the inviter, the owner or an admin.
| Parameter | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Group ID |
kind | 'invite_review' | 'invitation' | Yes | Request kind |
username | string | Yes | The invitee |
Returns: Promise<void>.
Errors: not_found, version_conflict.
im.groups.settings.get()
Gets how invitations to the user are handled.
Returns: Promise<unknown>, which is { invite_mode: 'allow_any' | 'need_confirm' | null; effective_invite_mode: 'allow_any' | 'need_confirm' }. invite_mode is the user's own setting, null if never set.
im.groups.settings.set()
Sets how invitations to the user are handled.
| Parameter | Type | Required | Description |
|---|---|---|---|
mode | string | null | Yes | allow_any or need_confirm. null clears the setting and uses the app default |
Returns: Promise<void>.
Related events
| Event | Payload | Description |
|---|---|---|
groups.changed | { upserted: string[]; removed: string[] } | Group information changed (upserted), or groups left "My groups" (removed) |
groupMembers.changed | { group_id: string; usernames: string[] | null } | A locally stored member list changed; usernames is null when the whole list changed |
groupRequests.changed | { list: 'pending' | 'invitations' | 'applications'; group_id: string | null } | pending: this group's pending requests changed. invitations, applications: the first page of the user's received invitations or sent applications was refreshed |
Types
GroupView
| Field | Type | Description |
|---|---|---|
group_id | string | Group ID |
group | GroupInfo | Group information; for groups the user has left, the last known information from before they left |
in_my_groups | boolean | Whether it's in "My groups" |
GroupInfo
Members see every field. For public groups the user isn't in, only group_id, type, name, avatar_url, description, member_count, join_mode, status and self are present.
| Field | Type | Description |
|---|---|---|
group_id | string | Group ID |
type | 'private' | 'public' | Group type |
name | string | Group name; empty string if none |
avatar_url | string | Group avatar URL; empty string if none |
description | string | Description |
announcement | string | Announcement |
announcement_updated_at | string | null | When the announcement was last changed |
announcement_updated_by | string | null | Username of whoever last changed the announcement from a client |
attributes | Record<string, string> | null | Custom attributes |
owner | string | The owner's username |
member_invite | string | Whether regular members can invite others: free, approval, disabled |
join_mode | string | null | Application mode for public groups: approval, free; null for private groups |
mute_all | boolean | Whether mute-everyone is on |
max_members | number | Member limit |
member_count | number | Current member count, including the owner |
status | string | active, disabled or dismissed (dissolved) |
disabled_by | string | null | Who disabled it: tenant (the app) or platform; null when not disabled |
info_version | number | Version of the group profile and settings; can be passed as version to update |
member_version | number | Version of the member list |
created_at | string | Creation time |
self | object | null | The user's information in the group: role, group_nickname, muted, muted_until, joined_at. null if not a member |
GroupMember
| Field | Type | Description |
|---|---|---|
username | string | Username |
nickname | string | Nickname from the user's profile |
avatar_url | string | Avatar from the user's profile |
role | GroupRole | owner, admin or member |
group_nickname | string | Group nickname in this group; empty string if none |
attributes | Record<string, string> | null | Member attributes |
muted | boolean | Whether muted |
muted_until | string | null | When the mute ends; null for a permanent mute |
joined_via | string | How they joined: create (at creation), server (added by the server), invite, apply or link |
inviter | string | null | The inviter's username |
joined_at | string | When they joined |
GroupMemberResult
The result for one user in a batch operation, one of:
| Form | Description |
|---|---|
{ username, status } | Success. status is joined, invited, pending_review or already_member (invite, create), or removed or not_member (remove) |
{ username, code, message?, details? } | Failure. code is the error code and details.reason explains why |
GroupRole
'owner' | 'admin' | 'member'.
GroupRequest
A join application or invitation; each item from requests.list(), applications.mine() and invitations.received().
| Field | Type | Description |
|---|---|---|
group_id | string | Group ID |
kind | string | application, invite_review or invitation |
username | string | The request's target user: the applicant or invitee |
nickname | string | The target user's nickname |
avatar_url | string | The target user's avatar |
inviter | object | null | The inviter's username, nickname and avatar_url; null for applications |
message | string | Application or invitation message |
reason | string | Decline reason |
status | string | pending, accepted, declined, canceled or expired |
cancel_cause | string | null | Why it lapsed: withdrawn, joined, blacklisted, group_private, not_eligible, dismissed |
requested_at | string | When it was last made |
expires_at | string | null | When it expires; only set while pending |
handled_at | string | null | When it was handled |
handled_by | string | null | Username of whoever handled it |
group | object | Only in the user's received invitations and sent applications: the group's group_id, name, avatar_url and member_count. Absent for private groups once the request is handled |
While a group is disabled, its pending requests show as canceled with cancel_cause null, and are restored when the group is re-enabled.
GroupBlacklistEntry
| Field | Type | Description |
|---|---|---|
username | string | Username |
nickname | string | Nickname |
avatar_url | string | Avatar |
reason | string | Reason |
created_by | string | null | The owner or admin who added it |
created_by_role | string | Role of whoever added it: owner, admin or server |
created_at | string | When it was added |
JoinLink
| Field | Type | Description |
|---|---|---|
link | string | null | The join link |
expires_at | string | null | When it expires |
created_by | string | null | Who created it |
JoinLinkPreview
| Field | Type | Description |
|---|---|---|
group_id | string | Group ID |
name | string | Group name |
avatar_url | string | Group avatar |
member_count | number | Member count |
is_member | boolean | Whether the user is already a member |
