Urgente
BSUIDs e Usernames do WhatsApp: guia completo para integrações
Senha incorreta.
Tem a senha deste guia ou da comunidade? A da comunidade vale para todos os materiais.
Quero fazer parte e receber esse tipo de conteúdoBSUIDs e Usernames do WhatsApp: guia completo para integrações
BSUIDs já estão em webhooks de produção desde 31 de março de 2026. Se sua integração identifica contatos por telefone, você precisa agir. Este guia cobre tudo: campos, timeline, migração, Send API e checklist.
Última atualização: 30 de março de 2026
Timeline de rollout
recipient com BSUID. Botão REQUEST_CONTACT_INFO disponível em templates (início de maio).
O que são BSUIDs
Business-Scoped User ID — identificador único para cada par (usuário + portfólio de negócio). Gerado pela Meta, sempre presente nos webhooks a partir de 31 de março.
| Propriedade | Detalhe |
|---|---|
| Formato | {ISO 3166-1 alpha-2}.{alfanuméricos} — ex: BR.99887766554433221100 |
| Tamanho | Código do país + . + até 128 caracteres alfanuméricos |
| Escopo | Único por par portfólio de negócio + usuário (não global) |
| Persistência | Regenerado quando o usuário troca de número de telefone |
| Parent BSUID | Formato {CC}.ENT.{alfanuméricos} — para portfólios vinculados (linked portfolios) |
Atenção: BSUIDs não são globais. Se você tem múltiplos portfólios, o mesmo usuário terá BSUIDs diferentes em cada um. E se o usuário trocar de número, o BSUID muda — a Meta envia um webhook user_id_update com o antigo e o novo.
Formato: Trate o BSUID como string opaca. Armazene e envie exatamente como recebido — não trunque, não remova o código do país, não altere casing. O tamanho pode variar (até 128 chars após o ponto), então use TEXT no banco, não VARCHAR(n).
Por que BSUIDs existem
O WhatsApp vai lançar usernames mais adiante em 2026. Quando um usuário adotar um username, poderá esconder o telefone de empresas nos webhooks.
Sem BSUIDs, uma empresa que recebe mensagem de um usuário com username não teria como identificá-lo — perderia o contexto, criaria duplicatas e quebraria fluxos.
BSUIDs chegam antes dos usernames. A Meta já envia BSUIDs em todos os webhooks de produção desde 31/março, mesmo antes de usernames estarem disponíveis para usuários. Isso permite que integrações se adaptem antecipadamente.
O que muda nos webhooks
Campos novos (sempre presentes a partir de 31/março)
Mensagens recebidas
contacts[].user_id— BSUIDcontacts[].profile.username— se adotadomessages[].from_user_id— BSUID remetente
Status (sent/delivered/read)
contacts[]— novo em status webhookscontacts[].user_id— BSUIDstatuses[].recipient_user_id— BSUID destino
Obs: contacts[] é omitido em status failed. username é omitido em status sent.
Campos que podem ser omitidos
Podem desaparecer
contacts[].wa_id— telefonemessages[].from— telefone remetentestatuses[].recipient_id— telefone destino
Quando são omitidos?
- Usuário adotou username
- E não houve interação nos últimos 30 dias por número de negócio
- E usuário não está no Contact Book
Regra dos 30 dias: A visibilidade do telefone é avaliada por número de negócio. Se você mandou mensagem do número A há 15 dias, o telefone aparece nos webhooks do número A — mas NÃO nos do número B do mesmo portfólio, a menos que B também tenha interagido.
Comparação de payload: antes vs depois
{
"contacts": [{
"wa_id": "5511999887766",
"profile": { "name": "João" }
}],
"messages": [{
"from": "5511999887766",
"type": "text",
"text": { "body": "Olá" }
// Sem user_id, from_user_id ou username
}]
}
{
"contacts": [{
"user_id": "BR.99887766554433221100", // ← NOVO: BSUID
"wa_id": "5511999887766", // presente (interação < 30d)
"profile": {
"name": "João",
"username": "joao.silva" // ← NOVO: se adotou username
}
}],
"messages": [{
"from": "5511999887766", // presente
"from_user_id": "BR.99887766554433221100", // ← NOVO: BSUID
"type": "text",
"text": { "body": "Olá" }
}]
}
{
"contacts": [{
"user_id": "BR.99887766554433221100", // ← BSUID: sempre presente
// wa_id: OMITIDO (username + sem interação 30d)
"profile": {
"name": "João",
"username": "joao.silva"
}
}],
"messages": [{
// from: OMITIDO
"from_user_id": "BR.99887766554433221100", // ← único identificador
"type": "text",
"text": { "body": "Olá" }
}]
}
Send API: to vs recipient
A Meta adiciona o campo recipient para enviar mensagens por BSUID. O campo to (telefone) continua funcionando.
| Cenário | Campo | Disponível |
|---|---|---|
| Tem telefone | "to": "5511999..." | Sempre |
| Só tem BSUID | "recipient": "BR.998..." | Maio/2026 (data exata TBD) |
| Tem ambos | "to" | Phone tem precedência |
| BSUID antes de maio | — | Error 131062 |
Error 131062: BSUID recipients not supported for this message type. Retornado se você tentar enviar por BSUID antes de maio/2026. Classifique como erro de usuário — não faça retry.
Auth templates: Templates de autenticação (one-tap, zero-tap, copy code) não suportam envio por BSUID — exigem número de telefone. Isso vale mesmo após maio/2026.
Resposta da Send API
Quando você envia para telefone, a resposta inclui wa_id (telefone) mas não inclui user_id. Quando envia para BSUID, inclui user_id mas não inclui wa_id (a menos que o telefone esteja disponível via Contact Book).
function buildSendPayload(phone, bsuid) {
if (phone) {
return { to: phone }; // Sempre funciona
} else if (bsuid && isAfterMay2026()) {
return { recipient: bsuid }; // Só a partir de maio/2026
} else {
logWarning("Sem identificador válido para envio");
return null;
}
}
Webhook user_id_update
Quando um usuário troca de número de telefone, o BSUID é regenerado. A Meta envia um webhook dedicado com o BSUID anterior e o atual.
{
"field": "user_id_update",
"value": {
"contacts": [{
"profile": { "name": "João" },
"wa_id": "5511999887766", // se disponível
"user_id": "BR.NOVO_BSUID..." // BSUID atual
}],
"user_id_update": [{
"wa_id": "5511999887766",
"detail": "User id for João has been updated.",
"user_id": {
"previous": "BR.ANTIGO_BSUID...",
"current": "BR.NOVO_BSUID..."
},
"timestamp": "1711756800"
}]
}
}
Crítico: Se você não tratar user_id_update, contatos que trocam de número "desaparecem" — o BSUID antigo não encontra mais ninguém. Inscreva seu app no campo user_id_update no App Dashboard.
Além do webhook dedicado, a Meta também envia uma system message de tipo user_changed_user_id com o texto User <NAME> changed from <OLD_BSUID> to <NEW_BSUID>.
Contact Book (início de abril/2026)
| Propriedade | Detalhe |
|---|---|
| Hospedagem | Meta-hosted — sem integração necessária |
| Gatilho | Enviar ou receber mensagem/ligação após o lançamento |
| Retroatividade | NÃO retroativo — só captura interações pós-lançamento |
| Escopo | Nível de portfólio de negócio (não individual por número) |
| Desabilitar | Disponível desde 16/março/2026 em Meta Business Suite > Business settings > Business info |
| Ao desabilitar | Para de armazenar e deleta dados existentes. Reabilitar começa do zero. |
| Portfólios vinculados | Dados não são sincronizados entre portfólios — cada um é independente |
| Local storage | Modo opcional em que o Contact Book se comporta diferente com REQUEST_CONTACT_INFO (ver callout abaixo) |
REQUEST_CONTACT_INFO e Contact Book: Por padrão, quando o usuário compartilha o telefone via este botão, o telefone é adicionado automaticamente ao Contact Book. Exceção: se você habilitou local storage no Contact Book, o telefone não é adicionado automaticamente — nesse caso, envie uma mensagem para o telefone do usuário para capturá-lo.
REQUEST_CONTACT_INFO (início de maio/2026)
Botão para templates de utility e marketing que solicita o telefone do usuário. Quando o usuário toca, a Meta dispara um webhook de contatos com telefone e vCard.
{
"type": "buttons",
"buttons": [{ "type": "REQUEST_CONTACT_INFO" }]
}
// Botão não é customizável e não requer parâmetros no envio
Parent BSUIDs (portfólios vinculados)
Se a sua empresa tem portfólios vinculados (linked portfolios), a Meta pode fornecer um parent BSUID que funciona em todos os portfólios vinculados.
| Propriedade | Detalhe |
|---|---|
| Formato | {CC}.ENT.{alfanuméricos} — ex: US.ENT.11815799212886844830 |
| Webhook | contacts[].parent_user_id, messages[].from_parent_user_id |
| Uso | Qualquer número de negócio nos portfólios vinculados pode usar o parent BSUID |
| Ativação | Requer contato com o representante Meta (POC) |
Para tech providers e solution partners
Regra crítica para providers: O BSUID do cliente deve ser usado com os números de telefone do portfólio do cliente. Usar o BSUID do cliente com números do seu portfólio vai falhar.
Se você é tech provider ou solution partner, garanta que:
- Cada cliente tem seu próprio conjunto de BSUIDs (escopo por portfólio)
- Seu sistema associa BSUIDs ao portfólio correto
- O envio por BSUID usa sempre o número de telefone do portfólio do cliente
O que pode quebrar
| Risco | Severidade | Causa |
|---|---|---|
| Contatos perdidos | Alto | Sistema cria novo contato em vez de associar ao existente (sem lookup por BSUID) |
| Contatos duplicados | Alto | Mesmo usuário com dois registros: um pelo phone, outro pelo BSUID |
| Envio falha | Alto | Contato BSUID-only + envio por phone = mensagem não vai |
| UI crashes | Médio | phone.replace() ou wa.me/phone quando phone é null |
| Histórico perdido | Médio | user_id_update não tratado → contato "desaparece" ao trocar número |
| Campanhas falham | Médio | Automações que dependem de phone como destinatário |
Migração: roteiro completo
de phone_id"] --> B["2 · Schema: colunas bsuid
e username, phone nullable"] B --> C["3 · Indexes únicos
(bsuid, instance_id)"] C --> D["4 · Parser webhook:
user_id, from_user_id,
recipient_user_id, username"] D --> E["5 · Resolução dual:
phone-first, BSUID-fallback"] E --> F["6 · Backfill automático
(cruzado bidirecional)"] F --> G["7 · Handler
user_id_update"] G --> H["8 · Send API:
to vs recipient +
guard temporal"] H --> I["9 · UI null-safe
+ @username fallback"] I --> J["10 · Testar no Meta
App Dashboard"] J --> K(["Deploy + Monitorar"])
1. Auditar código
Busque todas as referências que assumem que from/wa_id são sempre números E.164. Inclua lookups por telefone, validações, links wa.me/, CRM e relatórios.
2. Adaptar banco de dados
-- 1. Colunas BSUID e username
ALTER TABLE contacts ADD COLUMN IF NOT EXISTS bsuid TEXT;
ALTER TABLE contacts ADD COLUMN IF NOT EXISTS username TEXT;
ALTER TABLE chats ADD COLUMN IF NOT EXISTS bsuid TEXT;
-- 2. phone_id aceita NULL (CRÍTICO)
ALTER TABLE contacts ALTER COLUMN phone_id DROP NOT NULL;
-- 3. Tabela de mapeamento (auditoria)
CREATE TABLE IF NOT EXISTS bsuid_phone_mappings (
id BIGSERIAL PRIMARY KEY,
bsuid TEXT NOT NULL,
phone_id TEXT,
instance_id UUID NOT NULL,
first_seen TIMESTAMPTZ DEFAULT NOW(),
last_seen TIMESTAMPTZ DEFAULT NOW()
);
-- 4. Indexes únicos
CREATE UNIQUE INDEX IF NOT EXISTS idx_contacts_bsuid_instance
ON contacts (bsuid, instance_id) WHERE bsuid IS NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS idx_chats_bsuid_instance
ON chats (bsuid, instance_id) WHERE bsuid IS NOT NULL;
3. Parsear novos campos do webhook
// Webhook de MENSAGEM (payload.messages existe)
function parseIncomingMessage(payload) {
const contact = payload.contacts[0];
const message = payload.messages[0];
// Campos NOVOS (sempre presentes a partir de 31/março)
const bsuid = contact.user_id; // ← BSUID: sempre
const username = contact.profile?.username; // ← se adotou
const fromBsuid = message.from_user_id; // ← BSUID: sempre
// Campos que PODEM ser omitidos (username + sem interação 30d)
const phone = contact.wa_id || null; // ← pode ser undefined!
const from = message.from || null; // ← pode ser undefined!
return { bsuid, username, phone, from, fromBsuid };
}
// Webhook de STATUS (payload.statuses existe — nunca junto com messages)
function parseStatus(payload) {
// contacts[] é NOVO em status webhooks, omitido em status "failed"
const contact = payload.contacts?.[0];
const status = payload.statuses[0];
const bsuid = contact?.user_id || null;
const recipientBsuid = status.recipient_user_id; // ← BSUID destino
return { bsuid, recipientBsuid };
}
4. Resolução dual-identifier
A estratégia recomendada é phone-first, BSUID-fallback:
function resolveContact(phone, bsuid, username) {
let contact = null;
// 1. Tentar por phone (backward compatible)
if (phone) {
contact = findByPhone(phone);
if (contact && bsuid && !contact.bsuid) {
contact.bsuid = bsuid; // Backfill BSUID
contact.username = username;
save(contact);
logMapping(bsuid, phone); // Auditoria
}
}
// 2. Fallback: tentar por BSUID
if (!contact && bsuid) {
contact = findByBsuid(bsuid);
if (contact && phone && !contact.phone) {
contact.phone = phone; // Backfill phone
save(contact);
logMapping(bsuid, phone);
}
}
// 3. Nenhum encontrado: criar novo
if (!contact) {
contact = createContact({ phone, bsuid, username });
if (phone && bsuid) logMapping(bsuid, phone);
}
return contact;
}
Por que phone-first? O campo to (phone) tem precedência no Send API e sempre funciona. Começando pelo phone, você mantém compatibilidade total com contatos existentes e enriquece com BSUID automaticamente.
5. Handler user_id_update
Inscreva seu app no campo user_id_update no Meta App Dashboard. Quando receber o webhook, atualize o BSUID no registro do contato:
function handleUserIdUpdate(payload) {
for (const update of payload.user_id_update) {
const oldBsuid = update.user_id.previous;
const newBsuid = update.user_id.current;
const phone = update.wa_id || null;
const contact = findByBsuid(oldBsuid);
if (contact) {
contact.bsuid = newBsuid;
save(contact);
logMapping(newBsuid, phone);
}
}
}
6. Testar no App Dashboard
Desde 16/fev/2026, o Meta App Dashboard permite enviar webhooks de teste com BSUIDs. Teste estes cenários:
- Cenário 1: Usuário SEM username → BSUID + phone presentes
- Cenário 2: Usuário COM username, phone indisponível → BSUID + username, sem phone
- Cenário 3: Usuário COM username, phone disponível → todos os campos
- Cenário 4: user_id_update → BSUID antigo → BSUID novo
Checklist de migração
| Item | Prioridade | Prazo |
|---|---|---|
Parsear contacts[].user_id e messages[].from_user_id dos webhooks | Crítica | Agora |
Adicionar colunas bsuid e username no banco | Crítica | Agora |
Tornar phone_id nullable | Crítica | Agora |
| Implementar resolução dual (phone-first, BSUID-fallback) | Crítica | Primeira semana de abril |
| Backfill automático cruzado | Crítica | Primeira semana de abril |
Handler user_id_update | Alta | Abril |
| UI null-safe (phone_id pode ser null) | Alta | Abril |
| Display @username como fallback | Alta | Abril |
Adaptar Send API (to vs recipient + guard temporal) | Alta | Antes de maio |
| Tratar error 131062 (user error, não retry) | Normal | Antes de maio |
| Configurar monitoring para BSUIDs | Normal | Abril |
| Implementar REQUEST_CONTACT_INFO em templates | Normal | Maio+ |
Referências
- Meta BSUID Documentation (atualizada 23/mar/2026)
- Meta App Dashboard — testes com BSUIDs
Fonte: Meta Developer Documentation — Business-scoped user IDs. Última atualização do documento fonte: 23 de março de 2026. Qualquer mudança descrita pela Meta está sujeita a alteração.