# Update a WhatsApp group

`PATCH /v1/whatsapp/groups/{group_id}`

Changes what participants see at the top of the group: its subject, its
description, and its picture. Fields you omit are left as they are; send
`null` to clear the description. A `null` picture clears the one Bird
stores, but WhatsApp offers no way to take a group's photo down, so
participants keep seeing the current one until a new picture replaces it.

WhatsApp applies each field separately, so a change can be part-applied:
one field refused while the others take effect. The `202` response is the
change accepted, not applied: it echoes the group as Bird holds it, with
`last_operation` at `pending`. WhatsApp reports the outcome on the
`group_settings_update` webhook, which settles `last_operation` to `success`
or `failed` and fills its `results` with one entry per field, so a part-apply
says which field was refused and why. Re-read the group to see what took
effect.

Two `409`s guard this, and `status` is checked first: a group that is not
`active` returns `WhatsAppGroupNotActive`, whatever its `last_operation` says.
That matters for a group still being created, which is `pending` on both counts
at once, and the group-level answer is the more useful one, since no change can
land until it exists. Once the group is `active`, a change already outstanding
returns `WhatsAppGroupUpdateInProgress` while `last_operation.status` is
`pending`.

The picture names a file in your workspace's media library, and WhatsApp
takes only a square JPEG. `join_approval_mode` and the administering number
are fixed when the group is created and cannot be changed here.

## Code samples

**TypeScript**

```ts
const group = await bird.whatsapp.groups.update("wag_01krdgeqcxet5s7t44vh8rt9mg", {
  subject: "Norwood Fleet — Wednesday route",
});
console.log(group.last_operation?.status); // pending until WhatsApp reports back
```

Examples: [TypeScript](/docs/api/reference/update-whatsapp-group.ts.md) · [Python](/docs/api/reference/update-whatsapp-group.py.md) · [Go](/docs/api/reference/update-whatsapp-group.go.md) · [PHP](/docs/api/reference/update-whatsapp-group.php.md) · [CLI](/docs/api/reference/update-whatsapp-group.cli.md) · [MCP](/docs/api/reference/update-whatsapp-group.mcp.md) · [cURL](/docs/api/reference/update-whatsapp-group.curl.md)

## Example response `202`

```json
{
  "id": "wag_01krdgeqcxet5s7t44vh8rt9mg",
  "whatsapp_number_id": "wan_01krdgeqcxet5s7t44vh8rt9mg",
  "waba": "102290129340398",
  "subject": "New Purchase Inquiry",
  "description": "Jim would like to learn about new car purchase options for current year models.",
  "status": "active",
  "join_approval_mode": "auto_approve",
  "invite_link": "https://chat.whatsapp.com/JZm4S9tCkQx2LpVr7Ny8Ab",
  "participants": [
    {
      "bsuid": "BR.1566655121691972",
      "phone_number": "+16505551234",
      "username": "jim.almeida",
      "last_operation": {
        "type": "settings_update",
        "status": "pending",
        "requested_at": "2026-08-27T14:02:11Z",
        "settled_at": "2026-08-27T14:02:14Z",
        "results": [
          {
            "field": "subject",
            "applied": false,
            "error": {
              "description": "Group subject contains content that cannot be used.",
              "meta_error_code": "2388024"
            }
          }
        ],
        "last_error": {
          "description": "Group subject contains content that cannot be used.",
          "meta_error_code": "2388024"
        }
      }
    }
  ],
  "participant_count": 6,
  "pinned_messages": [
    {
      "message_id": "wam_01krdgeqcxet5s7t44vh8rt9mg",
      "pinned_until": "2026-09-01T09:14:52Z"
    }
  ],
  "profile_picture_url": "https://media.example.com/whatsapp/groups/JZm4S9tCkQx2.jpg",
  "last_operation": {
    "type": "settings_update",
    "status": "pending",
    "requested_at": "2026-08-27T14:02:11Z",
    "settled_at": "2026-08-27T14:02:14Z",
    "results": [
      {
        "field": "subject",
        "applied": false,
        "error": {
          "description": "Group subject contains content that cannot be used.",
          "meta_error_code": "2388024"
        }
      }
    ],
    "last_error": {
      "description": "Group subject contains content that cannot be used.",
      "meta_error_code": "2388024"
    }
  },
  "suspended_at": "2026-08-20T11:04:00Z",
  "created_at": "2026-05-20T09:14:52Z",
  "updated_at": "2026-05-25T16:42:01Z"
}
```

## Path parameters

- `group_id` (string): ID of the group to change.

## Request body

- `subject` (string): A new name for the group. Participants see the change in the group's chat.
- `description` (nullable string): A new description for the group. Send `null` to clear it; an empty string is a `422` rather than a second way to clear.
- `profile_picture_url` (nullable string): A new picture for the group, naming a file in your workspace's media library. WhatsApp takes a square JPEG of at least 192 by 192 pixels and up to 5 MB; anything else returns a `422`. Sending `null` clears the picture Bird stores, and an empty string is a `422` rather than a second way to clear it; WhatsApp has no operation for removing a group's photo, so the one participants see stays until another picture replaces it.

## Response body

- `id` (string, required): Unique identifier for the group. Accepted by every `/v1/whatsapp/groups/{group_id}` operation, and as `to` when sending a message to the group.
- `whatsapp_number_id` (string, required): The business number that created the group. It is the group's admin and the number every message to the group is sent from. Fixed when the group is created.
- `waba` (nullable string, required): Meta's identifier for the WhatsApp Business Account recorded when the group was created. This is a historical snapshot, not a live account directory projection. Null for a number we operate on your behalf, whose account is not yours to see.
- `subject` (string, required): The group's name, shown to participants and to anyone who opens the invite link.
- `description` (nullable string): The group's description, shown alongside the subject. Null when the group has none.
- `status` (string, required): Where the group stands. A group is messageable only while it is `active`.
- `join_approval_mode` (string, required): Whether opening the invite link joins the group outright or raises a join request to approve.
- `invite_link` (nullable string): The link that lets someone join the group, which is the only way in. A group has one link at a time. Null while the group is `pending`, since WhatsApp issues the link when it confirms the group. Rotating it through `POST /v1/whatsapp/groups/{group_id}/invite-link/rotate` replaces it, and every link the group had before then stops working.
- `participants` (array of object): Who is in the group, as of the last update WhatsApp sent, and the whole set rather than a page: WhatsApp holds a group to a handful of people, so there is never a page's worth to return. The business number that created the group is its admin and is not listed.
- `participants.bsuid` (string, required): Business-scoped user ID, Meta's identifier for this person against your business. The one identifier every participant has: WhatsApp always sends it, and it is stable for as long as they are in the group.
- `participants.phone_number` (string): Phone number in E.164 format. Absent when WhatsApp withholds it, which it does for anyone who has not shared their number with your business, so a group is normally a mix of participants with one and without.
- `participants.username` (string): The WhatsApp username this person chose. Absent when they have none, and not an identifier to address them by: it is theirs to change, so it names them in a list rather than keying anything.
- `participants.last_operation` (object): A removal asked of this participant that has not taken effect: `pending` while WhatsApp has yet to confirm it, or `failed` when WhatsApp refused. Never `success`, because a removal that succeeds takes the participant off this list: the entry disappearing is what says it worked. A `pending` removal refuses a second removal of the same person while leaving other participants free to be removed at the same time.
- `participants.last_operation.type` (string, required): What was asked.
- `participants.last_operation.status` (string, required): Where it got to. `pending` is what a client shows as in-progress, and what refuses the next change.
- `participants.last_operation.requested_at` (string, required): When Bird accepted the request.
- `participants.last_operation.settled_at` (nullable string): When WhatsApp reported the outcome. Null while `pending`.
- `participants.last_operation.results` (array of object): Per-field outcomes, on a `settings_update` that has settled. One entry per field the update carried, so a client can put a refusal next to the input it came from. Absent on every other operation type, which change one thing and report it on `status`.
- `participants.last_operation.results.field` (string, required): The setting this result reports on.
- `participants.last_operation.results.applied` (boolean, required): Whether WhatsApp applied this field. False when it refused this one, whatever it did with the others.
- `participants.last_operation.results.error` (object): Why WhatsApp refused this field. Present only when `applied` is false.
- `participants.last_operation.results.error.description` (string, required): WhatsApp's own explanation of the refusal, passed through. Show it to the person who asked for the change; never match on its text. Carries Bird's own words instead when the failure was Bird's verdict, such as a confirmation that never arrived.
- `participants.last_operation.results.error.meta_error_code` (nullable string): WhatsApp's most specific code for the refusal: its error subcode where it sent one, otherwise its top-level code. Treat it as an opaque string. Null when the failure was Bird's own verdict rather than a WhatsApp refusal.
- `participants.last_operation.last_error` (object): Why the operation failed as a whole. Present when `status` is `failed`, including when the confirmation never arrived and Bird gave up waiting. A `settings_update` that failed on some fields and not others carries the per-field detail in `results`.
- `participants.last_operation.last_error.description` (string, required): WhatsApp's own explanation of the refusal, passed through. Show it to the person who asked for the change; never match on its text. Carries Bird's own words instead when the failure was Bird's verdict, such as a confirmation that never arrived.
- `participants.last_operation.last_error.meta_error_code` (nullable string): WhatsApp's most specific code for the refusal: its error subcode where it sent one, otherwise its top-level code. Treat it as an opaque string. Null when the failure was Bird's own verdict rather than a WhatsApp refusal.
- `participant_count` (integer, required): How many people are in the group, excluding your business.
- `pinned_messages` (array of object): The group's pins, newest first. WhatsApp holds a few at once, and pinning past that unpins the oldest rather than refusing. No entry here is merely requested. An entry stays listed until it is unpinned, so one whose `pinned_until` has passed is still listed after WhatsApp has taken it off the chat.
- `pinned_messages.message_id` (string, required): The pinned message, as returned in the send response's `id`.
- `pinned_messages.pinned_until` (string, required): When the pin is due to lapse, projected from the `duration_days` the pin was asked for. An entry stays listed until it is unpinned, so a time in the past means WhatsApp has already taken the message off the chat.
- `profile_picture_url` (nullable string): Address of the group's picture, as WhatsApp serves it. Null when the group has none.
- `last_operation` (object): The last create, settings change or delete asked of the group. `pending` while WhatsApp has yet to confirm it, which is what a client shows as in-progress and what refuses the next change to the group. A settings change carries per-field `results`, since WhatsApp can refuse one field and apply the others. A removal reports on the participant's own entry rather than here, so several can be in flight at once. Pinning is synchronous and reports nothing.
- `last_operation.type` (string, required): What was asked.
- `last_operation.status` (string, required): Where it got to. `pending` is what a client shows as in-progress, and what refuses the next change.
- `last_operation.requested_at` (string, required): When Bird accepted the request.
- `last_operation.settled_at` (nullable string): When WhatsApp reported the outcome. Null while `pending`.
- `last_operation.results` (array of object): Per-field outcomes, on a `settings_update` that has settled. One entry per field the update carried, so a client can put a refusal next to the input it came from. Absent on every other operation type, which change one thing and report it on `status`.
- `last_operation.results.field` (string, required): The setting this result reports on.
- `last_operation.results.applied` (boolean, required): Whether WhatsApp applied this field. False when it refused this one, whatever it did with the others.
- `last_operation.results.error` (object): Why WhatsApp refused this field. Present only when `applied` is false.
- `last_operation.results.error.description` (string, required): WhatsApp's own explanation of the refusal, passed through. Show it to the person who asked for the change; never match on its text. Carries Bird's own words instead when the failure was Bird's verdict, such as a confirmation that never arrived.
- `last_operation.results.error.meta_error_code` (nullable string): WhatsApp's most specific code for the refusal: its error subcode where it sent one, otherwise its top-level code. Treat it as an opaque string. Null when the failure was Bird's own verdict rather than a WhatsApp refusal.
- `last_operation.last_error` (object): Why the operation failed as a whole. Present when `status` is `failed`, including when the confirmation never arrived and Bird gave up waiting. A `settings_update` that failed on some fields and not others carries the per-field detail in `results`.
- `last_operation.last_error.description` (string, required): WhatsApp's own explanation of the refusal, passed through. Show it to the person who asked for the change; never match on its text. Carries Bird's own words instead when the failure was Bird's verdict, such as a confirmation that never arrived.
- `last_operation.last_error.meta_error_code` (nullable string): WhatsApp's most specific code for the refusal: its error subcode where it sent one, otherwise its top-level code. Treat it as an opaque string. Null when the failure was Bird's own verdict rather than a WhatsApp refusal.
- `suspended_at` (string): When WhatsApp suspended the group. Present only while the group is `suspended`, and gone once WhatsApp lifts the suspension.
- `created_at` (string, required)
- `updated_at` (string, required)

## Related resources

- [Should I use a Bird SDK or call the API directly?](/explained/platform/should-i-use-an-sdk-or-call-the-api-directly) (answer)
- [Build your first integration](/learn/paths/integration) (course)
- [Send your first email](/docs/get-started/send-your-first-email) (docs)

[Get an implementation brief](/learn/workspace?topic=api-basics)
