BSUID + WhatsApp username support
Released: July 1, 2026
Business-Scoped User IDs (BSUID) Added
Section titled “Business-Scoped User IDs (BSUID) ”WhatsApp introduced usernames in 2026. Users who adopt a username no longer expose their phone number in webhooks after 30 days of no direct interaction. To keep integrations working, Meta ships a per-portfolio identifier called Business-Scoped User ID (BSUID) — format {ISO country}.{alphanumeric}, e.g. US.13491208655302741918. Managed businesses with linked portfolios also get a parent BSUID carrying an ENT segment (US.ENT.11815799212886844830).
Kirimdev now accepts, stores, and exposes both identifiers end-to-end.
Sending: recipient field on POST /v1/{phone_number_id}/messages
Section titled “Sending: recipient field on POST /v1/{phone_number_id}/messages”to (E.164 phone number) stays required-or-recipient. Send to a BSUID by
omitting to and setting recipient instead:
{ "messaging_product": "whatsapp", "recipient": "US.13491208655302741918", "type": "text", "text": { "body": "Hi from BSUID." }}If both to and recipient are supplied, Meta uses to (documented precedence). The send response now includes:
to— the phone number when sent viato,nullotherwisebsuid,parent_bsuid,username— the recipient contact’s identifiers
Contacts: BSUID as first-class identity
Section titled “Contacts: BSUID as first-class identity”POST /v1/{phone_number_id}/contacts accepts either phone_number or bsuid (at least one is required). username is display-only info and NOT identity — usernames can change.
GET /v1/{phone_number_id}/contacts gains bsuid and username filters alongside phone. Every contact response now includes bsuid, parent_bsuid, and username (all nullable).
Auth OTP restriction
Section titled “Auth OTP restriction”WhatsApp Business Platform rejects OTP authentication templates (one-tap, zero-tap, copy-code) sent to a BSUID with error 131062. The public API pre-flight guard rejects the combo BEFORE calling Meta and returns:
{ "error": { "type": "invalid_request_error", "code": "bsuid_not_supported_for_template", "message": "Business-Scoped User ID recipients are not supported for authentication OTP templates. Send to a phone number (`to`) instead." }}MCP tools Changed
Section titled “MCP tools ”Three tools accept the new identifiers:
send_message—recipientalongsideto. Description explains precedence + auth OTP limitation.create_contact—phone_numberoptional,bsuidoptional, at least one required.usernamefield added.search_contacts—bsuid(exact) andusername(case-insensitive exact) filters.
Webhooks Added
Section titled “Webhooks ”Two changes for subscribers:
-
message.statusevents now includerecipient_user_idandrecipient_parent_user_idalongside the existingrecipient_id(phone) — matching Meta’s shape 1:1 so BSUID sends can be reconciled without a follow-up lookup. -
New event type
contact.identity_updatedfires when Meta rotates a contact’s BSUID (typically after the user changes their phone number). Payload:{"id": "evt_...","type": "contact.identity_updated","created_at": "2026-07-01T02:00:00Z","data": {"contact_id": "ctc_...","old_bsuid": "US.oldbsuid","new_bsuid": "US.newbsuid","old_parent_bsuid": null,"new_parent_bsuid": null,"phone_number": "628123456789"}}Subscribe to
contact.identity_updatedon any webhook subscription to keep your BSUID→user mapping fresh without polling.
SDK Changed
Section titled “SDK ”@kirimdev/sdk ships the shape via a fresh openapi.json snapshot. SendMessageParams gains recipient; CreateContactParams gains bsuid + username; ListContactsParams gains bsuid + username filters; Contact + Message types expose the same three fields. Zero migration for existing callers using to + phone_number.
// Send by BSUIDawait kirim.phoneNumbers(pnid).messages.send({ messaging_product: 'whatsapp', recipient: 'US.13491208655302741918', type: 'text', text: { body: 'Hi.' },})
// Search by BSUIDconst paginator = kirim.phoneNumbers(pnid).contacts.list({ bsuid: 'US.13491208655302741918',})Broadcasts Added
Section titled “Broadcasts ”Broadcast recipients can be BSUID-only (contacts whose phone number is unknown). The worker’s dispatcher picks the identifier per row — phone wins over BSUID on Meta precedence. Auth OTP templates on BSUID-only recipients are pre-flighted to failed with bsuid_not_supported_for_template before enqueuing.
CSV imports still expect a phone_number column; audience-by-label / audience-by-selected pick up BSUID-enriched contacts automatically.