WhatsApp Business-scoped User IDs
Proposed CM.com Business Messaging API changes to support Business-scoped User IDs (BSUID) for outbound messages, incoming messages and status reports.
Preview: subject to change
This page describes using the upcoming Business-scoped User IDs (BSUID) feature on the CM.com Business Messaging API. The implementation has not been released yet and the field names, payload shapes and semantics on this page are subject to change until the feature is generally available. We do not recommend building production integrations against this contract yet. If you would like to be a launch partner or have feedback, please reach out to your CM.com account manager.
What's changing at a glance
- MT (sending): the existing
to.numberfield accepts a BSUID in addition to a phone number. No new endpoint, no new top-level field.- MO (incoming): the
fromobject gainsuserId,parentUserIdandusername. The new fields are always present in the payload, but their value is empty when they do not apply.from.numbermay also be empty if the user has adopted a username and is outside the 30-day interaction window.- SR (status reports): new
userId,parentUserIdandusernamefields are added next to the existingtofield in JSON and XML payloads, with the same "always present, may be empty" semantics.- No breaking changes: existing integrations that only use phone numbers keep working unchanged. The new fields are additive.
Background
WhatsApp is rolling out an optional usernames feature. When a user adopts a username, their phone number is no longer guaranteed to appear in webhooks. To keep businesses able to address those users, a new WhatsApp identifier called the Business-scoped User ID (BSUID) is being introduced upstream by Meta and exposed on the CM.com Business Messaging API. A BSUID uniquely identifies a WhatsApp user within a specific business portfolio: the umbrella account that owns your WhatsApp Business Account(s) (WABAs).
Key properties of a BSUID:
- Auto-generated and unique per business-portfolio / user pair.
- Format: an ISO 3166 alpha-2 country code, a period, and up to 128 alphanumeric characters, for example
NL.84827364598365. - Scoped to a single business portfolio. If you operate multiple WABAs under the same portfolio, a BSUID can be used from any phone number in any of those WABAs; a BSUID issued under one portfolio cannot be used from a phone number in a WABA that belongs to a different portfolio.
- Regenerated when the user changes their phone number. CM.com surfaces this as a system event on your MO webhook (see BSUID changes).
- Cannot be used for one-tap, zero-tap or copy-code authentication templates. Those still require the user's phone number.
- Cannot be combined with click-to-WhatsApp-only flows that depend on the phone number.
Why "scoped"?
A BSUID is only meaningful inside the business portfolio it was generated for. The same WhatsApp end-user will receive a different BSUID for every portfolio they interact with.
Each WhatsApp Business Account (WABA) you have configured sits inside a business portfolio, and the BSUID lives one level above the WABA, at that portfolio level. As a result, multiple WABAs that share a portfolio also share the same set of BSUIDs.
Scope of this preview documentation
This page covers the impact on the Business Messaging API for the WhatsApp channel only:
- MT: outgoing messages sent through the Business Messaging API.
- MO: incoming messages delivered to your MO webhook.
- SR: status reports delivered to your status report webhook (JSON and XML).
Other channels are unaffected. Parent BSUIDs (ENT BSUIDs, used by managed businesses that operate WABAs across multiple linked business portfolios) are described at the bottom of this page.
MT: Sending a message to a BSUID
We will re-use the existing to field to accept BSUIDs. No new top-level field is introduced and no new endpoint is needed; API users only need to put a BSUID where they used to put an MSISDN.
The platform decides which value it received based on the format:
- A value that matches our existing phone-number convention (digits only, leading
00and country code) is treated as a phone number, exactly as today. - A value that matches the BSUID format (
<ISO-3166 alpha-2>.<alphanumeric>, e.g.NL.84827364598365) is treated as a BSUID.
One identifier per recipient
CM.com will accept exactly one identifier per recipient. Each
toentry must contain either a phone number or a BSUID, not both.
Existing sends to phone numbers continue to work unchanged. To target a BSUID instead, place the BSUID value in to.number:
Example: sending to a BSUID
{
"messages": {
"msg": [{
"body": {
"type": "auto",
"content": "Fallback Text"
},
"to": [{
<!-- CHANGED: now accepts a BSUID in addition to a phone number -->
"number": "NL.84827364598365"
}],
"from": "00316098765432",
"allowedChannels": ["WhatsApp"],
"richContent": {
"conversation": [{
"text": "Hello from CM.com"
}]
}
}]
}
}
Field reference
| Field | Description | Required |
|---|---|---|
to.number | Either the recipient's phone number (existing behaviour) or a Business-scoped User ID in <countryCode>.<id> form | Yes |
Things to be aware of
- BSUIDs are portfolio-scoped, not WABA-scoped. A BSUID you received via a webhook on one of your WABAs can be re-used from any phone number in any other WABA, as long as those WABAs sit under the same business portfolio. Sending to a BSUID from a phone number in a WABA that belongs to a different portfolio will be rejected; the rejection is reported on your status report webhook with the corresponding
errorCodeanderrorDescription. - BSUIDs cannot be used for authentication templates of type
one_tap,zero_taporcopy_code. Use the phone number for those. - BSUIDs can change. After a system event (see BSUID changes), update your stored BSUID for that user to the new value.
- Only WhatsApp understands BSUIDs, so channel fallback to non-WhatsApp channels (SMS, Viber, …) is not possible when the
to.numberis a BSUID. For BSUID-targeted sends we recommend settingallowedChannelsto["WhatsApp"]to make the intent explicit.
MO: Receiving a message from a user with a BSUID
Webhook payloads on the existing MO webhook are extended with two new properties on the from object:
| Field | Description |
|---|---|
from.number | Phone number of the sender. May be empty if the end-user has adopted a username and the conditions for sharing a phone number with you are not met. |
from.name | Profile name of the user (existing field). |
from.userId | New. The Business-scoped User ID of the sender. Always present for WhatsApp MOs once the rollout completes. |
from.parentUserId | New. Parent BSUID of the sender. Always present in the payload, but empty unless you have linked business portfolios (each typically containing one or more WABAs) and parent BSUIDs are enabled. See Parent BSUIDs. |
from.username | New. WhatsApp username of the sender (e.g. @pablomorales). Always present in the payload, but empty unless the user has adopted a username. |
When is
from.numberempty?The phone number is included on the MO webhook while you have a recent interaction with that user (sent to or received from them in the last 30 days, evaluated per business phone number) or they are in your contact book. Outside those conditions, only the BSUID is delivered. Build your integration so that
from.numbermay legitimately be empty.
Example MO with BSUID and phone number
{
"reference": "2f2d42ac-3809-40fb-bce5-dc720e400000",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "Guus Beckett",
<!-- ADDED -->
"userId": "NL.84827364598365",
<!-- ADDED -->
"parentUserId": "",
<!-- ADDED -->
"username": ""
},
"to": {
"number": "0031687453450"
},
"message": {
"text": "Hello, I'd like to know your opening hours.",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"meta_received_time": "2026-05-04T08:32:30"
}
},
"groupings": [
"39373ce0-f4aa-4918-8ff1-3cef7f77b112",
"messagesApi",
""
],
"timeUtc": "2026-05-04T08:32:33",
"channel": "WhatsApp"
}
Example MO when the user opted into usernames and the phone number is unavailable
{
"reference": "2f2d42ac-3809-40fb-bce5-dc720e400000",
"messageContext": "",
"from": {
<!-- CHANGED: may be empty when the user has adopted a username and the phone-number conditions are not met -->
"number": "",
"name": "Pablo M.",
<!-- ADDED -->
"userId": "US.13491208655302741918",
<!-- ADDED -->
"parentUserId": "US.ENT.20351749385746821093",
<!-- ADDED -->
"username": "@pablomorales"
},
"to": {
"number": "0031687453450"
},
"message": {
"text": "Does it come in another colour?",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"meta_received_time": "2026-05-04T08:32:30"
}
},
"groupings": ["", "", ""],
"timeUtc": "2026-05-04T08:32:33",
"channel": "WhatsApp"
}
The same from.userId enrichment applies to every WhatsApp MO variant: text, media, location, contacts, replies, button replies, list replies, flow replies, product orders, deleted-message notifications, error notifications, request-welcome and Conversions API referrals.
SR: Status reports with BSUID
The existing status report webhook (JSON and XML) is extended with a USERID (XML) / userId (JSON) field inside MSG / msg.
The TO / to field continues to carry whatever identifier the message was sent to. If you sent the MT to a phone number, TO contains the phone number; if you sent the MT to a BSUID, TO contains the BSUID. In both cases USERID is the user's BSUID for your portfolio, whenever it is available.
JSON status report
{
"messages": {
"msg": {
<!-- ADDED -->
"userId": "NL.84827364598365",
<!-- ADDED -->
"parentUserId": "",
<!-- ADDED -->
"username": "@pablomorales",
"conversationTrackingId": "26a41f6c2d952c4a4aa2396f9ea436cd",
"operator": "",
"pricing": {
"billable": "true",
"category": "authentication",
"pricingModel": "PMP",
"pricingType": "regular"
},
"received": "2026-05-04T15:38:56",
"reference": "ad8758c4-b8c6-4803-8334-8f950ab4af00:1777901933652:768226ee:1:1:8388606",
"status": {
"code": "2",
"errorCode": "",
"errorDescription": "Delivered"
},
"to": "0031687453450"
}
}
}
XML status report
<MESSAGES>
<MSG RECEIVED="2026-05-04T15:38:56">
<!-- ADDED -->
<USERID>NL.84827364598365</USERID>
<!-- ADDED -->
<PARENTUSERID></PARENTUSERID>
<!-- ADDED -->
<USERNAME>@pablomorales</USERNAME>
<CONVERSATION_TRACKING_ID>26a41f6c2d952c4a4aa2396f9ea436cd</CONVERSATION_TRACKING_ID>
<OPERATOR></OPERATOR>
<PRICING BILLABLE="true" CATEGORY="authentication" PRICINGMODEL="PMP" PRICINGTYPE="regular"/>
<REFERENCE>ad8758c4-b8c6-4803-8334-8f950ab4af00:1777901933652:768226ee:1:1:8388606</REFERENCE>
<STATUS>
<CODE>2</CODE>
<ERRORCODE></ERRORCODE>
<ERRORDESCRIPTION>Delivered</ERRORDESCRIPTION>
</STATUS>
<TO>0031687453450</TO>
</MSG>
</MESSAGES>
Field reference
| Field | Description |
|---|---|
userId | New. The Business-scoped User ID for the recipient as known by your portfolio. Always included for accepted, delivered and read status reports, regardless of whether the original MT was sent to a phone number or to a BSUID. For failed status reports, empty when the original MT was sent to a phone number. |
to | The identifier you used in the original MT. Either a phone number or a BSUID. |
parentUserId | New. The recipient's parent BSUID. Always present in the payload, but empty unless parent BSUIDs are enabled for your portfolio. |
username | New. WhatsApp username of the recipient. Always present in the payload, but empty unless the user has adopted a username. Empty for sent status reports. |
Note
Status code semantics (
0= accepted,1= rejected,2= delivered,3= failed,4= read) are unchanged. When a message is rejected or fails because of a BSUID-related issue, the reason is surfaced in the existingerrorCodeanderrorDescriptionfields, exactly as for any other rejection.
Identifier matrix
A quick reference for which identifiers you can expect in each direction.
All BSUID-related properties are always present in MO and SR payloads; the value is empty when the property does not apply.
| Flow | to / TO | from.number / wa_id | from.userId / userId | from.parentUserId / parentUserId | from.username / username |
|---|---|---|---|---|---|
| MT | phone number or BSUID (input) | n/a | n/a | n/a | n/a |
| MO | your business number | empty if not available | populated | empty unless parent BSUIDs enabled | empty unless user has one |
| SR | echo of the MT recipient | n/a | populated for sent / delivered / read | empty unless parent BSUIDs enabled | empty unless user has one (and not for sent) |
BSUID changes
When an end-user changes their WhatsApp phone number, their BSUID is regenerated. CM.com surfaces this on the existing MO webhook as a system event, in the same shape as the existing phone-number-change event: custom.message_type is set to system and the text field describes the change, including both the old and the new BSUID.
<!-- ADDED: this entire system MO event is new -->
{
"reference": "wamid.HBgLZjk3MmRhYjZjFQIAEhgKQTI3MzU0Q0Y4QTRGMzRBNjAA",
"messageContext": "",
"from": {
"number": "0031612345678",
"name": "",
"userId": "NL.84827364598365",
"parentUserId": "",
"username": ""
},
"to": {
"number": "0031687453450"
},
"message": {
"text": "User A changed BSUID from NL.84827364598365 to NL.99999999999999",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"meta_received_time": "2026-05-06T20:12:05",
"message_type": "system"
},
"error": ""
},
"groupings": ["", "", ""],
"time": "2026-05-06 22:12:07",
"timeUtc": "2026-05-06T20:12:07",
"channel": "WhatsApp"
}
To process the event, parse the text field to extract the old and new BSUID, treat the old BSUID as no longer valid for sending, and replace any stored BSUID with the new value.
Parent BSUIDs
Managed businesses that operate WABAs across multiple linked business portfolios can opt in to parent BSUIDs. A parent BSUID has the form <countryCode>.ENT.<alphanumeric> (for example NL.ENT.11815799212886844830) and can be used by any business phone number, in any WABA, across the linked portfolios.
If parent BSUIDs are enabled for your account:
- MO webhooks populate
from.parentUserIdin addition tofrom.userId. - Status report webhooks populate
parentUserIdin addition touserId. - For MT, you may pass either the regular BSUID or the parent BSUID in
to.number. We will route based on the prefix (ENTsegment).
Migration guidance
- Tolerant parsing first. Update your MO and SR consumers so that they do not break when they encounter the new
userId/parentUserId/usernamefields, and so that they toleratefrom.numberbeing empty. - Persist the BSUID. Start storing the BSUID against your end-user records as soon as it appears in MO traffic. Treat it as the durable identifier for messaging, since phone numbers may disappear from later webhooks.
- Use phone numbers where you must. Continue to use the phone number for one-tap, zero-tap and copy-code authentication templates.
- Send to BSUIDs once available. Once BSUID-targeted sending is rolled out, switch outbound traffic for users that have adopted a username (or whose phone number is no longer being shared) to BSUID-only sends by placing the BSUID in
to.number. - Handle BSUID changes. Process the system MO event that signals a BSUID change (see BSUID changes): parse the new BSUID from the
textfield, replace stored BSUIDs and avoid sending to stale ones.
Related documentation
Updated 8 days ago