Sending to a WhatsApp group
A group send is an ordinary POST /v1/whatsapp/messages whose to names a group instead of a person: one request, one message, and every participant of that group chat receives it and can reply where the others see it. What changes is the reporting. The message carries counters for how many of them it reached, and delivery is confirmed one participant at a time.
Groups are created in the dashboard, not over the API. WhatsApp groups covers creating one, inviting people to it, and the limits WhatsApp puts on it.
Prerequisites
You need an API key with WhatsApp write permission and the ID of an Active group (wag_…). Copy it from the group's Details tab on the Groups page, or read it from to.group_id on a message that arrived through the group.
Replace the example group ID with your own. Initialize the client for your language using the TypeScript, Python, Go, or PHP SDK guide. For CLI examples, install and authenticate the CLI with WhatsApp write access. Use the API host for your workspace region in cURL requests.
1. Send the message
Put the group ID in to and leave from out. A group is scoped to the business number it was created with, so that number is the only one the message can go out on; naming a sender returns a 422 E15018.
const msg = await bird.whatsapp.send({
to: "wag_01krdgeqcxet5s7t44vh8rt9mg",
text: { body: "The route sheet for Tuesday is up." },
});
console.log(msg.id, msg.status);msg = client.whatsapp.send(
to="wag_01krdgeqcxet5s7t44vh8rt9mg",
text={"body": "The route sheet for Tuesday is up."},
)
print(msg.id, msg.status)msg, err := client.Whatsapp.Send(context.Background(), bird.WhatsappSendParams{
To: "wag_01krdgeqcxet5s7t44vh8rt9mg",
Text: &bird.WhatsAppTextSend{Body: "The route sheet for Tuesday is up."},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)$text = (new WhatsAppMessageSendRequestText())
->setBody("The route sheet for Tuesday is up.");
$message = $bird->whatsapp->send(
to: 'wag_01krdgeqcxet5s7t44vh8rt9mg',
text: $text,
);
echo $message->getId(), ' ', $message->getStatus();bird whatsapp send \
--text 'The route sheet for Tuesday is up.' \
--to wag_01krdgeqcxet5s7t44vh8rt9mgcurl -X POST "https://us1.platform.bird.com/v1/whatsapp/messages" \
-H "Authorization: Bearer $BIRD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "wag_01krdgeqcxet5s7t44vh8rt9mg",
"text": { "body": "The route sheet for Tuesday is up." }
}'The API returns 202 with the group echoed on to.group_id, status: accepted, and recipient_count: how many people were in the group when the send was accepted. That count is the denominator for everything in step 3, and it is fixed at that moment. Someone joining through the invite link while the message is in flight does not receive it and does not change the count.
2. What a group takes
A group takes text, images, video, audio, stickers, documents, a location, contact cards, and a template your workspace authored in any category but authentication. Two kinds of content are refused with a 422 E15052, before the message is created or charged, because WhatsApp delivers neither one to a group chat:
- Anything interactive: reply buttons, list menus, link buttons, carousels, and the location and contact-info requests.
- An authentication template. Send the one-time passcode to the participant directly instead.
A Bird-managed template sends from a Bird-owned number, which is never the number a group is scoped to, so addressing one to a group returns a 422 E15001.
Free-form content still needs an open customer service window, and a group has one of its own: any participant messaging the group opens a single 24-hour window for the whole group, and that person messaging you outside the group does not open it. Once the window lapses, a template is what reaches the group.
3. Follow the fan-out
Retrieve the message to see how far it has travelled. Three counters report the fan-out:
| Field | What it reports |
|---|---|
| recipient_count | Participants at accept time, the denominator for the other two |
| delivered_count | How many WhatsApp has confirmed the message reached, including anyone who reported only a read |
| read_count | How many have opened it |
On a group message, status reports the furthest point every recipient has reached: it turns delivered only once delivered_count equals recipient_count, and stays sent while some have confirmed and others have not. No WhatsApp message has a read status, so reading is read_count and read_at. delivered_at and read_at are the first recipient's, not the last. failed and rejected are never per participant, because there is one hand-off to WhatsApp and one way for it to be refused.
A send to a group nobody had joined yet carries no counters at all, since there is no denominator to report, so treat to.group_id rather than the counters as what tells a group message from a one-to-one one.
To see which participant a confirmation is about, list the message's events. A group send fans out into at most one whatsapp.delivered and at most one whatsapp.read per participant, each carrying recipient with that person's phone number, their business-scoped user ID, or both. Neither is promised for anyone: WhatsApp skips the delivery receipt for a participant already looking at the chat, and a read arrives only if they open the message. Count what arrives rather than waiting for one of each per participant, and read the counters for the totals. The single whatsapp.sent event carries no recipient: that is the one hand-off to WhatsApp, which names nobody. The whatsapp.delivered and whatsapp.read webhooks carry the same field, which is how you tell otherwise identical callbacks apart.
4. Read one group's conversation
Pass group_id to list messages for one group's thread, in both directions:
for await (const msg of bird.whatsapp.list({ group_id: "wag_01krdgeqcxet5s7t44vh8rt9mg" })) {
console.log(msg.id, msg.direction, msg.status);
}for msg in client.whatsapp.list(group_id="wag_01krdgeqcxet5s7t44vh8rt9mg"):
print(msg.id, msg.direction, msg.status)for msg, err := range client.Whatsapp.List(context.Background(), bird.WhatsappListParams{
GroupID: "wag_01krdgeqcxet5s7t44vh8rt9mg",
}) {
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Direction, *msg.Status)
}foreach ($bird->whatsapp->list(['group_id' => 'wag_01krdgeqcxet5s7t44vh8rt9mg']) as $message) {
echo $message->getId(), ' ', $message->getDirection(), "\n";
}bird whatsapp list --group-id wag_01krdgeqcxet5s7t44vh8rt9mgcurl "https://us1.platform.bird.com/v1/whatsapp/messages?group_id=wag_01krdgeqcxet5s7t44vh8rt9mg" \
-H "Authorization: Bearer $BIRD_API_KEY"An inbound group message reads back with the participant who wrote it on from, and a to that carries both your business number and group_id: the number that received it, qualified by the group it arrived through. Neither to nor from matches a group, so group_id is the only filter that narrows the list to one group. The same messages are in the WhatsApp log in the dashboard.
Cost
A group send is charged in the two components Sending WhatsApp messages describes, with one difference in how each is priced. Bird's fee is charged once for the send and priced on the country of the business number it went out on, because a group can span several countries and has no single recipient country. Meta's share accrues per participant the message reached, each priced at the ordinary one-to-one rate for that participant's own country, so passthrough_amount grows as their receipts arrive.
Troubleshooting
- 404 (E15046): The group ID names no group this workspace holds. A group belongs to the workspace that created it, so an ID from another workspace is not found here.
- 409 (E15047): The group is pending, suspended, deleted, or failed. Only an Active group can be messaged, and a group stays pending until WhatsApp confirms it.
- 422 (E15018): Drop from. The group sends on the number it was created with.
- 422 (E15052): Interactive content, or an authentication template. See what a group takes.
- 422 (E15044): The group's service window is closed. Send a template, or wait for a participant to write to the group.
- status stuck on sent: Fewer than recipient_count participants have confirmed delivery. Read the message's events to see who is outstanding.
Next steps
- WhatsApp groups: creating a group, invite links, and what WhatsApp limits
- Receiving WhatsApp group messages: which participant wrote one, and replying to the group
- WhatsApp events: the event envelope and the delivery webhooks
- Sending WhatsApp messages: the send endpoint, its content arms, and the cost object
- Business-scoped user IDs: the identifier a participant is named by when you have no phone number
Gerelateerde bronnen
Ga verder met de documentatie, gidsen en voorbeelden voor dit onderwerp. De bronnen zijn in het Engels.
Bekijk de gidsConnecting WhatsApp to Bird: from buying a number to a live channelBegrijp het conceptWhat is the 24-hour customer service window on WhatsApp?Gebruik de toolWhatsApp message builderOntdek de mogelijkheidWhatsApp
Probeer de oefening en ontvang een implementatieoverzicht