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.
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, Meta is introducing the Business-scoped User ID (BSUID) , new identifier that uniquely identifies a WhatsApp user within your business portfolio.
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. A BSUID can be used from any phone number across all WABAs in that portfolio, but not from a phone number in 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-taporcopy-codeauthentication templates.
Why "scoped"?
The same WhatsApp user will have a different BSUID for every business portfolio they interact with. Multiple WABAs under the same portfolio share the same set of BSUIDs.
MT: Sending a message to a BSUID
Place the BSUID directly in the existing to.number field. The platform distinguishes between a phone number and a BSUID by format:
- Digits only with a leading 00 and country code → treated as a phone number.
- . (e.g. NL.84827364598365) → 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"
}
]
}
}
]
}
}
{
"messages": {
"msg": [
{
"body": {
"type": "auto",
"content": "Fallback Text"
},
"to": [
{
<!-- CHANGED: now accepts a BSUID in addition to a phone number -->
"number": "00316012345678",
}
],
"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
- A BSUID received on one WABA can be reused from any phone number in any other WABA within the same portfolio. Sending from a phone number in a different portfolio will fail and the rejection will appear in your status report.
- BSUIDs cannot be used for
one_tap,zero_taporcopy_codeauthentication templates. - BSUIDs can change. When a BSUID changes, update your stored value (see BSUID changes).
- Channel fallback to non-WhatsApp channels is not possible when
to.numberis a BSUID. SetallowedChannelsto["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 or the user_id, If the end-user has adopted a username and the conditions for sharing a phone number with you are not met. |
from.whatsapp.user_id | New The Business-scoped User ID of the sender. Always present for WhatsApp MOs once the rollout completes. |
from.whatsapp.parent_user_id | New Parent BSUID of the sender. Will be omitted unless you have linked business portfolios (each typically containing one or more WABAs) and parent BSUIDs are enabled. See Parent BSUIDs. |
from.whatsapp.username | New The sender's WhatsApp username (e.g. @pablomorales). Always present but empty unless the user has adopted a username. |
When is
from.numberempty?The phone number is included while you have a recent interaction with that user (sent to or received from them in the last 30 days, per business phone number) or they are in your contact book. Outside those conditions, only the BSUID is delivered. Make sure your integration handles an empty
from.numbergracefully.
Example: phone number and BSUID both present
{
"reference": "2f2d42ac-3809-40fb-bce5-dc720e400000",
"from": {
"number": "+316012345678",
"name": "Samuel Beckett",
<!-- ADDED -->
"whatsapp": {
"user_id": "NL.84827364598365",
}
},
"to": {
"number": "+31607453450"
},
"message": {
"text": "Hello, I'd like to know your opening hours."
},
"groupings": [
"39373ce0-f4aa-4918-8ff1-3cef7f77b112",
"messagesApi",
""
],
"timeUtc": "2026-05-04T08:32:33",
"channel": "WhatsApp"
}
Example: user has adopted a username and phone number is unavailable
{
"reference": "2f2d42ac-3809-40fb-bce5-dc720e400000",
"from": {
"number": "NL.84827364598365",
"name": "Samuel Beckett",
<!-- ADDED -->
"whatsapp": {
"user_id": "NL.84827364598365",
"username": "@samuelbeckett",
"parent_user_id": "US.ENT.20351749385746821093"
}
},
"to": {
"number": "+31607453450"
},
"message": {
"text": "How long do I have to wait for Godot?",
"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"
}
The same from.whatsapp.user_id enrichment applies to all WhatsApp MO types: 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) gains three new fields alongside the existing to field. The to field continues to echo whatever identifier was used in the original MT, phone number or BSUID. The new user_id field always reflects the user's BSUID when available, regardless of how the MT was addressed.
JSON
{
"messages": {
"msg": {
"received": "2026-05-04T15:38:56",
"reference": "reference2",
"to": "0031607453450",
"recipient": {
"number": "0031612345678",
<!-- ADDED -->
"whatsapp": {
"user_id": "NL.84827364598365"
}
},
"status": {
"code": "2",
"errorCode": "",
"errorDescription": "Delivered"
},
"operator": ""
}
}
}
{
"messages": {
"msg": {
"received": "2026-05-04T15:38:56",
"reference": "reference2",
"to": "0031607453450",
"recipient": {
"number": "0031612345678",
<!-- ADDED -->
"whatsapp": {
"user_id": "NL.84827364598365",
"username": "@pablomorales"
}
},
"status": {
"code": "2",
"errorCode": "",
"errorDescription": "Delivered"
},
"operator": ""
}
}
}
{
"messages": {
"msg": {
"received": "2026-05-04T15:38:56",
"reference": "reference2",
"to": "0031607453450",
"recipient": {
"number": "0031612345678",
<!-- ADDED -->
"whatsapp": {
"user_id": "NL.84827364598365",
"parent_user_id": "US.ENT.20351749385746821093",
"username": "@pablomorales"
}
},
"status": {
"code": "2",
"errorCode": "",
"errorDescription": "Delivered"
},
"operator": ""
}
}
}
{
"messages": {
"msg": {
"received": "2026-05-04T15:38:56",
"reference": "reference2",
"to": "0031607453450",
"recipient": {
"number": "NL.84827364598365",
<!-- ADDED -->
"whatsapp": {
"user_id": "NL.84827364598365"
}
},
"status": {
"code": "2",
"errorCode": "",
"errorDescription": "Delivered"
},
"operator": ""
}
}
}
{
"messages": {
"msg": {
"operator": "",
"received": "2026-06-17T13:57:13",
"reference": "reference2",
"status": {
"code": "1",
"errorCode": "87",
"errorDescription": "Message marked as failed by CM after 24 hours without receiving final status from the operator"
},
"to": "0031607453450",
"recipient": {
"number": "",
<!-- ADDED -->
"whatsapp": null
}
}
}
}
XML
<messages>
<msg>
<received>2026-05-04T15:38:56</received>
<to>0031607453450</to>
<recipient>
<number>0031612345678</number>
<whatsapp>
<user_id>NL.84827364598365</user_id>
<parent_user_id></parent_user_id>
<username></username>
</whatsapp>
</recipient>
<reference>ad8758c4-b8c6-4803-8334-8f950ab4af00:1777901933652:768226ee:1:1:8388606</reference>
<status>
<code>2</code>
<errorCode></errorCode>
<errorDescription>Delivered</errorDescription>
</status>
<operator></operator>
</msg>
</messages>
<messages>
<msg>
<received>2026-05-04T15:38:56</received>
<to>NL.84827364598365</to>
<recipient>
<number>0031612345678</number>
<whatsapp>
<user_id>NL.84827364598365</user_id>
<parent_user_id></parent_user_id>
<username>@pablomorales</username>
</whatsapp>
</recipient>
<reference>ad8758c4-b8c6-4803-8334-8f950ab4af00:1777901933652:768226ee:1:1:8388606</reference>
<status>
<code>2</code>
<errorCode></errorCode>
<errorDescription>Delivered</errorDescription>
</status>
<operator></operator>
</msg>
</messages>
Field reference
| Field | Description |
|---|---|
recipient.to | The identifier you used in the original MT. Either a phone number or a BSUID. |
recipient.whatsapp.user_id | 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, null when the original MT was sent to a phone number. |
recipient.whatsapp.parent_user_id | New. The recipient's parent BSUID. Will be present if the end-user opt-in, otherwise will be empty. |
recipient.whatsapp.username | New. WhatsApp username of the recipient. Will be present if the end-user opt-in, otherwise will be empty. Also omitted for sentstatus. |
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.whatsapp.userId / userId | from.whatsapp.parentUserId / parentUserId | from.whatsapp.username / username |
|---|---|---|---|---|---|
| MT | phone number or BSUID (input) | n/a | n/a | n/a | n/a |
| MO | your business number | empty if not available | always 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 (never for sent) |
BSUID changes
When a user changes their WhatsApp phone number, their BSUID is regenerated. CM.com delivers this as a system event on your MO webhook, custom.message_type is set to system and the text field contains both the old and the new BSUID.
<!-- ADDED: this entire system MO event is new -->
{
"reference": "wamid.HBgLZjk3MmRhYjZjFQIAEhgKQTI3MzU0Q0Y4QTRGMzRBNjAA",
"messageContext": "",
"from": {
"number": "0031612345678",
"whatsapp": {
"user_id": "NL.84827364598365",
"parent_user_id": "",
"username": "@pablomorales",
"number": "0031612345678"
}
},
"to": {
"number": "0031607453450"
},
"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"
}
When you receive this event, parse the text field to extract the old and new BSUID, mark the old value as invalid, and update your stored BSUID.
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. - SR 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.
Business usernames: adopting and changing
Some usernames may already be reserved for you. You can request one of these, and once it's approved, it will become "active" as soon as usernames become available in your country. From that point, your business username will appear on your profile, and users will be able to find you by searching for it (exact match only).
You can use the username_suggestions endpoint to check which ones are available, then request the one you want.
curl 'https://graph.facebook.com/<API_VERSION>/<BUSINESS_PHONE_NUMBER_ID>/username_suggestions' \
-H 'Authorization: Bearer <ACCESS_TOKEN>'
To adopt a username, you need to send a POSTrequest to the Username API with your desired username.
curl -X POST 'https://graph.facebook.com/<API_VERSION>/<BUSINESS_PHONE_NUMBER_ID>/username' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <ACCESS_TOKEN>' \
-d '
{
"username": "<DESIRED_USERNAME>",
"transfer_action": "<TRANSFER_ACTION>" //Optional, by default is none.
}'
The propertytransfer_action lets you decide what should happen if the username you want is already in use on another phone number within your business portfolio. By default, the value is none and the request will simply fail (with error 147005) so you don't accidentally take a username away from another number. If you do want to move it over, set transfer_action to force_transfer and the username will be unassigned from the other phone number and given to this one instead.
A successful response returns a status field: approved means the username is approved and will become visible to WhatsApp users once usernames are generally available, while reserved means the username is reserved and approved but not yet visible, becoming visible once the feature rolls out generally.
Error Codes
| Code | Description | Cause / fix |
|---|---|---|
10 | App lacks permission | The token's system user needs Full control or Partial access (Phone numbers) on the WABA. |
33 | Invalid ID | Invalid phone number ID, deleted WABA, or missing whatsapp_business_management permission. |
100 | Invalid parameter | The username doesn't match the required format. |
147001 | Username not available | Already claimed, failed internal checks, or not available. Try a different username. |
147002 | Account not eligible | The owning business portfolio needs a higher tier. |
147003 | Facebook Page not linked | Link the phone number to the Facebook Page that already holds this username. |
147004 | Instagram account not linked | Link the phone number to the Instagram account that already holds this username. |
147005 | Username transfer required | The username is already assigned to another phone number in your portfolio. Resend the request with transfer_action: "force_transfer" to move it. |
133010 | Account not registered | Register the business phone number for API use first. |
Request phone number from users
A new REQUEST_CONTACT_INFO button type lets you request a user's phone number directly in the conversation. It can be added to utility and marketing templates, or sent as an interactive message.
When a user taps the button, their phone number is shared in the message thread and a contacts webhook is triggered containing their number. If you have the contact book feature enabled, the phone number is automatically added to your contact book.
Example request
{
"messages": {
"msg": [
{
"body": {
"type": "auto",
"content": "Fallback Text"
},
"to": [
{
"number": "NL.84827364598365"
}
],
"from": "0031607453450",
"allowedChannels": [
"WhatsApp"
],
"richContent": {
"conversation": [
{
"template": {
"whatsapp": {
"components": [],
"element_name": "request_contact_bsuid_test",
"language": {
"code": "en",
"policy": "deterministic"
}
}
}
}
]
}
}
]
}
}
{
"messages": {
"msg": [
{
"from": "0031607453450",
"to": [
{
"number": "NL.84827364598365"
}
],
"body": {
"type": "auto",
"content": "Request contact info"
},
"allowedChannels": [
"WhatsApp"
],
"richContent": {
"conversation": [
{
"interactive": {
"type": "request_contact_info",
"body": {
"text": "To continue assisting you, we'd like to save your contact details. Please tap the button below to share your phone number securely."
},
"action": {
"name": "request_contact_info"
}
}
}
]
}
}
]
}
}

Contact Request Info Message
Example response
When a user taps a REQUEST_CONTACT_INFO button or shares a contact directly in the chat, a contacts webhook is triggered.
{
"reference": "wamid.HBgLMzQ2OD14aef4EAFzNEFCMzREOAA=",
"messageContext": "",
"from": {
"number": "+31612345678",
"name": "John Doe",
"whatsapp": {
"user_id": "NL.84827364598365"
}
},
"to": {
"number": "0031607453450"
},
"message": {
"text": "",
"media": {
"mediaUri": "",
"contentType": "",
"title": ""
},
"custom": {
"contacts": [
{
"vcard": "QkVHSU46VkNBUkQKVkVSU0lPTjozLjAKTjo7SmVzczs7OwpGTjpKZzClRFTDt0eXBlPU1vYmlsZTt3YWlkPTM0Njg1MTIwNDczOiszNCA2ODUgMTIgMDQgNzMKRU5EOlZDQVJE",
"origin": "contact_request",
"addresses": null,
"birthday": null,
"emails": null,
"name": {
"formatted_name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"middle_name": null,
"suffix": null,
"prefix": null
},
"org": null,
"phones": [
{
"phone": "+31 612 34 56 78",
"type": "MOBILE",
"wa_id": "31612345678"
}
],
"urls": null
}
],
"meta_received_time": "2026-07-24T08:26:53",
"message_type": "contacts"
},
"error": ""
},
"groupings": [
"",
"",
""
],
"time": "2026-07-24 10:26:54",
"timeUtc": "2026-07-24T08:26:54",
"channel": "WhatsApp"
}
Related documentation
Updated 10 days ago