Migrating to WhatsApp Business-Scoped User IDs
WhatsApp is rolling out usernames, so people can be reached on WhatsApp without handing out their phone number. For businesses, WhatsApp replaces the phone number with a Business-scoped user ID (BSUID) -- an identifier that is issued to your business for that person.
For most integrations this is a small change with one sharp edge: a WhatsApp contact may no longer have a phone number at all. Anything in your system that assumes "one WhatsApp contact = one phone number" needs attention.
SendSeven captures Business-scoped user IDs automatically, keeps them attached to the right contact, and picks the correct wire format when sending. What you have to change is your own identifier assumptions: read WhatsApp identifiers from contact_methods[] instead of the contact's phone, and never "clean up" a BSUID value.
What WhatsApp Is Changing
| Before | Now | |
|---|---|---|
| Sender identity on inbound | Phone number (whatsapp_id) | Phone number and a Business-scoped user ID |
| Username adopters | Did not exist | Phone number is withheld entirely -- only the BSUID arrives |
| Sending | Address the phone number | Address the phone number or the BSUID |
| Scope | A phone number is a phone number everywhere | A BSUID only works from the WhatsApp Business portfolio that issued it |
Two consequences matter:
- Every inbound WhatsApp message now carries a Business-scoped user ID for the sender, whether or not that person adopted a username. SendSeven stores it while the phone number is still visible, which is what keeps the contact reachable if they adopt a username later.
- Once someone adopts a username, their phone number stops arriving. From that point the BSUID is the only way to reach them on WhatsApp -- and there is no way to reach them on SMS, RCS, or a phone call at all until they choose to share their number.
What a BSUID Looks Like
US.13491208655302741918
- A two-letter country code, a period, then up to 128 letters and digits.
- Some businesses also see parent IDs, which carry an extra segment:
US.ENT.11815799212886844830. - It is opaque. There is no meaning to extract, and the country code is not a routing prefix.
Stripping, replacing, or "normalizing" the periods produces a value WhatsApp rejects. Store and transmit BSUIDs byte for byte as you received them. If your pipeline runs phone-number cleanup over WhatsApp identifiers, exclude BSUIDs from it.
How SendSeven Represents It
A Business-scoped user ID is a contact method with method_type: "whatsapp_bsuid".
| Property | Value |
|---|---|
method_type | whatsapp_bsuid |
value | The BSUID exactly as WhatsApp issued it, periods included |
channel_id | Required. The WhatsApp channel the ID belongs to |
display_name | The contact's public WhatsApp username, when known -- a label only |
{
"id": "cm_wab_001",
"contact_id": "contact_d4e5f6a7",
"method_type": "whatsapp_bsuid",
"value": "US.13491208655302741918",
"channel_id": "ch_123",
"display_name": "@alexrivera",
"is_primary": true,
"created_at": "2026-08-04T09:12:00Z"
}
It is a separate type, not a wider whatsapp_id
whatsapp_id is normalized as a phone-style identifier: a leading +, spaces, dashes, parentheses and periods are stripped. Running that over US.13491208655302741918 would silently store US13491208655302741918 -- a well-formed-looking value that WhatsApp refuses at send time. So BSUIDs get their own slot, and posting one into whatsapp_id returns 422 with a pointer to whatsapp_bsuid.
It is channel-scoped
A BSUID is issued by a WhatsApp Business portfolio and only works when messaging from a phone number in that portfolio. channel_id is therefore required, exactly as it is for messenger_id and instagram_id. A contact who writes to two of your WhatsApp numbers can hold two whatsapp_bsuid methods, one per channel.
It may be the contact's only identifier
For a username adopter there is no phone and no whatsapp_id method -- the BSUID is created as the contact's primary method and stands alone. That contact is complete and reachable; it is not a broken record.
The username is not addressable
A WhatsApp username (@alexrivera) cannot be used as a recipient by any API. SendSeven keeps it on the method's display_name so your agents and your UI can show it, and nothing more.
What SendSeven Handles Automatically
| Behaviour | What happens |
|---|---|
| Inbound capture | Every inbound WhatsApp message stores the sender's BSUID as a whatsapp_bsuid method on the contact, scoped to the receiving channel |
| Phone-number-less senders | When WhatsApp discloses no phone number, the BSUID becomes the contact's primary method and no phone/whatsapp_id method is invented |
| Contact matching | An inbound BSUID resolves to the existing contact rather than creating a duplicate |
| Replies | Replying in a conversation resolves the recipient from the conversation's own contact method -- BSUID contacts need no special handling |
| Wire format | SendSeven detects a BSUID by its shape and sends it in WhatsApp's Business-scoped-ID recipient field instead of the phone-number field |
| ID rotation | WhatsApp can reissue a person's BSUID. When it does, SendSeven re-points the stored method to the new value, so the contact stays reachable and its ID in your system does not change |
| Phone reveal | If a BSUID-only contact later shares their phone number, it is attached to that contact -- and if the number already belonged to a separate contact record, the two are merged and a contact.updated webhook is sent for the survivor |
| Username changes | A new public username refreshes the method's display_name |
About the phone-reveal merge
When a BSUID-only contact shares their phone number and that number already exists on a different contact, the records are the same person: they existed under a phone number before they adopted a username. SendSeven merges them, and the observable result is:
- The BSUID contact survives as the primary record and keeps its contact ID.
- The other record's ID keeps resolving:
GET /contacts/{old_id}returns the survivor with amerged_fromobject and anX-Merged-Intoresponse header. See Old UUID Resolution. - Field data is preserved by the normal merge rules -- a non-empty value beats an empty one.
- Conversations, notes, tags and subscriptions move to the survivor.
If you subscribe to contact.updated, the merge is pushed to you: the survivor's
event carries a merge object whose merged_from array lists the absorbed
contact IDs, so you can re-point your own records without polling. There is no
separate "contacts merged" event type and no contact.deleted for the absorbed
record. See Merged contacts.
If you are not subscribed, nothing breaks: old IDs keep resolving through
GET /contacts/{id} with the X-Merged-Into header.
Only a number the sender shares about themselves is linked. If someone forwards you a third party's contact card, that number is never attached to the sender.
What You Need to Change
1. Stop treating a missing phone number as bad data
A WhatsApp contact with "phone": null and no whatsapp_id is normal. Validation that rejects such records, alerts on them, or "repairs" them by copying another field will corrupt real contacts.
2. Read WhatsApp identifiers from contact_methods[]
The top-level phone and email fields are convenience denormalizations of the primary phone/email methods. They will never contain a BSUID. To find how a contact is reachable on WhatsApp, look for whatsapp_id and whatsapp_bsuid entries:
curl -X GET "https://api.sendseven.com/api/v1/contacts/contact_d4e5f6a7/methods?method_type=whatsapp_bsuid" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"
3. Fix "one WhatsApp identifier per contact" logic
A contact can hold, at the same time:
- a
whatsapp_id(their phone-shaped WhatsApp ID), and - one
whatsapp_bsuidper WhatsApp channel they have messaged.
Matching or dedupe logic that keys on a single WhatsApp value will pick an arbitrary one. Key on the whole triple instead: method_type + value + channel_id. Two whatsapp_bsuid values on different channels are not duplicates, and two identical BSUID values on different channels are the same person, not one record to collapse.
4. Handle the new method_type in webhook payloads
whatsapp_bsuid appears anywhere a contact method appears:
- in
contact.contact_methods[]on every event that carries a contact, - as the top-level
contact_methodobject on message events, identifying the exact identifier the message used.
Two things break naive consumers:
- A
method_typevalue yourswitch/matchhas never seen. Make the default branch pass the record through rather than drop or reject it. - A non-null
channel_idon a WhatsApp method. Previously only Messenger and Instagram methods carried one.
See Webhook Events.
5. Sending to a BSUID
Nothing changes for replies -- send with conversation_id and the recipient is resolved for you. When you address a recipient explicitly:
| You have | Send with |
|---|---|
| A conversation | conversation_id (recommended -- no identifier handling at all) |
| A stored contact method | contact_method_id |
| A raw BSUID | to = the BSUID plus channel_id of the WhatsApp channel it belongs to |
| A contact only | contact_id (+ channel_id) -- the phone-shaped ID is preferred, and the BSUID is used when that is all the contact has |
curl -X POST "https://api.sendseven.com/api/v1/messages" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"to": "US.13491208655302741918",
"channel_id": "ch_123",
"contact_id": "contact_d4e5f6a7",
"text": "Your order has shipped."
}'
Pass the value verbatim. SendSeven recognizes the shape, chooses the right recipient field for WhatsApp, and stores the value as a whatsapp_bsuid method on the contact scoped to channel_id -- so later sends can use contact_method_id instead.
For templates, the whatsapp_id request field accepts a BSUID and contact_method_id accepts a whatsapp_bsuid method. See Send a Template Message.
6. Do not move BSUIDs between systems
A BSUID is meaningful only to the WhatsApp Business portfolio that issued it and the channel it is bound to. It is not a phone number, not portable to another provider, and not usable to reach the same person on SMS, RCS, or a voice call. Exports include a whatsapp_bsuid column for reference only -- there is no import column for it, and copying a value onto a different channel produces an address that fails.
7. Ask for a phone number when you genuinely need one
If a workflow requires a real phone number -- shipping SMS, a voice callback, identity checks -- you have to ask. WhatsApp provides a purpose-built prompt: an interactive message of type request_contact_info, whose single button asks the recipient to share their own number. When they tap it, the number is linked (or merged) onto the contact automatically. See WhatsApp Interactive Messages.
Migration Checklist
- Nothing rejects, alerts on, or "repairs" a WhatsApp contact with no phone number.
- WhatsApp reachability is read from
contact_methods[], not fromcontact.phone. - Dedupe and matching key on
method_type+value+channel_id. - Unknown
method_typevalues pass through your webhook consumer instead of failing it. -
channel_idmay be non-null on a WhatsApp contact method. - No phone-number cleanup or normalization runs over BSUID values.
- Storage for identifier values allows at least 255 characters.
- Sends use
conversation_idorcontact_method_idwhere possible; raw BSUIDs are always paired withchannel_id. - Workflows that truly need a phone number ask for one instead of assuming it.
FAQ
Do I have to do anything for contacts who have not adopted a username?
No. They keep their phone number and their whatsapp_id method, and they additionally gain a whatsapp_bsuid method. Existing sends keep working unchanged.
Will my existing contacts get duplicated? No. An inbound BSUID resolves to the contact it belongs to and is added as an additional method.
Is a BSUID permanent? It is stable enough to be your long-term handle for that person, but WhatsApp can reissue it. SendSeven re-points the stored method when that happens, so the contact ID you hold does not change. Do not build logic that treats the BSUID string itself as an immutable key in your own database -- key on the SendSeven contact ID.
Can I look someone up by their WhatsApp username?
No. Usernames are not addressable and not searchable through any API. SendSeven shows the username on the method's display_name when WhatsApp provides it.
Can I send a template to a BSUID? Yes, with one exception: authentication templates are rejected for Business-scoped recipients, because their one-tap, zero-tap and copy-code buttons require a phone number. Deliver the code with a non-authentication template, or over another channel once you have a number.
Can I start a brand-new conversation with a BSUID I got from somewhere else? Only if the ID was issued to your own WhatsApp Business portfolio and you send from a channel in it. BSUIDs from another business, or from a different portfolio, will not deliver.
Will a BSUID contact count against the 24-hour messaging window differently? No. The window rules are unchanged -- see 24-Hour Messaging Window.
What happens to the second contact record after a phone reveal merge?
Its ID keeps resolving to the survivor with a merged_from object and an X-Merged-Into header. If you subscribe to contact.updated, you also get pushed the survivor's event with merge.merged_from naming the absorbed ID. Update your stored reference either way.
Next Steps
- Manage Contacts -- contact method types and merge behaviour
- Webhook Events -- payload shapes that now include
whatsapp_bsuid - Send a Template Message -- recipient resolution rules
- WhatsApp Interactive Messages -- asking a contact for their phone number