Groups: monitor and reply
Your number can take part in WhatsApp groups — for example, the group you keep with each customer — and your integration follows everything by webhook: who wrote, what they quoted, who they mentioned, who joined and left. And it replies in the group itself, quoting the message and mentioning the person.
bZapper sends over the multi-device protocol: everything on this page applies to numbers connected by QR code or pairing code — which is how every number sends today. The official WhatsApp API (Cloud API) doesn't operate groups.
1. Put the number in the group
With the invite link, look at the group before joining:
curl -X POST "https://api.bzapper.com.br/groups/join/preview?instance_id=$INST" \
-H "Authorization: Bearer $BZ_KEY" -H "Content-Type: application/json" \
-d '{"code":"https://chat.whatsapp.com/AbCdEf123"}'
{ "jid": "[email protected]", "name": "Boxy Pharma — Support", "topic": "…", "size": 14,
"announce": false, "locked": false }
Then join with POST /groups/join?instance_id=… and the same { "code" } (accepts the code or
the full link). When the number joins, group.joined arrives.
2. Receive: who wrote, what they quoted
Every group message arrives as message.received with the group block and the author in
sender:
{
"event_type": "message.received",
"instance_id": "…",
"group": { "jid": "[email protected]", "name": "Boxy Pharma — Support" },
"sender": { "jid": "[email protected]", "phone": "+5511988887777",
"lid": "55443322@lid", "name": "Rita" },
"mentions": ["[email protected]"],
"payload": {
"type": "text", "body": "@5511900001111 order 4412 is late",
"wa_message_id": "3EB0…", "message_id": "…",
"mentioned_me": true,
"quoted_id": "3EB0…", "quoted_participant": "[email protected]"
}
}
| Field | What it is |
|---|---|
group.name | Group name — present already on the first message of a new group |
sender.phone | Author's phone (+CCdigits). Use it to know who the person is (e.g. whether they're on your team) |
sender.lid | WhatsApp privacy identifier. Stable per person |
sender.jid | Author JID: the phone one when we know it, otherwise the @lid |
payload.mentioned_me | true when your number was mentioned |
payload.quoted_id | wa_message_id of the quoted message (when it's a reply) |
payload.quoted_participant | Author of the quoted message — ties the reply to the right thread |
WhatsApp identifies many group participants only by @lid. bZapper swaps it for the phone
using what WhatsApp sends alongside and the map the session has already learned. If the
person is still unknown, sender.phone is empty and sender.jid is the @lid — which stays
stable and can be used for correlation until the phone shows up.
3. Reply in the group
Send to the group JID (to: "[email protected]"), always with the instance_id of the number
that is in the group.
Quoting the message (quoted_message_id = the received wa_message_id) and
mentioning the person:
{
"instance_id": "…",
"to": "[email protected]",
"body": "@5511988887777 I opened ticket #812 and will update you here.",
"quoted_message_id": "3EB0…",
"mentions": ["5511988887777"]
}
- Quote author: bZapper finds who wrote the quoted message in the history and builds the
quote with the right author. If the quoted message didn't go through bZapper, send
quoted_participant(the author's phone or JID). - Mentions:
mentionsaccepts phones ("5511…","+55 11 9…") or JIDs. For the mention to be highlighted,bodymust contain@followed by the phone digits.
Reacting — POST /messages/reaction with to = the group, quoted_message_id and emoji.
The reacted message's author is resolved the same way (or via quoted_participant).
Marking as read — POST /messages/{wa_message_id}/read:
{ "instance_id": "…", "chat": "[email protected]", "wa_message_ids": ["3EB0…"], "sender": "5511988887777" }
In a group the read receipt goes per author. Without sender, we use each message's
stored author; if none is in the history, the answer is 400 sender_required.
Typing… — POST /presence/chat with { "instance_id", "to": "[email protected]", "state": "typing" }
(and "paused" to stop).
4. Group events
| Event | When |
|---|---|
group.joined | Your number joined the group |
group.left | Your number left, was removed, or the group was deleted — payload.reason = left | removed | deleted. After it, nothing else arrives from that group |
group.participant_added / group.participant_removed | Participants joined / left (payload.participants, with phone when known) |
group.participant_promoted / group.participant_demoted | Became / stopped being admin |
group.subject_changed / group.description_changed | Name / description changed |
All carry group { jid, name } and, when present, payload.actor (who did it).
5. Participants
GET /groups/{jid}?instance_id=… returns the group with size and the participants:
{ "jid": "[email protected]", "name": "Boxy Pharma — Support", "size": 14,
"participants": [
{ "jid": "55443322@lid", "phone": "+5511988887777", "lid": "55443322@lid", "is_admin": true, "is_super_admin": false }
] }
6. Send without duplicates
If your integration retries sends on its own (timeout, queue with retry), send the
Idempotency-Key header — the retry returns the same response without sending the message
again. See Send idempotency.