Create WhatsApp groups owned by your business number, manage participants, invite links and join requests, and message the group from the inbox.
Your business number can own real WhatsApp groups: a support room with a client, a cohort for a course, a coordination group for a delivery team. Pozy creates the group, manages who is in it, and routes every group message through the same inbox endpoints you use for one-to-one chats.
Groups are not contact lists. To send the same message to many people individually, use a broadcast.
#Requirements
| Requirement | Why |
|---|---|
| A Cloud API-only number | Meta turns the Groups API off for numbers in coexistence with the WhatsApp Business app. Connect with onboarding=api (see Connect WhatsApp). |
| Official Business Account status on the number | Meta only grants group creation to numbers it has marked as an official business account. The is_official_business_account field of number info tells you. |
Meta's limits on groups:
| Limit | Value |
|---|---|
| Participants per group | 8 in total |
| Groups per number | 10,000 |
subject |
128 characters |
description |
2,048 characters |
#Create a group
Call POST /v1/whatsapp/wa-groups with a subject and, optionally, a description and a joinApprovalMode:
approval_required: people who open the invite link wait until you approve them.auto_approve: anyone with the link joins straight away.
curl -X POST https://api.pozy.ai/v1/whatsapp/wa-groups \
-H "Authorization: Bearer $POZY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "6712a9d4c0e3b58f1a7d2e64",
"subject": "Acme x Northwind launch",
"description": "Launch week coordination. Questions go here.",
"joinApprovalMode": "approval_required"
}'const res = await fetch("https://api.pozy.ai/v1/whatsapp/wa-groups", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.POZY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
accountId: "6712a9d4c0e3b58f1a7d2e64",
subject: "Acme x Northwind launch",
description: "Launch week coordination. Questions go here.",
joinApprovalMode: "approval_required",
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.group.groupId, data.group.inviteLink);import os
import requests
H = {"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"}
resp = requests.post(
"https://api.pozy.ai/v1/whatsapp/wa-groups",
headers=H,
json={
"accountId": "6712a9d4c0e3b58f1a7d2e64",
"subject": "Acme x Northwind launch",
"description": "Launch week coordination. Questions go here.",
"joinApprovalMode": "approval_required",
},
timeout=30,
)
group = resp.json()["group"]
print(group["groupId"], group["inviteLink"])The response returns 201 with the groupId and an inviteLink (https://chat.whatsapp.com/...). List the number's active groups with GET /v1/whatsapp/wa-groups, which pages with after.
#Bring people in
WhatsApp treats groups as invite-only, so the invite link is the normal way in. You can also add numbers directly.
| Task | Call |
|---|---|
| Add people | POST /v1/whatsapp/wa-groups/{groupId}/participants with phoneNumbers in E.164 (?accountId= in the query) |
| Remove people | DELETE .../participants with the same body |
| Get a fresh invite link | POST .../invite-link. The previous link stops working. |
| See who is waiting | GET .../join-requests. Each entry has the phone and a Unix timestamp. |
| Let them in | POST .../join-requests with phoneNumbers |
| Turn them away | DELETE .../join-requests with phoneNumbers |
curl -X POST "https://api.pozy.ai/v1/whatsapp/wa-groups/120363401234567890/participants?accountId=6712a9d4c0e3b58f1a7d2e64" \
-H "Authorization: Bearer $POZY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "phoneNumbers": ["+351912345678", "+447700900123"] }'Splitting a long list across several calls does not get a group past 8 participants. A 400 on add usually means the group is full.
Change the subject, description or join mode later with POST /v1/whatsapp/wa-groups/{groupId}. GET on the same path reads the group, and DELETE removes it.
#Talk in the group
A group shows up in the inbox as a conversation once the first message arrives in it. There is no call to open a group conversation from your side, so ask a participant to say hello first (or share the invite link with a prompt to introduce themselves).
When that message arrives, message.received carries the conversationId. From then on, send to the group with POST /v1/inbox/conversations/{conversationId}/messages, exactly as you would in a one-to-one chat:
curl -X POST "https://api.pozy.ai/v1/inbox/conversations/6714c1f6e2a5d70b3c9f4a86/messages" \
-H "Authorization: Bearer $POZY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "6712a9d4c0e3b58f1a7d2e64",
"message": "Welcome everyone. The launch checklist is pinned in the description."
}'const res = await fetch("https://api.pozy.ai/v1/inbox/conversations/6714c1f6e2a5d70b3c9f4a86/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.POZY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
accountId: "6712a9d4c0e3b58f1a7d2e64",
message: "Welcome everyone. The launch checklist is pinned in the description.",
}),
});
if (!res.ok) throw new Error((await res.json()).error);requests.post(
"https://api.pozy.ai/v1/inbox/conversations/6714c1f6e2a5d70b3c9f4a86/messages",
headers=H,
json={
"accountId": "6712a9d4c0e3b58f1a7d2e64",
"message": "Welcome everyone. The launch checklist is pinned in the description.",
},
timeout=30,
).raise_for_status()Every participant's messages land in that conversation, so inbox webhooks and workflows see group messages like any other.
#Common errors
| Error | Cause | Fix |
|---|---|---|
400 with type: platform_error on any group call |
The number is in coexistence, so Meta refuses group calls. Meta's own code is in platformError. |
Reconnect the number with onboarding=api |
400 when adding participants |
The group already has 8 people | Remove someone, or use a broadcast to reach more people |
| No conversation in the inbox | Nobody has written in the group yet | Wait for the first participant message |
#Endpoints
| Endpoint | What it does |
|---|---|
GET /v1/whatsapp/wa-groups |
List active groups |
POST /v1/whatsapp/wa-groups |
Create group |
GET /v1/whatsapp/wa-groups/{groupId} |
Get group info |
POST /v1/whatsapp/wa-groups/{groupId} |
Update group settings |
DELETE /v1/whatsapp/wa-groups/{groupId} |
Delete group |
POST /v1/whatsapp/wa-groups/{groupId}/participants |
Add participants |
DELETE /v1/whatsapp/wa-groups/{groupId}/participants |
Remove participants |
POST /v1/whatsapp/wa-groups/{groupId}/invite-link |
Create invite link |
GET /v1/whatsapp/wa-groups/{groupId}/join-requests |
List join requests |
POST /v1/whatsapp/wa-groups/{groupId}/join-requests |
Approve join requests |
DELETE /v1/whatsapp/wa-groups/{groupId}/join-requests |
Reject join requests |
POST /v1/inbox/conversations/{conversationId}/messages |
Send message |
#Webhook events
| Event | When it fires |
|---|---|
message.received |
Message received. |
conversation.started |
Conversation started. |
message.sent |
Message sent. |