Sync a WhatsApp Group Member List With group.updated
To keep a WhatsApp group member list current, use UnifyPort’s group.updated webhook as a signal to refresh the roster through List conversation members. Process every entry in data.changes[], but do not treat its participant IDs as a complete membership list. Publish a refreshed roster only after all pages succeed, and schedule another read if a new change arrives while the refresh is running.
Key takeaways
data.actoridentifies the person making the change;changes[].members[]identifies affected participants.- Membership and administrator roles are separate dimensions of the roster.
- A partial or failed read must not erase previously observed members.
- This is a read-side synchronization workflow, not an automatic member-management action.
Read the change without turning it into a command
WhatsApp’s group-admin documentation distinguishes appointing and dismissing administrators. Your member-list UI should preserve that distinction: a role change is not a departure.
UnifyPort’s group.updated reference supplies data.conversation, data.actor, and data.changes[]. One event can contain several changes. Route it using the receiver’s workspace context, provider, account_id, and data.conversation.id, not a group name.
| Change kind | Roster consequence | Recommended handling |
|---|---|---|
member_added / member_removed | Membership may have changed | Request a roster refresh |
promoted / demoted | Roles may have changed | Refresh roles as well as member identities |
renamed / description_changed / avatar_changed | Group presentation changed | Route to a separate group-information refresh |
dissolved | Group lifecycle changed | Flag the group for review; do not erase stored history automatically |
The handler should inspect the entire array, not just changes[0]. A rename and a member addition in one event require both relevant refreshes. Retain unrecognized or malformed changes for investigation instead of guessing their effects.
If an operator actually wants to change a role, use the separate promote/demote workflow. An observed removal should not trigger another removal request; deliberate writes belong in the member-removal workflow.
Build the roster from a completed read
The List conversation members reference returns peer_id, display_name, avatar_url, and role. Roles are member, admin, and superadmin, with the last representing the owner or creator.
This illustrative backend request uses your selected account and group identifiers from environment variables. It has not been executed here:
curl --get \
"https://api.unifyport.ai/v1/accounts/${UNIFYPORT_ACCOUNT_ID}/conversations/members" \
-H "X-Api-Key: ${UNIFYPORT_API_KEY}" \
--data-urlencode "conversation_id=${WHATSAPP_GROUP_ID}" \
--data-urlencode "type=group" \
--data-urlencode "limit=50"
If data.has_more is true, continue with data.next_cursor in the cursor query parameter. Accumulate all pages in a staging collection keyed by the returned peer_id. Keep the API key on the backend.
Do not remove someone from the visible roster because they are absent from the first page. If any page fails, retain the last completed roster and show that refresh is unresolved. A successful empty completed read and a failed request are different outcomes.
Preserve identifiers exactly. The event’s participant IDs are not necessarily interchangeable with member-list peer_id values. In particular, the WhatsApp management endpoint requires a returned member LID; do not construct one from a phone number or equate differently formatted IDs by string manipulation.
Prevent a refresh from losing its next trigger
The following is a recommended application algorithm, not additional UnifyPort fields or a server-side transaction guarantee:
- Accept events durably. Subscribe to
group.updatedwhile preserving other needed subscriptions. Verify the signature and timestamp according to the webhook delivery contract before trusting the body. Signing usessigning_secretand hex HMAC-SHA256 overX-Device-Timestamp, a dot, and the raw body. - Record refresh demand atomically. Store the authenticated ordinary event with event-ID deduplication and increment a local refresh generation for the affected group in one transaction. Then acknowledge with 2xx. A repeated delivery must not create another business action.
- Let one worker read each scoped group. Capture the current local generation, then fetch all member pages. Coalesce several pending events into one read rather than fetching once per participant.
- Check again before publication. In a transaction coordinated with intake, compare the captured generation with the current one. If it changed during pagination, retain the staged result as provisional and schedule another pass. Do not mark the roster current.
- Publish a completed observation. If every page succeeded and no newer local trigger is pending, replace the displayed roster and mark that generation processed. An event committed afterward creates new refresh work.
A persistent worker must recover unfinished generations after a crash. An in-memory flag alone can lose the request to refresh. Serialize or fence workers so a slower earlier read cannot overwrite a later completed result.
This guard detects events received by your application during the read. It does not prove that upstream membership stayed unchanged: the API documents no shared snapshot revision spanning pages and webhook events. Display a last-observed time rather than promising a perfectly current roster. A delayed event can simply trigger another read; it need not roll the roster back to an old delta.
Plan for silence and failed reads
UnifyPort is an unofficial interface. Its event-support matrix describes parser mappings, not guaranteed notification delivery for every upstream account. There is no guaranteed replay of missed events.
Initialize the roster when the group view is first used, offer an explicit refresh, and consider bounded periodic reconciliation for groups your application actively uses. Choose that schedule from operational needs; this guide does not assert a polling quota or freshness guarantee.
A failed membership lookup does not by itself prove the group dissolved or every participant left. Keep an unavailable or stale state separate from an empty roster. Pending applicants also belong elsewhere: group.join_request is not a confirmed membership addition.
FAQ
Can I rebuild the entire group from changes[].members[]?
No. Those arrays describe participants affected by particular changes, not all current members. Read and paginate the member list.
Does data.actor identify the person who joined or left?
It identifies who made the change. Read the relevant members[] array for affected participants, and use the member-list read for the roster.
Can a role update grant access inside my application?
Do not automatically equate a messaging-platform role with your application’s permissions. Keep your operator authorization policy separate, and recheck before sensitive operations.
Next step and sources
Implement the refresh worker against List conversation members. Proposed tests include a participant on a later page, a middle-page failure, duplicate events, several changes in one event, a new change during pagination, and a worker crash before publication. These are tests to run, not reported results.
References checked on 2026-11-09:
Turn messaging integration into a stable product pipeline.
Start by sending through one API, then bring every inbound message back into your business system with standard events.