> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kapso.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Business-scoped user IDs

> What changes when WhatsApp starts sending BSUIDs and how to adapt your integration

<Tip>
  Use the copy button to paste this guide into Codex, Claude Code, Cursor, or another coding agent and ask it to adapt your integration.
</Tip>

## TL;DR

Meta is rolling out **business-scoped user IDs (BSUIDs)** as a primary identity in WhatsApp. Inbound payloads can already arrive with BSUIDs, and phone numbers can be omitted in some username-related cases. Your integration needs to:

* Store `business_scoped_user_id`, `parent_business_scoped_user_id`, and `username`
* Make `phone_number` and `wa_id` nullable
* Match users by BSUID first, phone number second
* Handle identity-change events if you consume raw Meta webhooks or keep your own identity store
* Reply with `to` when you have a phone number, or `recipient` when you only have a BSUID

## What changed

Meta is rolling out **business-scoped user IDs** for WhatsApp. They identify a user inside a business account and can appear together with a phone number or by themselves.

Kapso now exposes these additive fields where identity is already exposed today:

* `business_scoped_user_id`
* `parent_business_scoped_user_id`
* `username`

Relevant official guide:

* [Meta: business-scoped user IDs](https://developers.facebook.com/documentation/business-messaging/whatsapp/business-scoped-user-ids/)

## What each field means

* `business_scoped_user_id`: the main WhatsApp identifier for a user inside your business context. When present, treat this as the primary identity key.
* `parent_business_scoped_user_id`: a parent BSUID that Meta only sends for eligible managed businesses with linked business portfolios. Unlike `business_scoped_user_id`, it can work across the linked portfolio group. Store it when present, but treat `business_scoped_user_id` as the primary identity key inside a normal single-portfolio integration.
* `username`: the user's WhatsApp username when available. Useful for display and some reconciliation flows, but not a stable primary identifier.

## Current status

What is live now in Kapso:

* inbound payloads can include both phone-based identity and BSUID-based identity
* some webhook and API payloads can have `phone_number` or `wa_id` as `null`
* conversations, contacts, messages, and flow context can now include the new identity fields
* outbound sends can target phone numbers with `to` or BSUIDs with `recipient`

### Outbound BSUID sends

Use `recipient` for a regular BSUID or parent BSUID:

```json theme={null}
{
  "messaging_product": "whatsapp",
  "recipient": "US.13491208655302741918",
  "type": "template",
  "template": {
    "name": "order_update",
    "language": {
      "code": "en_US"
    }
  }
}
```

Use `to` for phone numbers. If both `to` and `recipient` are present, Meta uses the phone number in `to`.

When replying to an inbound message:

```js theme={null}
const destination = message.from
  ? { to: message.from }
  : { recipient: message.from_user_id };
```

Do not pass a BSUID in `to`. Use the full BSUID in `recipient`, including its country prefix and period.

With the [TypeScript SDK](/docs/whatsapp/typescript-sdk/introduction), every message builder takes `recipient` alongside `to`:

```ts theme={null}
await client.messages.sendText({
  phoneNumberId: '123',
  recipient: 'US.13491208655302741918',
  body: 'On its way.'
});
```

Notes:

* non-template messages still require an open 24-hour customer service window
* template messages can start or reopen a conversation
* sandbox numbers do not support BSUID recipients
* authentication templates cannot be sent to BSUID recipients
* BSUIDs are scoped to the business portfolio that owns the sending phone number

### Addressing contacts by BSUID

The Platform contacts API accepts a BSUID or parent BSUID wherever it accepts a contact UUID or phone number:

```bash theme={null}
curl https://api.kapso.ai/platform/v1/whatsapp/contacts/US.13491208655302741918 \
  -H "X-API-Key: $KAPSO_API_KEY"
```

This works for `GET`, `PATCH`, and `DELETE` on `/platform/v1/whatsapp/contacts/{identifier}`.

Pass the BSUID verbatim, including the country prefix and period. A parent BSUID (`US.ENT.…`) can be shared by several contacts in a project, and the same BSUID can exist under two portfolios. An ambiguous lookup returns `409 Conflict` with the candidate contact UUIDs instead of guessing; address the contact by its UUID in that case.

To list instead of fetch a single contact, filter with `business_scoped_user_id`:

```bash theme={null}
curl "https://api.kapso.ai/platform/v1/whatsapp/contacts?business_scoped_user_id=US.13491208655302741918" \
  -H "X-API-Key: $KAPSO_API_KEY"
```

### Starting workflows by BSUID

`POST /platform/v1/workflows/{id}/executions` accepts `recipient` in place of `phone_number`. See [Start and resume via API](/docs/workflows/start-and-resume-via-api).

## Payload shapes

Your parser should handle these inbound shapes:

* phone identity **and** BSUID identity together
* BSUID identity with **no phone number**
* `username` present with `phone_number` missing
* status payloads with recipient identity fields
* identity-change system messages: `user_changed_number` and `user_changed_user_id`

Kapso surfaces the new fields in these places:

| Location                      | Fields                                                                                                                     |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| Contact payloads              | `wa_id`, `business_scoped_user_id`, `parent_business_scoped_user_id`, `username`                                           |
| Conversation payloads         | `phone_number`, `business_scoped_user_id`, `parent_business_scoped_user_id`, `username`                                    |
| Message payloads (Kapso)      | `business_scoped_user_id`, `parent_business_scoped_user_id`, `username`                                                    |
| Message payloads (Meta-style) | `from_user_id`, `from_parent_user_id`, `to_user_id`, `to_parent_user_id`, `username`                                       |
| Workflow context              | `context.whatsapp_business_scoped_user_id`, `context.whatsapp_parent_business_scoped_user_id`, `context.whatsapp_username` |

### Payload assumptions

During the rollout, build for these cases:

* `phone_number`, `wa_id`, `from`, or `to` can be omitted instead of set to `null`
* `username` can change over time
* identity can transition over time through system messages like `user_changed_number` and `user_changed_user_id`

### Example payloads

Phone number and BSUID together:

```json theme={null}
{
  "conversation": {
    "phone_number": "16315551181",
    "business_scoped_user_id": "US.13491208655302741918",
    "parent_business_scoped_user_id": "US.ENT.506847293015824",
    "username": "@testusername"
  }
}
```

Kapso webhook with a BSUID-only inbound message:

```json theme={null}
{
  "message": {
    "id": "wamid.123",
    "type": "text",
    "from_user_id": "US.13491208655302741918",
    "username": "@testusername",
    "text": {
      "body": "Hello"
    }
  },
  "conversation": {
    "id": "conv_123",
    "phone_number": null,
    "business_scoped_user_id": "US.13491208655302741918",
    "parent_business_scoped_user_id": null,
    "username": "@testusername"
  }
}
```

`message.from` is omitted and `conversation.phone_number` is `null` in this case. Do not reject the webhook because the phone identity is missing.

Forwarded Meta webhooks follow the same rule: `entry[].changes[].value.messages[].from` can be absent while `from_user_id` is present.

Forwarded Meta status identity without a phone number:

```json theme={null}
{
  "contacts": [
    {
      "user_id": "US.13491208655302741918",
      "parent_user_id": "US.ENT.506847293015824",
      "profile": {
        "username": "@testusername"
      }
    }
  ],
  "statuses": [
    {
      "id": "wamid.456",
      "status": "delivered",
      "recipient_user_id": "US.13491208655302741918",
      "recipient_parent_user_id": "US.ENT.506847293015824"
    }
  ]
}
```

The forwarded Meta payload uses `user_id` in `contacts` and `recipient_user_id` in `statuses`. Phone-based fields such as `wa_id` and `recipient_id` can be omitted.

## When phone numbers are included

If a WhatsApp user enables a username, Meta omits their phone number unless at least one condition applies:

* the receiving business phone number messaged or called the user's phone number in the last 30 days
* the receiving business phone number received a message or call from the user's phone number in the last 30 days
* the user is stored in the business portfolio's Meta contact book

The 30-day checks apply per business phone number. An interaction through another number in the same portfolio does not satisfy those checks on its own.

The contact book is portfolio-scoped. Once it records the identity mapping, any business phone number in that portfolio can receive the phone number in eligible webhooks.

<Note>
  There is no universal date when Meta will stop sending phone numbers. Availability depends on username adoption and the conditions above.
</Note>

## Matching and storage

Treat WhatsApp identity as a compound shape, not just a phone number.

Recommended matching order:

1. `business_scoped_user_id` when present
2. `wa_id` or `phone_number` when present
3. keep both when you have both

Recommended storage rules:

* store `business_scoped_user_id`, `parent_business_scoped_user_id`, and `username`
* allow `wa_id` and `phone_number` to be nullable
* keep BSUID identity and phone identity on the same logical user/contact when both refer to the same person
* do not key your data model only by phone number anymore

If phone identity and BSUID identity point to different local records, merge or relink them only when the same Meta payload or an identity-change event establishes the relationship. Otherwise, keep the records separate for manual reconciliation.

## Identity-change events

If you only consume normal Kapso webhooks and read the current state from Kapso APIs, this is lower priority at the beginning because Kapso already reconciles these identity changes internally.

If you keep your own identity store, mirror WhatsApp users into your own database, or consume forwarded Meta webhooks directly, these events matter and you should handle them.

Meta announces a BSUID rotation on the regular `messages` field as a system message. `user_changed_number` is used when the new phone number can be shared, `user_changed_user_id` when only the BSUID transition can be shared. Both carry the current `user_id` (and `parent_user_id` when enabled) plus `previous_user_id` and `previous_parent_user_id`. There is no subscribable `user_id_update` field.

```json theme={null}
{
  "type": "system",
  "system": {
    "type": "user_changed_number",
    "wa_id": "12195555358",
    "user_id": "US.99225512874400319256",
    "previous_user_id": "US.13491208655302741918"
  }
}
```

Kapso reconciles the change onto the existing contact, conversation, and sandbox session, then emits [`whatsapp.contact.identity_changed`](/docs/platform/webhooks/message-events#whatsapp-contact-identity-changed). These system messages are not delivered as `whatsapp.message.received` and do not reach workflows or agents.

See:

* [Receive messages](/docs/platform/webhooks/overview)
* [Meta: business-scoped user IDs](https://developers.facebook.com/documentation/business-messaging/whatsapp/business-scoped-user-ids/)

If you consume raw Meta webhooks yourself:

* treat them as identity reconciliation events, not normal user content
* update the existing user/contact/conversation linkage instead of creating a new user blindly
* keep previous phone identity if it is still the same logical person
* use the event to move from phone-first matching to BSUID-first matching
* expect coexistence windows where old and new identifiers can both appear

## Migration checklist

* update your schema so `wa_id` and `phone_number` can be nullable where appropriate
* store `business_scoped_user_id`, `parent_business_scoped_user_id`, and `username`
* stop keying your users only by phone number
* make webhook parsers accept phone-based and BSUID-based payloads
* review validations, unique indexes, searches, and CRM mappings that require a phone number
* make your matching logic tolerate transition periods where both old and new identifiers can appear
* use `recipient` for BSUID or parent BSUID outbound sends
* use [request contact info](/docs/whatsapp/send-messages/request-contact-info) when you need the customer's phone number
* test at least these cases before rollout reaches your users:
  * phone + BSUID inbound payload
  * BSUID-only inbound payload
  * username + BSUID payload with no phone
  * status webhook with recipient identity
  * `user_changed_number` or `user_changed_user_id` identity change system message

## Timeline

<Info>
  The rollout has started. If you already consume Kapso WhatsApp payloads, adapt now.
</Info>

Phone numbers can be omitted for username adopters, but many payloads still include them during the rollout - especially after a recent interaction, or when the user is in Meta's contact book.

* **Early April 2026**: BSUIDs begin appearing in inbound webhooks
* **Early April 2026**: Meta contact book rollout begins, which affects when phone numbers can still appear after prior interactions
* **June 29, 2026**: businesses can reserve usernames through Meta tools or the Username API
* **Early July 2026**: Meta begins enabling BSUID sends and phone number request CTAs
* **2026**: broader usernames rollout continues gradually by region

## What's next

Planned follow-up areas:

* expanding examples and API references as later username and BSUID phases become generally available
* updating this guide as Meta publishes more concrete rollout dates

Until then, treat this page as the source of truth for the currently supported Kapso behavior.

## Related docs

* [Receive messages](/docs/platform/webhooks/overview)
* [Request contact info](/docs/whatsapp/send-messages/request-contact-info)
* [Business usernames](/docs/whatsapp/business-usernames)
* [Webhook event types](/docs/platform/webhooks/message-events)
* [WhatsApp data](/docs/platform/whatsapp-data)
* [Variables and context](/docs/flows/variables-and-context)

## Changelog

* **2026-08-04**: Added addressing Platform API contacts by BSUID or parent BSUID.
* **2026-07-30**: Added BSUID-only webhook examples, reply routing, phone number availability rules, and safer reconciliation guidance.
* **2026-06-30**: Added BSUID outbound sends, request contact info, and username rollout notes.
* **2026-04-20**: Clarified that phone numbers can be omitted only in some rollout cases, added identity-change event guidance, and expanded the timeline with Meta's published dates.
