A group is a multi-member chat room: a named space with a member list, role-based posting, and a persistent message history. Groups share the CommunityChatGateway WebSocket namespace with direct messaging but have their own REST endpoints and their own socket rooms.
Endpoints
/client/community/groupsJWT/client/community/groupsJWT/client/community/groups/:groupIdJWT/client/community/groups/:groupId/messagesJWT/client/community/groups/:groupId/messagesJWT/client/community/groups/:groupId/membersJWT/client/community/groups/:groupId/members/:emailJWTCreating a group
await fetch('/client/community/groups', {
method: 'POST',
headers: {
orgid: 'my-org',
Authorization: `Bearer ${customerJwt}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Logo project',
description: 'Working group for the Q2 rebrand',
type: 'private', // 'public' | 'private'
members: [
{ email: '[email protected]', role: 'member' },
{ email: '[email protected]', role: 'admin' },
],
page: 'optional-page-id', // attach to a community page
}),
});
The creator becomes an admin automatically. Members not yet in the org are still added — they'll see the group on their next sign-in.
Listing your groups
GET /client/community/groups returns { data, total } for groups the caller belongs to. Filters: ?page=<pageId>, limit, pageNum.
Sending messages
There are two ways to send into a group: REST and the WebSocket gateway. REST is the simplest path for occasional sends or non-real-time clients; WS gives you live fan-out and typing indicators.
REST
await fetch(`/client/community/groups/${groupId}/messages`, {
method: 'POST',
headers: {
orgid: 'my-org',
Authorization: `Bearer ${customerJwt}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
content: 'Approved — let’s ship.',
contentType: 'text',
attachments: [],
replyTo: undefined,
}),
});
WebSocket
import { io } from 'socket.io-client';
const socket = io('https://appengine.appmint.io/community-chat', {
transports: ['websocket'],
auth: { token: customerJwt, orgId: 'my-org' },
});
// On connect, the gateway auto-joins all groups the user belongs to.
socket.emit('sendGroupMessage', {
groupId: 'group-id-here',
content: 'Hi team',
contentType: 'text',
});
socket.on('newGroupMessage', (msg) => { /* render */ });
socket.on('groupTyping', ({ groupId, from }) => { /* show indicator */ });
Joining and leaving group rooms manually
If your client lazily joins rooms (rather than auto-joining on connect):
socket.emit('joinGroup', { groupId });
socket.emit('leaveGroup', { groupId });
Member management
// Add a member
await fetch(`/client/community/groups/${groupId}/members`, {
method: 'POST',
headers: { orgid: 'my-org', Authorization: `Bearer ${jwt}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ email: '[email protected]', role: 'member' }),
});
// Remove
await fetch(`/client/community/groups/${groupId}/members/[email protected]`, {
method: 'DELETE',
headers: { orgid: 'my-org', Authorization: `Bearer ${jwt}` },
});
Member roles are admin and member. Only admins can add/remove members or rename the group.
Reading history
GET /client/community/groups/:groupId/messages returns messages in reverse-chronological order with limit + before cursor params. Combine with the newGroupMessage socket event to keep an open chat window in sync.
const res = await fetch(
`/client/community/groups/${groupId}/messages?limit=50&before=${oldestSeenId}`,
{ headers: { orgid: 'my-org', Authorization: `Bearer ${jwt}` } }
);
Group chat vs. staff chat
The Community group chat is for member-to-member conversation. The staff ChatModule (under /chat/*) handles customer-support style inbound — it has presence, queues, AI assistant routing, and CRM inbox integration. They're distinct namespaces; do not pipe community group messages through the staff chat.
Group rooms are scoped per org — the WebSocket connection's orgId from the handshake is the boundary. A user with the same email in two orgs will see two distinct group sets, one per token.