Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Group Endpoints

Create Group

Creates a new group with the authenticated user as the sole member and admin.

POST /api/v1/groups

Authentication: Required.

Request Body — CreateGroupRequest

FieldTypeRequiredDescription
group_namestringYesUnique group name. 1–64 characters, must start with ASCII alphanumeric, only letters/digits/underscores.
aliasstringNoDisplay name. Max 64 characters, no ASCII control characters.

Response Body — CreateGroupResponse

FieldTypeDescription
group_idbytesThe server-assigned unique group ID (UUID).

Notes

The creator is automatically added as a member with the admin role. No other members are added at creation time. Additional members are added via the escrow invite system.

After creating the group on the server, the client MUST:

  1. Create an MLS group locally.
  2. Upload the initial commit and GroupInfo via POST /api/v1/groups/{id}/commit, including the mls_group_id.

Status Codes

CodeCondition
201 CreatedGroup created successfully.
400 Bad RequestInvalid group name format, alias too long or contains control characters.
401 UnauthorizedInvalid or expired token.
409 ConflictGroup name already taken.

SSE Events

None.


List Groups

Lists all groups the authenticated user is a member of, including member lists and metadata.

GET /api/v1/groups

Authentication: Required.

Request Body

None.

Response Body — ListGroupsResponse

FieldTypeDescription
groupsrepeated GroupInfoList of groups the user belongs to.

Each GroupInfo:

FieldTypeDescription
group_idbytesServer-assigned group ID (UUID).
aliasstringDisplay name (may be empty).
group_namestringUnique group name.
membersrepeated GroupMemberAll members of the group.
mls_group_idstringHex-encoded MLS group identifier.
message_expiry_secondsint64Per-group message expiry (-1=disabled, 0=delete-after-fetch, >0=seconds).
visibilityGroupVisibilityPRIVATE (default) or PUBLIC.

Each GroupMember:

FieldTypeDescription
user_idbytesMember’s user ID (UUID).
usernamestringMember’s username.
aliasstringMember’s display name (may be empty).
roleGroupRoleGROUP_ROLE_ADMIN or GROUP_ROLE_MEMBER.
signing_key_fingerprintstringSHA-256 hex of the member’s MLS signing public key (may be empty).

Status Codes

CodeCondition
200 OKSuccess.
401 UnauthorizedInvalid or expired token.

SSE Events

None.


Update Group

Updates a group’s alias, name, and/or message expiry settings.

PATCH /api/v1/groups/{group_id}

Authentication: Required. Authorization: Admin only.

Path Parameters

ParameterTypeDescription
group_idstringThe group to update.

Request Body — UpdateGroupRequest

FieldTypeRequiredDescription
aliasstringNoNew display name. Max 64 characters, no ASCII control characters.
group_namestringNoNew group name. Same validation rules as creation.
message_expiry_secondsint64NoNew message expiry value. Only applied when update_message_expiry is true.
update_message_expiryboolNoMUST be true for the message_expiry_seconds field to take effect.
visibilityGroupVisibilityNoNew visibility setting. UNSPECIFIED (0) means no change.

Message Expiry Validation

When update_message_expiry is true:

  • The value MUST be -1 (disabled), 0 (delete-after-fetch), or a positive integer (seconds).
  • If the server has a non-disabled retention policy (i.e., message_retention is not "-1"), the group expiry MUST NOT exceed the server retention value.

Response Body — UpdateGroupResponse

Empty message.

Status Codes

CodeCondition
200 OKGroup updated.
400 Bad RequestInvalid name/alias format, invalid expiry value, or expiry exceeds server retention.
401 UnauthorizedInvalid token, not a member, or not an admin.

SSE Events

  • GroupUpdateEvent with update_type: GROUP_UPDATE_TYPE_GROUP_SETTINGS — sent to all group members, including the sender.

Get Group Info

Returns the stored MLS GroupInfo blob for a group. Required for external commits (account reset / rejoin).

GET /api/v1/groups/{group_id}/group-info

Authentication: Required. Authorization: Group member.

Path Parameters

ParameterTypeDescription
group_idstringThe group whose GroupInfo to fetch.

Request Body

None.

Response Body — GetGroupInfoResponse

FieldTypeDescription
group_infobytesRaw MLS GroupInfo bytes.

Status Codes

CodeCondition
200 OKGroupInfo returned.
401 UnauthorizedInvalid token or not a group member.
404 Not FoundNo GroupInfo has been stored for this group.

SSE Events

None.


Get Retention Policy

Returns the server-wide retention policy and the group’s per-group expiry setting.

GET /api/v1/groups/{group_id}/retention

Authentication: Required. Authorization: Group member.

Path Parameters

ParameterTypeDescription
group_idstringThe group to query.

Request Body

None.

Response Body — GetRetentionPolicyResponse

FieldTypeDescription
server_retention_secondsint64Server-wide retention (-1=disabled, 0=delete-after-fetch, >0=seconds).
group_expiry_secondsint64Per-group expiry (-1=disabled, 0=delete-after-fetch, >0=seconds).

Status Codes

CodeCondition
200 OKSuccess.
401 UnauthorizedInvalid token or not a group member.

SSE Events

None.


List Public Groups

Lists all groups with PUBLIC visibility. Available to any authenticated user.

GET /api/v1/groups/public?pattern={pattern}

Authentication: Required.

Query Parameters

ParameterTypeRequiredDescription
patternstringNoSubstring filter on group_name. Only groups whose name contains this string are returned.

Request Body

None.

Response Body — ListPublicGroupsResponse

FieldTypeDescription
groupsrepeated PublicGroupInfoList of public groups.

Each PublicGroupInfo:

FieldTypeDescription
group_idbytesServer-assigned group ID (UUID).
group_namestringUnique group name.
aliasstringDisplay name (may be empty).
member_countuint32Number of members in the group.

Status Codes

CodeCondition
200 OKSuccess. Returns an empty list if no public groups exist.
401 UnauthorizedInvalid or expired token.

SSE Events

None.


Join Public Group

Adds the caller as a member of a public group and returns the MLS GroupInfo needed to build an external commit. The caller must not already be a member.

POST /api/v1/groups/{group_id}/join

Authentication: Required.

Path Parameters

ParameterTypeDescription
group_idstringThe public group to join.

Request Body

None (empty body).

Response Body — GetGroupInfoResponse

FieldTypeDescription
group_infobytesMLS GroupInfo message for building an external commit.

Notes

This is step 1 of a two-step join flow:

  1. POST /api/v1/groups/{group_id}/join — Server validates the group is public, adds the user as a member, and returns the MLS GroupInfo.
  2. POST /api/v1/groups/{group_id}/external-join — Client builds an MLS external commit from the GroupInfo and submits it. The external_join handler detects this is a new joiner (no prior message history) and emits a GroupUpdateEvent instead of IdentityResetEvent.

The server adds the user as a member with the member role before the external commit is submitted. This maintains the server’s authorization model while allowing the existing external_join endpoint’s membership check to pass.

Status Codes

CodeCondition
200 OKUser added as member. Returns MLS GroupInfo for external commit.
400 Bad RequestNo GroupInfo available for the group.
403 ForbiddenGroup is not public (ERROR_CODE_GROUP_NOT_PUBLIC).
404 Not FoundGroup does not exist.
409 ConflictUser is already a member or has a pending invite.

SSE Events

None (SSE events are emitted by the subsequent external_join call).


Delete Group

Permanently deletes a group and all associated data.

POST /api/v1/groups/{group_id}/delete

Authentication: Required. Authorization: Group admin.

Path Parameters

ParameterTypeDescription
group_idstringThe group to delete.

Request Body

None.

Response Body — DeleteGroupResponse

Empty message.

Status Codes

CodeCondition
200 OKGroup deleted.
401 UnauthorizedInvalid token, not a group member, or not an admin. Non-existent groups also return 401 to prevent group existence probing.

Notes

This is an irreversible operation. The server:

  1. Verifies the caller is a group admin.
  2. Collects all group members for SSE notification.
  3. Deletes the group row (DELETE FROM groups WHERE id = ?). CASCADE handles all dependent tables (group_members, messages, pending_invites, pending_welcomes, message_fetch_watermarks).
  4. Broadcasts GroupDeletedEvent to all former members (including the caller).

All members’ local MLS state for the group becomes orphaned. Clients clean up local MLS state upon receiving the GroupDeletedEvent.

SSE Events

GroupDeletedEvent is broadcast to all former group members (including the caller).

Ban Member

Bans a member from the group. Performs an MLS removal (like Remove Member) and additionally adds the user to the group’s ban list, preventing them from rejoining via public join or invite acceptance. Any pending invites for the banned user in the group are also cancelled.

POST /api/v1/groups/{group_id}/ban

Auth: Required (admin only)

Request Body: BanMemberRequest (protobuf)

Response: BanMemberResponse (protobuf)

CodeCondition
200 OKMember banned.
400 Bad RequestTarget is not a member.
401 UnauthorizedCaller is not an admin.
404 Not FoundUser not found.

SSE Events

MemberRemovedEvent is broadcast to all remaining members and the banned user.

Unban Member

Removes a user from the group’s ban list, allowing them to rejoin.

POST /api/v1/groups/{group_id}/unban

Auth: Required (admin only)

Request Body: UnbanMemberRequest (protobuf)

Response: UnbanMemberResponse (protobuf)

CodeCondition
200 OKUser unbanned.
400 Bad RequestUser is not banned.
401 UnauthorizedCaller is not an admin.
404 Not FoundUser not found.

List Banned Users

Lists all banned users for a group.

GET /api/v1/groups/{group_id}/banned

Auth: Required (admin only)

Response: ListBannedUsersResponse (protobuf)

CodeCondition
200 OKReturns ban list.
401 UnauthorizedCaller is not an admin.