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.number field accepts a BSUID in addition to a phone number. No new endpoint, no new top-level field.
  • MO (incoming): the from object gains userId, parentUserId and username. The new fields are always present in the payload, but their value is empty when they do not apply. from.number may also be empty if the user has adopted a username and is outside the 30-day interaction window.
  • SR (status reports): new userId, parentUserId and username fields are added next to the existing to field 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-tap or copy-code authentication 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 to entry 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

FieldDescriptionRequired
to.numberEither the recipient's phone number (existing behaviour) or a Business-scoped User ID in <countryCode>.<id> formYes

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_tap or copy_code authentication 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.number is a BSUID. Set allowedChannels to ["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:

FieldDescription
from.numberPhone 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_idNew
The Business-scoped User ID of the sender. Always present for WhatsApp MOs once the rollout completes.
from.whatsapp.parent_user_idNew
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.usernameNew
The sender's WhatsApp username (e.g. @pablomorales). Always present but empty unless the user has adopted a username.

📘

When is from.number empty?

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.number gracefully.

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

FieldDescription
recipient.toThe identifier you used in the original MT. Either a phone number or a BSUID.
recipient.whatsapp.user_idNew. 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_idNew. The recipient's parent BSUID. Will be present if the end-user opt-in, otherwise will be empty.
recipient.whatsapp.usernameNew. 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 existing errorCode and errorDescription fields, 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.

Flowto / TOfrom.number / wa_idfrom.whatsapp.userId / userIdfrom.whatsapp.parentUserId / parentUserIdfrom.whatsapp.username / username
MTphone number or BSUID (input)n/an/an/an/a
MOyour business numberempty if not availablealways populatedempty unless parent BSUIDs enabledempty unless user has one
SRecho of the MT recipientn/apopulated for sent / delivered / readempty unless parent BSUIDs enabledempty 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.parentUserId in addition to from.userId.
  • SR webhooks populate parentUserId in addition to userId.
  • For MT, you may pass either the regular BSUID or the parent BSUID in to.number. We will route based on the prefix (ENT segment).

Migration guidance

  1. Tolerant parsing first. Update your MO and SR consumers so that they do not break when they encounter the new userId / parentUserId / username fields, and so that they tolerate from.number being empty.
  2. 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.
  3. Use phone numbers where you must. Continue to use the phone number for one-tap, zero-tap and copy-code authentication templates.
  4. 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.
  5. Handle BSUID changes. Process the system MO event that signals a BSUID change (see BSUID changes): parse the new BSUID from the text field, 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

CodeDescriptionCause / fix
10App lacks permissionThe token's system user needs Full control or Partial access (Phone numbers) on the WABA.
33Invalid IDInvalid phone number ID, deleted WABA, or missing whatsapp_business_management permission.
100Invalid parameterThe username doesn't match the required format.
147001Username not availableAlready claimed, failed internal checks, or not available. Try a different username.
147002Account not eligibleThe owning business portfolio needs a higher tier.
147003Facebook Page not linkedLink the phone number to the Facebook Page that already holds this username.
147004Instagram account not linkedLink the phone number to the Instagram account that already holds this username.
147005Username transfer requiredThe username is already assigned to another phone number in your portfolio. Resend the request with transfer_action: "force_transfer" to move it.
133010Account not registeredRegister 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

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


Did this page help you?