WhatsApp BSUID and Usernames: What Developers Must Change
For as long as the WhatsApp API has existed, a customer was a phone number. Your CRM keyed contacts on it, your webhooks joined on it and your campaigns targeted it. Usernames change that. Once a customer adopts a WhatsApp username, their phone number can stop appearing in your webhooks, and the identifier you get instead is a WhatsApp BSUID.
Meta has been sending BSUIDs since April 2026, and sending to them has worked since July. Usernames are rolling out to users gradually through 2026, so the first contact without a phone number could reach your system any day. Meta's position is blunt: if you can't process those messages, there's no recourse.
This guide covers what a BSUID is, when phone numbers disappear, how to send with one and the schema changes to make now.
What a BSUID is
A business-scoped user ID identifies one WhatsApp user for one business portfolio. Per Meta's business-scoped user IDs page, as of October 2026:
- Format: the user's two-letter ISO country code, a period, then up to 128 alphanumeric characters, for example
US.13491208655302741918. Always use the whole value; stripping the prefix makes requests fail. - Where it appears: the
user_idproperty in every messages webhook, whether or not the user has a username. - Scope: any phone number in your portfolio can message a BSUID from that portfolio. A number in a different portfolio can't, even one you also control.
- Lifetime: stable until the user changes phone number, at which point a new BSUID is generated.
- Parent BSUIDs: managed businesses with several portfolios can ask Meta about enrolling for a shared ID, sent as
parent_user_idand formatted likeUS.ENT.11815799212886844830.
Meta says supporting BSUIDs is required for all partners and directly integrated businesses, and for Click-to-WhatsApp advertisers.
When the phone number disappears
Usernames are optional. If a user adopts one, it shows instead of their phone number in the app, and their number is left out of webhooks unless at least one of these holds:
- Your business number messaged or called their number within the last 30 days.
- Your business number received a message or call from their number within the last 30 days.
- They are in your contact book, Meta's hosted store described below.
The 30-day lookback is checked per business phone number. If your sales number spoke to someone last week, a webhook on your support number may still arrive without their phone. Here is what each webhook carries, from Meta's quick reference:
| Field | User without username | User with username |
|---|---|---|
wa_id (phone) | Always included | Only if a condition above is met |
user_id (BSUID) | Always included | Always included |
username | Not included | Always on incoming messages; on delivered and read statuses |
parent_user_id | If enrolled | If enrolled |
One edge case to code for: on failed status webhooks the contacts block is omitted, and recipient_user_id is left out when you sent to a phone number. Join failures on the message ID you stored at send time.
The key dates: BSUIDs in webhooks from early April 2026, business username reservations from June 29, 2026, sending to BSUIDs from July 2026 and the phone-number request button from early July 2026.
Sending to a BSUID
Put the BSUID in recipient and leave out to. If you include both, to wins and the response looks like a phone-number send. Use the current Graph API version from Meta's changelog in place of v23.0:
curl "https://graph.facebook.com/v23.0/PHONE_NUMBER_ID/messages" \
-H "Authorization: Bearer $WHATSAPP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"recipient": "US.13491208655302741918",
"type": "text",
"text": { "body": "Your table for 4 is confirmed for 8 pm." }
}'
{
"messaging_product": "whatsapp",
"contacts": [
{ "input": "US.13491208655302741918", "user_id": "US.13491208655302741918" }
],
"messages": [ { "id": "wamid.HBgL..." } ]
}
wa_id is omitted from the response when you send to a BSUID. Most message types work. The exceptions are one-tap, zero-tap and copy-code authentication templates, which still need a phone number; a BSUID send that isn't supported returns error 131062. For the Marketing Messages API, Meta recommends sending to phone numbers where you have them, so phone numbers keep coming back in webhooks. The basics of the request are in our guide to the send WhatsApp message API.
What to change in your schema
Make the BSUID the primary WhatsApp identity and the phone number an optional attribute. A Postgres sketch:
CREATE TABLE wa_contacts (
id BIGSERIAL PRIMARY KEY,
portfolio_id TEXT NOT NULL,
bsuid TEXT NOT NULL, -- variable length, never parse it
parent_bsuid TEXT,
phone_e164 TEXT, -- nullable: absent for some username users
username TEXT,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (portfolio_id, bsuid)
);
CREATE TABLE wa_bsuid_changes (
portfolio_id TEXT NOT NULL,
old_bsuid TEXT NOT NULL,
new_bsuid TEXT NOT NULL,
changed_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
Then resolve every inbound webhook in the same order: BSUID first, phone second, create last. When a user changes their number, Meta sends a system message on your normal messages subscription. Its system.type is user_changed_number if the new phone can be shared with you, or user_changed_user_id if only the BSUID change can be reported. Both carry previous_user_id so you can join the old identity to the new one. There is no separate user_id_update webhook field to subscribe to.
def upsert_contact(db, portfolio_id: str, contact: dict) -> int:
bsuid = contact["user_id"]
phone = contact.get("wa_id") # may be missing
username = contact.get("profile", {}).get("username")
row = db.find_by_bsuid(portfolio_id, bsuid)
if row is None and phone:
row = db.find_by_phone(portfolio_id, phone) # pre-username customers
if row is None:
return db.create(portfolio_id, bsuid=bsuid, phone=phone, username=username)
db.update(row.id, bsuid=bsuid, phone=phone or row.phone, username=username or row.username)
return row.id
def handle_system_message(db, portfolio_id: str, msg: dict) -> None:
s = msg["system"]
if s.get("type") in ("user_changed_number", "user_changed_user_id"):
old, new = s.get("previous_user_id"), s["user_id"]
if old:
db.rekey(portfolio_id, old_bsuid=old, new_bsuid=new, phone=s.get("wa_id"))
db.log_bsuid_change(portfolio_id, old, new)
Ripple the change outward too. Meta's FAQ calls out CRMs, triggered campaigns, measurement and billing: anything that joins on phone number needs a BSUID path. If you sync WhatsApp into a third-party CRM, see our notes on WhatsApp CRM integration. And webhook handlers should log the full identifier set on arrival; our guide to receiving WhatsApp messages via API covers the payload.
Getting the phone number back when you need it
Some flows genuinely need a phone number: delivery partners, SMS fallback, authentication. Meta offers two tools.
The share-contact button. A REQUEST_CONTACT_INFO button can go on utility and marketing templates, or be sent as an interactive message of type request_contact_info. The label is fixed ("Share Contact Info", shown in the user's language). If the user taps it, their number is shared in the thread and a contacts webhook delivers it. Design for people who decline.
The contact book. Meta hosts a portfolio-wide contact book that records a user's phone number and BSUID after any interaction with their number, and uses it to fill phone numbers into webhooks later. It only captures interactions after launch, and data stays until you disable the feature (which deletes it) or deactivate your account. You can check or delete a single entry:
curl -X DELETE "https://graph.facebook.com/v23.0/PHONE_NUMBER_ID/contact_book?messaging_product=whatsapp&bsuid=US.13491208655302741918" \
-H "Authorization: Bearer $WHATSAPP_TOKEN"
That delete belongs in your erasure process, covered in WhatsApp API data privacy.
A note on business usernames
Businesses can claim a username too, reserved since June 29, 2026 through WhatsApp Manager, Meta Business Suite or the API. It doesn't hide your business number. Usernames use English letters, digits, periods and underscores, run 3 to 35 characters and can't end in a domain like .com. Each number can change its username at most twice in a rolling 14 days. In chats, a customer's saved contact name shows first, then your verified or Official Business Account name, then the username, then the number. For the wider platform, see the complete guide to the WhatsApp API.
Where Tenashi fits
The BSUID work is a Business Platform migration: if you're on Cloud API or a provider, it's on your roadmap now. Tenashi sits outside that platform. It links the WhatsApp number your team already uses with a QR scan and gives everyone one shared inbox for DMs and groups, with contacts, custom fields, deals and follow-ups under every chat.
Its developer API addresses people by phone number in to, or by an existing chat's conversationId, which is also how you post into a WhatsApp group. For chats you already have, storing the conversationId is the safer key. The honest limits: Tenashi isn't Meta's API, has no templates, buttons or green tick, and WhatsApp can still ban numbers that spam. Plans start from $18/mo.
Read the API docs · Get started
FAQ
What is a WhatsApp BSUID?
A BSUID, or business-scoped user ID, is Meta's identifier for one WhatsApp user within one business portfolio, formatted like US.13491208655302741918. It appears as user_id in every messages webhook since April 2026, works with any number in your portfolio and is regenerated when the user changes their phone number.
Will I stop receiving phone numbers in webhooks?
Only for users who adopt a username, and even then not always. Their number is still included if that business number messaged or called them, or heard from them, within 30 days, or if they're in your Meta contact book. Users without usernames keep sending both their phone number and BSUID.
Can I send WhatsApp messages to a BSUID?
Yes, since July 2026. Put the BSUID in the recipient field and leave out to. If you include both, the phone number in to takes precedence. Most message types work, but one-tap, zero-tap and copy-code authentication templates still require a phone number, and unsupported sends return error 131062.
Does a WhatsApp BSUID ever change?
Yes. A BSUID is regenerated when the user changes their phone number. Meta reports it as a system message on your messages webhook, with type user_changed_number or user_changed_user_id, carrying both the new user_id and previous_user_id. Store a change history so old conversations, orders and consent records stay linked.
Do business usernames hide my business phone number?
No. Meta says adopting a business username won't hide your business phone number in the app; usernames are for discovery, not privacy. Customers can search for your exact username to find you. In chats, a saved contact name or your verified business name is shown before the username.
Store the BSUID, treat the phone number as optional and handle number-change messages, and usernames become a non-event. If your team works WhatsApp from one shared number, see how Tenashi's inbox works.