Número banido no WhatsApp: como migrar usuários para um novo
O WhatsApp baniu o número do seu agente: os chats não se movem e o novo número não pode escrever primeiro. Um passo a passo com código de webhook para migrar usuários.

O essencial
- Quando o WhatsApp bane um número da whasp, você recebe um substituto sem custo, com o mesmo label, perfil e período pago, anunciado por um evento de webhook number.replaced, mas nenhum chat se move.
- O novo número não pode escrever primeiro para ninguém (409 chat_not_open), então todo usuário precisa escrever para ele, e o convite deve viajar por um canal que é seu, como e-mail, push ou um banner no app.
- Envie a cada usuário um link montado com new_number.wa_link e uma mensagem pré-preenchida com um código de uso único, e convide em lotes de cerca de 25 por dia, porque um número aceita 40 novos chats a cada 24 horas.
- Associe o histórico ao seu próprio id de usuário, e não ao número, para reconhecer quem volta pelo telefone ou pelo código e continuar a conversa de onde ela parou.
- Prepare-se antes do primeiro banimento: um segundo canal coletado no cadastro, um link de redirecionamento estável, esse link no recado do perfil e um handler de number.replaced já testado.
Quando o WhatsApp bane um número, os chats não se movem: o substituto começa vazio e não pode mandar mensagem primeiro para ninguém. Para migrar os seus usuários, avise por um canal que é seu, envie a cada um um link wa.me para o novo número com uma mensagem pré-preenchida e reconheça-os quando escreverem, para restaurar o histórico a partir do seu próprio armazenamento.
O que acontece quando o WhatsApp bane o número do seu agente?
Quando o WhatsApp bane um número da whasp em definitivo, a whasp o aposenta e passa você para um número pronto, sem custo. O substituto tem o mesmo label, o mesmo perfil (nome, recado, foto) e o mesmo período pago. Nada mais é transferido. Nenhum chat se move, e o novo número não tem nada do histórico antigo.
Seu webhook recebe um evento number.replaced com o número antigo e o objeto do novo número, incluindo o wa_link. Aqui está o evento, resumido:
{
"id": "evt_9dK3mQ2vLx",
"type": "number.replaced",
"created_at": "2026-10-18T19:00:00Z",
"data": {
"old_number": "+15555550100",
"new_number": {
"number": "+15555550102",
"wa_link": "https://wa.me/15555550102",
"label": "ava",
"status": "active",
"profile": { "name": "Ava", "about": "Sua amiga de IA", "synced": true },
"paid_until": "2026-11-17T19:00:00Z"
}
}
}
Um evento number.status_changed também pode chegar, quando a saúde do número antigo passa a critical. A ordem dos eventos não é garantida, então não dependa dela. O número banido não responde mais a ninguém. Para entender por que isso aconteceu, leia por que o WhatsApp bane números. Para a primeira hora depois de um banimento, incluindo a página do próprio WhatsApp sobre banimento de contas, veja o que fazer quando um número é banido.
O novo número pode mandar mensagem primeiro para os seus usuários?
O novo número não pode mandar mensagem para quem não escreveu para ele. Um envio para essa pessoa retorna 409 chat_not_open. Isso vale para todo número da whasp, inclusive os substitutos, e é de propósito: mensagens frias são o que bane números pessoais, e um número novo que abrisse centenas de chats no primeiro dia estaria convidando o próximo banimento.
Então a migração precisa começar pelo usuário. Ele precisa tocar em um link e escrever. Isso faz do convite a parte difícil, porque ele não pode vir do chat banido do WhatsApp nem do novo número. Precisa viajar por um canal que é seu. Quando o usuário escreve, os envios sem from saem do novo número, porque a API usa o número para o qual a pessoa escreveu por último.
Qual é o passo a passo para migrar os usuários?
O passo a passo tem sete etapas, e o código abaixo cobre as seis primeiras.
- Verifique o evento e deduplique pelo id dele (eventos podem chegar duas vezes, e a especificação Standard Webhooks recomenda usar
webhook-idcomo chave de idempotência). - Encontre os usuários afetados: aqueles cujo chat vivia no
old_number. Salve o número em cada usuário a partir demessage.received, em quemessage.numberdiz qual número recebeu a mensagem. - Monte um link por usuário:
new_number.wa_linkmais?text=(o formato click-to-chat do WhatsApp) com uma mensagem curta que leve um código de uso único. - Envie o link por um canal seu: um banner no app, uma notificação push ou um e-mail.
- Convide em lotes, começando pelos mais recentemente ativos.
- Reconheça a pessoa quando ela escrever, restaure o histórico e faça o agente dizer que o número mudou.
- Lembre uma vez e pare. Depois de uma semana, mande um único lembrete a quem não voltou. O resto volta quando quiser o agente de novo.
Como tratar o evento e enviar os convites?
Verifique a assinatura no corpo bruto, deduplique pelo id do evento e comece os convites. Este handler reaproveita a verificação do Standard Webhooks que está na documentação da whasp.
import { randomBytes } from "node:crypto";
import { Webhook } from "standardwebhooks";
type User = { id: string; phone: string; number: string; lastActive: number; invited?: boolean };
declare const users: Map<string, User>; // seu banco de dados
declare function notify(user: User, link: string): Promise<void>; // e-mail, push ou banner no app
const wh = new Webhook(process.env.WHASP_WEBHOOK_SECRET ?? ""); // whsec_...
const seen = new Set<string>(); // ids de eventos já tratados
export const codes = new Map<string, string>(); // código de convite -> id do usuário
let moveFrom: { number: string; link: string } | null = null;
const DAILY_INVITES = 25; // abaixo de 80% dos 40 novos chats por dia, com folga para cadastros
export async function POST(request: Request) {
const body = await request.text(); // o corpo bruto: verifique antes de fazer o parse
let event: any;
try {
event = wh.verify(body, Object.fromEntries(request.headers));
} catch {
return new Response("Invalid signature", { status: 401 });
}
if (seen.has(event.id)) return new Response(null, { status: 204 });
seen.add(event.id);
if (event.type === "number.replaced") {
const { old_number, new_number } = event.data;
moveFrom = { number: old_number, link: new_number.wa_link };
await inviteBatch(); // em produção: responda 2xx primeiro e envie a partir de uma fila
}
return new Response(null, { status: 204 });
}
// Rode uma vez quando o evento chegar e depois uma vez por dia, até não sobrar ninguém.
export async function inviteBatch() {
const target = moveFrom;
if (!target) return;
const batch = [...users.values()]
.filter((u) => u.number === target.number && !u.invited)
.sort((a, b) => b.lastActive - a.lastActive)
.slice(0, DAILY_INVITES);
for (const user of batch) {
const code = randomBytes(4).toString("hex");
codes.set(code, user.id);
const text = encodeURIComponent(`Oi, sou eu de novo (código ${code})`);
await notify(user, `${target.link}?text=${text}`);
user.invited = true;
}
}
O código é aleatório e de uso único, e não o id do usuário, porque uma mensagem pré-preenchida é um texto visível e editável, e ids não devem ficar em links. Troque os Maps e o Set por tabelas do banco de dados.
O tamanho do lote vem dos limites. Um número da whasp aceita 40 novos chats a cada 24 horas e 100 chats ativos em 30 dias, e a saúde dele passa a at_risk quando um limite passa de 80%, o que dá 32 novos chats. Cerca de 25 convites por dia deixam espaço para novos cadastros. Um número com 100 usuários ativos leva quatro dias, e os mais recentemente ativos vão primeiro porque são os que vão perceber.
O que o convite deve dizer?
O convite deve dizer que o agente tem um novo número, mostrar para onde o link leva e soar como uma mensagem sua, não como um alerta. Evite a palavra “banido”; os usuários querem saber que a Ava mudou de número, não o motivo.
Assunto: A Ava tem um novo número
O número antigo da Ava no WhatsApp parou de funcionar. Toque abaixo para continuar de onde você parou. O nome e a foto são os mesmos, e ela se lembra de tudo. [Abrir o chat]
O toque abre o WhatsApp com “Oi, sou eu de novo (código 3f9a1c2e)” pronto para enviar. Envie o link só pelo seu app ou pelo seu domínio. Uma mensagem de um número desconhecido dizendo ser a Ava parece phishing, e o novo número nem consegue enviá-la. Depois da primeira semana, conte três coisas: convidados, retornaram e silenciosos. Os silenciosos recebem um lembrete.
Como reconhecer quem volta e restaurar o histórico?
Identifique a pessoa primeiro pelo telefone e depois pelo código, e então carregue a conversa pelo seu próprio id de usuário. O telefone em chat é o mesmo no novo número e no antigo, então quem volta é encontrado sem código. O código cobre quem escreve de um celular novo.
declare const users: Map<string, User>;
declare const codes: Map<string, string>;
declare function reply(userId: string, text: string, opts: { moved: boolean }): Promise<void>; // seu agente: carrega o histórico pelo id do usuário
export async function onInbound(m: { chat: string; number: string; text: string | null }) {
const code = m.text?.match(/\(código ([0-9a-f]{8})\)/)?.[1];
const byCode = code ? users.get(codes.get(code) ?? "") : undefined;
const user = byCode ?? [...users.values()].find((u) => u.phone === m.chat);
if (!user) return; // um desconhecido: indique o cadastro
if (code) codes.delete(code);
user.phone = m.chat;
const moved = user.number !== m.number;
user.number = m.number; // este chat agora vive no novo número
// moved: faça o agente dizer "Sou eu. Meu número antigo parou de funcionar, e eu ainda me lembro de tudo."
await reply(user.id, m.text ?? "", { moved });
}
Chame onInbound a partir do seu handler de message.received. A regra de projeto por trás disso: associe o histórico ao usuário, nunca ao número. Se a sua tabela de memória usa o número da whasp como parte da chave, um banimento deixa todas as conversas órfãs. A memória de conversa mostra um esquema que sobrevive a uma migração.
Conte à pessoa o que aconteceu. Um agente que recebe quem voltou com o roteiro de boas-vindas parece um estranho. Uma linha basta: é o mesmo agente, o número antigo parou de funcionar e a conversa continua.
E se o usuário não tiver outro canal?
Se o WhatsApp é o seu único canal com um usuário, você não consegue alcançá-lo. O chat antigo está mudo e o novo número não pode escrever primeiro. Três hábitos diminuem essa lacuna.
Colete um segundo canal no cadastro. Um e-mail ou um login do app custa um campo. É o único caminho de volta confiável.
Publique um link estável. Coloque no seu próprio domínio um redirecionamento que pede a GET /v1/numbers/pick um número saudável na hora do clique. Use esse link onde ele precisa durar, como no seu app, em bios e em QR codes, em vez de um link wa.me bruto.
// GET https://chat.example.com/ava: o link único que você imprime, publica e coloca nos e-mails.
export async function GET() {
const res = await fetch(
`https://api.whasp.ai/v1/numbers/pick?text=${encodeURIComponent("Oi, sou eu")}`,
{ headers: { Authorization: `Bearer ${process.env.WHASP_API_KEY}` } },
);
if (!res.ok) return new Response("Tente de novo em um minuto.", { status: 503 }); // 409 no_capacity
const { wa_link } = await res.json();
return Response.redirect(wa_link, 302);
}
Coloque esse link no perfil. Um substituto mantém o perfil, e o recado aceita até 139 caracteres em ASCII simples por enquanto (ou seja, sem acentos). Uma URL curta ali viaja para todos os substitutos futuros e fica onde os usuários podem ler. Links wa.me e QR codes trata de onde colocá-la.
O que configurar antes do primeiro banimento?
Qualquer número operado como uma pessoa pode ser banido, então prepare-se enquanto tudo está calmo.
- Teste o handler com um payload de exemplo de
number.replaced, incluindo um duplicado e uma assinatura inválida. - Planeje para um evento perdido. A whasp tenta entregar até 12 vezes com backoff, depois desiste e descarta o corpo, então ele não pode ser reenviado.
GET /v1/webhook/deliverieslista os últimos 7 dias.GET /v1/numberslista os seus números: um que você não reconhece é o substituto, e os camposstatusereplaced_bymostram o que aconteceu com o antigo. - Rode mais de um número quando puder. Assim, um banimento afeta só os usuários daquele número. Quantos números o seu agente precisa traz a conta.
- Associe o histórico ao usuário e guarde um telefone em todo registro de usuário.
O que fazer agora?
Adicione o handler de number.replaced esta semana e depois coloque o link estável no seu app e no recado do perfil. A partir de agora, colete um e-mail no cadastro. A API completa está na documentação.
Perguntas
O que acontece com os meus chats quando o WhatsApp bane um número?
Na whasp, nada se move. O número banido é aposentado e você recebe um substituto com o mesmo label, perfil e período pago. O novo número começa sem chats, e o seu sistema guarda o histórico, então você o restaura quando cada usuário escrever.
O número substituto pode mandar mensagem primeiro para os meus usuários?
Não. Um envio para quem nunca escreveu para aquele número retorna 409 chat_not_open, e a regra vale também para números substitutos. Os usuários precisam escrever primeiro, em geral tocando em um link wa.me que você manda por e-mail, push ou dentro do seu app.
Quantos usuários posso migrar para o novo número por dia?
Um número da whasp aceita 40 novos chats a cada 24 horas e 100 chats ativos em 30 dias, e passa a at_risk acima de 80% de um limite. Cerca de 25 convites por dia ficam abaixo disso e deixam espaço para novos cadastros.
Como aviso o novo número aos usuários se só tenho a conversa do WhatsApp?
Você não consegue alcançá-los pelo novo número, e o chat banido fica mudo. Prepare-se antes: colete um e-mail ou um login do app no cadastro e publique um link estável no seu domínio que sempre redirecione para um número saudável.
E se o meu webhook perdeu o evento number.replaced?
A whasp tenta entregar até 12 vezes com backoff, depois desiste e descarta o corpo. Liste os seus números com GET /v1/numbers: um que você não reconhece é o substituto, e os campos status e replaced_by mostram o que aconteceu com o antigo.


