# 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](/docs/guides/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**](https://bird.com/dashboard/w/whatsapp/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](/docs/sdks/typescript), [Python](/docs/sdks/python), [Go](/docs/sdks/go), or [PHP](/docs/sdks/php) SDK guide. For CLI examples, [install and authenticate the CLI](/docs/cli#authenticate) with WhatsApp write access. Use the API host for your [workspace region](/docs/api/regions) 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`](/docs/api/errors/E15018).

**TypeScript**

```typescript
const msg = await bird.whatsapp.send({
  to: "wag_01krdgeqcxet5s7t44vh8rt9mg",
  text: { body: "The route sheet for Tuesday is up." },
});
console.log(msg.id, msg.status);
```

Examples: [TypeScript](/docs/guides/whatsapp/groups/sending.ts.md) · [Python](/docs/guides/whatsapp/groups/sending.py.md) · [Go](/docs/guides/whatsapp/groups/sending.go.md) · [PHP](/docs/guides/whatsapp/groups/sending.php.md) · [CLI](/docs/guides/whatsapp/groups/sending.cli.md) · [MCP](/docs/guides/whatsapp/groups/sending.mcp.md) · [cURL](/docs/guides/whatsapp/groups/sending.curl.md)

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`](/docs/api/errors/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](/docs/guides/whatsapp/templates) 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`](/docs/api/errors/E15001).

Free-form content still needs an open [customer service window](/docs/knowledge-base/whatsapp/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](/docs/api/reference/get-whatsapp-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](/docs/api/reference/list-whatsapp-message-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](/docs/guides/whatsapp/business-scoped-user-ids), 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](/docs/guides/whatsapp/events) 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](/docs/api/reference/list-whatsapp-messages) for one group's thread, in both directions:

**TypeScript**

```typescript
for await (const msg of bird.whatsapp.list({ group_id: "wag_01krdgeqcxet5s7t44vh8rt9mg" })) {
  console.log(msg.id, msg.direction, msg.status);
}
```

Examples: [TypeScript](/docs/guides/whatsapp/groups/sending.ts.md) · [Python](/docs/guides/whatsapp/groups/sending.py.md) · [Go](/docs/guides/whatsapp/groups/sending.go.md) · [PHP](/docs/guides/whatsapp/groups/sending.php.md) · [CLI](/docs/guides/whatsapp/groups/sending.cli.md) · [cURL](/docs/guides/whatsapp/groups/sending.curl.md)

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](/docs/guides/whatsapp/message-log) in the dashboard.

## Cost

A group send is charged in the two components [Sending WhatsApp messages](/docs/guides/whatsapp/sending-whatsapp#cost-and-billing) 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](/docs/api/errors/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](/docs/api/errors/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](/docs/api/errors/E15018))**: Drop `from`. The group sends on the number it was created with.
- **`422` ([E15052](/docs/api/errors/E15052))**: Interactive content, or an authentication template. See [what a group takes](#2-what-a-group-takes).
- **`422` ([E15044](/docs/api/errors/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](/docs/guides/whatsapp/groups): creating a group, invite links, and what WhatsApp limits
- [Receiving WhatsApp group messages](/docs/guides/whatsapp/groups/receiving): which participant wrote one, and replying to the group
- [WhatsApp events](/docs/guides/whatsapp/events): the event envelope and the delivery webhooks
- [Sending WhatsApp messages](/docs/guides/whatsapp/sending-whatsapp): the send endpoint, its content arms, and the cost object
- [Business-scoped user IDs](/docs/guides/whatsapp/business-scoped-user-ids): the identifier a participant is named by when you have no phone number

## Related resources

- [Connecting WhatsApp to Bird: from buying a number to a live channel](/learn/whatsapp/connecting-whatsapp-to-bird) (video)
- [What is the 24-hour customer service window on WhatsApp?](/explained/whatsapp/what-is-the-24-hour-customer-service-window) (answer)
- [WhatsApp message builder](/tools/whatsapp-message-builder) (tool)
- [WhatsApp](/products/whatsapp) (product)

[Get an implementation brief](/learn/workspace?topic=whatsapp)
