Manage WhatsApp groups
Create groups, read their metadata and participant lists, add or remove members, change admins, edit the subject and description, share invite links, and leave — all through one session's REST API.
- A session that is started and connected. See Sessions and Connect your first session.
- An API key with the operator role for any write (create, modify, leave, revoke). Read routes accept any valid key. See Authentication.
- A group id in WhatsApp's
@g.usformat, for example120363021234567890@g.us.
Examples use the local base URL http://localhost:2785/api, a session id of my-session, and the header X-API-Key: YOUR_API_KEY. Replace YOUR_API_KEY with a key from your dashboard. In production, swap the base URL for your domain over TLS.
How group routes work
Every group route is nested under a session, so the session id is always in the path. There are 13 in all — listing, reading, participant management, subject and description, invite links, and leaving:
GET /api/sessions/{sessionId}/groups
POST /api/sessions/{sessionId}/groups
GET /api/sessions/{sessionId}/groups/{groupId}
POST /api/sessions/{sessionId}/groups/{groupId}/participants
DELETE /api/sessions/{sessionId}/groups/{groupId}/participants
POST /api/sessions/{sessionId}/groups/{groupId}/participants/promote
POST /api/sessions/{sessionId}/groups/{groupId}/participants/demote
PUT /api/sessions/{sessionId}/groups/{groupId}/subject
PUT /api/sessions/{sessionId}/groups/{groupId}/description
GET /api/sessions/{sessionId}/groups/{groupId}/invite-code
POST /api/sessions/{sessionId}/groups/{groupId}/invite-code/revoke
POST /api/sessions/{sessionId}/groups/{groupId}/leave
Two rules apply to all of them:
- Identifiers. A group is addressed by its
@g.usjid; an individual member by their@c.usjid (the phone number in international format without+, for example628123456789@c.us). - Roles.
GETroutes need a plain API key. Writes — create, participant changes, subject, description, leave, revoke — require an operator-role key and return403otherwise.
The session must be started. Any group route on a session that is not started returns 400.
List groups
GET /api/sessions/{sessionId}/groups returns the groups the session belongs to as a raw array (no envelope). Page with the limit (1–1000, default 1000) and offset (default 0) query parameters.
curl "http://localhost:2785/api/sessions/my-session/groups?limit=50&offset=0" \
-H "X-API-Key: YOUR_API_KEY"
[
{
"id": "120363021234567890@g.us",
"name": "Project Team",
"participantsCount": 12,
"isAdmin": true,
"linkedParentJID": null
}
]
isAdmin reflects whether this session's account is an admin of the group — check it before attempting a write that requires group-admin rights on WhatsApp's side. linkedParentJID is set when the group is linked to a community, otherwise null.
Read group metadata
GET /api/sessions/{sessionId}/groups/{groupId} returns full details, including the participant list. It returns 404 if the group is not found in this session.
curl "http://localhost:2785/api/sessions/my-session/groups/120363021234567890@g.us" \
-H "X-API-Key: YOUR_API_KEY"
{
"id": "120363021234567890@g.us",
"name": "Project Team",
"description": "Internal coordination group.",
"owner": "628123456789@c.us",
"createdAt": 1718900000,
"isReadOnly": false,
"isAnnounce": false,
"linkedParentJID": null,
"participants": [
{
"id": "628123456789@c.us",
"number": "628123456789",
"name": "Alice",
"isAdmin": true,
"isSuperAdmin": true
}
]
}
Each participant carries isAdmin (group admin) and isSuperAdmin (the group owner). isAnnounce is true when only admins can post. isReadOnly is a best-effort mirror of isAnnounce — both derive from WhatsApp's announce flag, so it always equals isAnnounce.
Create a group
POST /api/sessions/{sessionId}/groups creates a group from a name and a non-empty list of participant jids. The session's own account becomes the owner. Both fields are required; an empty participants array fails validation with 400.
curl -X POST "http://localhost:2785/api/sessions/my-session/groups" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Project Team",
"participants": ["628123456789@c.us", "628987654321@c.us"]
}'
The response is the created group (status 201). Use the returned id as the groupId in every later call.
{
"id": "120363021234567890@g.us",
"name": "Project Team",
"participantsCount": 3,
"isAdmin": true,
"linkedParentJID": null
}
Manage participants
The four participant routes all take the same body — a non-empty participants array of @c.us jids — and return a fixed acknowledgement.
{ "participants": ["628123456789@c.us"] }
| Action | Method + path | Acknowledgement message |
|---|---|---|
| Add | POST .../participants | Participants added |
| Remove | DELETE .../participants | Participants removed |
| Promote to admin | POST .../participants/promote | Participants promoted to admin |
| Demote from admin | POST .../participants/demote | Participants demoted from admin |
Add participants
curl -X POST "http://localhost:2785/api/sessions/my-session/groups/120363021234567890@g.us/participants" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "participants": ["628123456789@c.us"] }'
{ "success": true, "message": "Participants added" }
Remove participants
Removal is a DELETE that carries a JSON body — set the Content-Type header so the body is parsed.
curl -X DELETE "http://localhost:2785/api/sessions/my-session/groups/120363021234567890@g.us/participants" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "participants": ["628123456789@c.us"] }'
{ "success": true, "message": "Participants removed" }
Promote and demote admins
promote grants group-admin rights; demote revokes them. The session's account must itself be a group admin for either to take effect.
curl -X POST "http://localhost:2785/api/sessions/my-session/groups/120363021234567890@g.us/participants/promote" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "participants": ["628123456789@c.us"] }'
{ "success": true, "message": "Participants promoted to admin" }
Change the subject and description
The subject is the group name. PUT .../subject requires a non-empty subject.
curl -X PUT "http://localhost:2785/api/sessions/my-session/groups/120363021234567890@g.us/subject" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "subject": "New Team Name" }'
{ "success": true, "message": "Group subject updated" }
PUT .../description requires a description field. Unlike the subject, an empty string is valid and clears the description.
curl -X PUT "http://localhost:2785/api/sessions/my-session/groups/120363021234567890@g.us/description" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "description": "Internal coordination group." }'
{ "success": true, "message": "Group description updated" }
Share and rotate invite links
GET .../invite-code returns the current invite code and the full https://chat.whatsapp.com/<code> link.
curl "http://localhost:2785/api/sessions/my-session/groups/120363021234567890@g.us/invite-code" \
-H "X-API-Key: YOUR_API_KEY"
{
"inviteCode": "AbCdEf123456",
"inviteLink": "https://chat.whatsapp.com/AbCdEf123456"
}
To invalidate the existing link and mint a fresh one, call POST .../invite-code/revoke with an empty body. The response contains the new code and link — anyone holding the old link can no longer join.
curl -X POST "http://localhost:2785/api/sessions/my-session/groups/120363021234567890@g.us/invite-code/revoke" \
-H "X-API-Key: YOUR_API_KEY"
{
"inviteCode": "XyZ987654321",
"inviteLink": "https://chat.whatsapp.com/XyZ987654321",
"message": "Invite code revoked and new one generated"
}
Leave a group
POST .../leave removes the session's own account from the group. Send an empty body.
curl -X POST "http://localhost:2785/api/sessions/my-session/groups/120363021234567890@g.us/leave" \
-H "X-API-Key: YOUR_API_KEY"
{ "success": true, "message": "Left the group" }
Do it with the SDK
The JavaScript SDK wraps every route above. Note the client baseUrl is the host root (http://localhost:2785), without the /api prefix.
import { OpenWAClient } from '@rmyndharis/openwa';
const client = new OpenWAClient({
baseUrl: 'http://localhost:2785',
apiKey: 'YOUR_API_KEY',
});
const group = await client.groups.create('my-session', {
name: 'Project Team',
participants: ['628123456789@c.us', '628987654321@c.us'],
});
await client.groups.addParticipants('my-session', group.id, ['628111222333@c.us']);
await client.groups.promoteParticipants('my-session', group.id, ['628111222333@c.us']);
await client.groups.setSubject('my-session', group.id, 'Project Team — Q3');
const { inviteLink } = await client.groups.inviteCode('my-session', group.id);
console.log(inviteLink);
See SDK usage for client setup and typed error handling.
Troubleshooting
| Status | Cause | Fix |
|---|---|---|
400 | Validation failed (empty name, empty participants, empty subject, missing description), or the session is not started. | Send a complete body; start the session first (see Sessions). |
401 | Missing or invalid X-API-Key. | Send a valid key in the X-API-Key header. |
403 | The key's role is below operator on a write route. | Use an operator-role key. See Authentication. |
404 | The group jid is not found in this session. | Confirm the @g.us id, and that this session is a member. |
An operator-role API key authorizes the request, but participant, subject, description, and invite changes still require the session's WhatsApp account to be a group admin. Check isAdmin on the group before attempting them.
Next steps
- Send a message to a group using its
@g.usjid as the recipient. - Receive group events via webhooks — participant changes, new messages, and more.
- Full group endpoint reference — every field, query parameter, and status code.