Numéro WhatsApp banni : migrer vos utilisateurs vers un nouveau
Numéro de votre agent banni par WhatsApp : les conversations ne suivent pas et le nouveau numéro ne peut pas écrire en premier. Marche à suivre et code webhook.

L’essentiel
- Quand WhatsApp bannit un numéro whasp, vous recevez sans frais un numéro de remplacement avec le même label, le même profil et la même période payée, annoncé par un événement webhook number.replaced, mais aucune conversation ne suit.
- Le nouveau numéro ne peut écrire à personne en premier (409 chat_not_open) : chaque utilisateur doit donc lui écrire, et l'invitation doit passer par un canal qui vous appartient, comme l'e-mail, une notification push ou une bannière dans l'application.
- Envoyez à chaque utilisateur un lien construit à partir de new_number.wa_link avec un message prérempli qui porte un code à usage unique, et invitez par lots d'environ 25 par jour, car un numéro autorise 40 nouvelles conversations par 24 heures.
- Indexez l'historique par votre propre identifiant utilisateur, pas par le numéro : une personne qui revient est reconnue par son téléphone ou par son code, et la conversation reprend là où elle s'était arrêtée.
- Préparez-vous avant le premier bannissement : un second canal collecté à l'inscription, un lien de redirection stable, ce lien dans la ligne d'info du profil, et un gestionnaire number.replaced testé.
Quand WhatsApp bannit un numéro, les conversations ne suivent pas : le remplaçant démarre vide et ne peut écrire à personne en premier. Pour déplacer vos utilisateurs, prévenez-les par un canal qui vous appartient, envoyez à chacun un lien wa.me vers le nouveau numéro avec un message prérempli, et reconnaissez-les quand ils écrivent pour restaurer leur historique depuis votre propre base.
Que se passe-t-il quand WhatsApp bannit le numéro de votre agent ?
Quand WhatsApp bannit définitivement un numéro whasp, whasp le retire et vous bascule sans frais sur un numéro prêt. Le remplaçant a le même label, le même profil (nom, info, photo) et la même période payée. Rien d’autre ne suit. Aucune conversation ne bouge, et le nouveau numéro n’a rien de l’ancien historique.
Votre webhook reçoit un événement number.replaced avec l’ancien numéro et l’objet du nouveau numéro, y compris son wa_link. Voici l’événement, abrégé :
{
"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": "Ton amie IA", "synced": true },
"paid_until": "2026-11-17T19:00:00Z"
}
}
}
Un événement number.status_changed peut arriver aussi, quand l’état de santé de l’ancien numéro passe à critical. L’ordre des événements n’est pas garanti : ne vous y fiez pas. Le numéro banni ne répond plus à personne à partir de là. Pour comprendre pourquoi c’est arrivé, lisez pourquoi WhatsApp bannit des numéros. Pour la première heure qui suit un bannissement, y compris la page de WhatsApp sur les bannissements de comptes, voyez que faire quand un numéro est banni.
Le nouveau numéro peut-il écrire en premier à vos utilisateurs ?
Le nouveau numéro ne peut écrire à personne qui ne lui a pas écrit. Un envoi vers une telle personne renvoie 409 chat_not_open. C’est vrai pour tous les numéros whasp, remplaçants compris, et c’est voulu : les messages à froid sont ce qui fait bannir les numéros personnels, et un numéro neuf qui ouvrirait des centaines de conversations dès le premier jour appellerait le bannissement suivant.
Le déplacement doit donc partir de l’utilisateur. Il doit toucher un lien et écrire. L’invitation devient ainsi la partie difficile, car elle ne peut venir ni de la conversation WhatsApp bannie ni du nouveau numéro. Elle doit passer par un canal qui vous appartient. Dès qu’un utilisateur écrit, les envois sans from partent du nouveau numéro, car l’API utilise le numéro auquel la personne a écrit en dernier.
Quel est le plan pour déplacer vos utilisateurs ?
Le plan compte sept étapes, et le code ci-dessous couvre les six premières.
- Vérifiez l’événement et dédupliquez sur son identifiant (les événements peuvent arriver deux fois, et la spécification Standard Webhooks conseille d’utiliser
webhook-idcomme clé d’idempotence). - Trouvez les utilisateurs concernés : ceux dont la conversation vivait sur
old_number. Enregistrez le numéro sur chaque utilisateur à partir demessage.received, oùmessage.numberindique quel numéro a reçu le message. - Construisez un lien par utilisateur :
new_number.wa_linkplus?text=(le format click-to-chat de WhatsApp) avec un court message qui porte un code à usage unique. - Envoyez le lien par votre propre canal : une bannière dans l’application, une notification push ou un e-mail.
- Invitez par lots, en commençant par les plus récemment actifs.
- Reconnaissez la personne quand elle écrit, restaurez son historique, et faites dire à l’agent que le numéro a changé.
- Relancez une fois, puis arrêtez. Au bout d’une semaine, envoyez une relance aux personnes qui ne sont pas revenues. Les autres reviendront quand elles auront de nouveau envie de l’agent.
Comment traiter l’événement et envoyer les invitations ?
Vérifiez la signature sur le corps brut, dédupliquez sur l’identifiant de l’événement et lancez les invitations. Ce gestionnaire reprend la vérification Standard Webhooks de la documentation de 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>; // votre base de données
declare function notify(user: User, link: string): Promise<void>; // e-mail, push ou bannière dans l'application
const wh = new Webhook(process.env.WHASP_WEBHOOK_SECRET ?? ""); // whsec_...
const seen = new Set<string>(); // identifiants d'événements déjà traités
export const codes = new Map<string, string>(); // code d'invitation -> identifiant utilisateur
let moveFrom: { number: string; link: string } | null = null;
const DAILY_INVITES = 25; // sous 80 % des 40 nouvelles conversations par jour, avec de la marge pour les inscriptions
export async function POST(request: Request) {
const body = await request.text(); // le corps brut : vérifier avant de parser
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(); // en production : répondre 2xx d'abord, envoyer depuis une file
}
return new Response(null, { status: 204 });
}
// À lancer à l'arrivée de l'événement, puis une fois par jour jusqu'à ce qu'il ne reste personne.
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(`Salut, c'est encore moi (code ${code})`);
await notify(user, `${target.link}?text=${text}`);
user.invited = true;
}
}
Le code est aléatoire et à usage unique, et non l’identifiant utilisateur, car un message prérempli est un texte visible et modifiable, et les identifiants n’ont pas leur place dans des liens. Remplacez les Map et le Set par des tables de base de données.
La taille du lot découle des limites. Un numéro whasp autorise 40 nouvelles conversations par 24 heures et 100 conversations actives par 30 jours, et son état de santé passe à at_risk quand une limite dépasse 80 %, soit 32 nouvelles conversations. Environ 25 invitations par jour laissent de la place aux nouvelles inscriptions. Un numéro de 100 utilisateurs actifs demande quatre jours, et les personnes les plus récemment actives passent en premier, car ce sont elles qui le remarqueront.
Que doit dire l’invitation ?
L’invitation doit dire que l’agent a un nouveau numéro, montrer où mène le lien, et se lire comme un message de vous, pas comme un avertissement. Évitez le mot « banni » : pour les utilisateurs, ce qui compte est qu’Ava a changé de numéro, pas pourquoi.
Objet : Ava a un nouveau numéro
L’ancien numéro WhatsApp d’Ava ne fonctionne plus. Touchez le bouton ci-dessous pour reprendre là où vous en étiez. Le nom et la photo sont les mêmes, et elle se souvient de tout. [Ouvrir la conversation]
Le clic ouvre WhatsApp avec « Salut, c’est encore moi (code 3f9a1c2e) » prêt à être envoyé. N’envoyez le lien que depuis votre propre application ou votre propre domaine. Un message d’un numéro inconnu qui prétend être Ava ressemble à de l’hameçonnage, et le nouveau numéro ne peut de toute façon pas en envoyer. Après la première semaine, comptez trois choses : invités, revenus et silencieux. Les silencieux reçoivent une relance.
Comment reconnaître un utilisateur de retour et restaurer son historique ?
Reconnaissez la personne d’abord par son téléphone, ensuite par son code, puis chargez la conversation avec votre propre identifiant utilisateur. Le numéro de téléphone dans chat est le même sur le nouveau numéro que sur l’ancien : un utilisateur de retour est donc retrouvé sans code. Le code couvre la personne qui écrit depuis un nouveau téléphone.
declare const users: Map<string, User>;
declare const codes: Map<string, string>;
declare function reply(userId: string, text: string, opts: { moved: boolean }): Promise<void>; // votre agent : charge l'historique par identifiant utilisateur
export async function onInbound(m: { chat: string; number: string; text: string | null }) {
const code = m.text?.match(/\(code ([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; // un inconnu : renvoyez-le vers l'inscription
if (code) codes.delete(code);
user.phone = m.chat;
const moved = user.number !== m.number;
user.number = m.number; // cette conversation vit maintenant sur le nouveau numéro
// moved : faites dire à l'agent « C'est moi. Mon ancien numéro ne marchait plus, et je me souviens de tout. »
await reply(user.id, m.text ?? "", { moved });
}
Appelez onInbound depuis votre gestionnaire message.received. La règle de conception derrière : indexez l’historique par utilisateur, jamais par numéro. Si votre table de mémoire utilise le numéro whasp dans sa clé, un bannissement rend orphelines toutes les conversations. La mémoire de conversation montre un schéma qui survit à un déplacement.
Dites à la personne ce qui s’est passé. Un agent qui accueille un utilisateur de retour avec son script d’accueil fait l’effet d’un inconnu. Une ligne suffit : c’est le même agent, l’ancien numéro a cessé de fonctionner, et la conversation continue.
Et si un utilisateur n’a pas d’autre canal ?
Si WhatsApp est votre seul canal avec un utilisateur, vous ne pouvez pas le joindre. L’ancienne conversation est muette et le nouveau numéro ne peut pas écrire en premier. Trois habitudes réduisent cet écart.
Collectez un second canal à l’inscription. Un e-mail ou une connexion à l’application coûte un seul champ. C’est le seul chemin de retour fiable.
Publiez un lien stable. Mettez sur votre propre domaine une redirection qui demande à GET /v1/numbers/pick un numéro sain au moment du clic. Utilisez ce lien là où il doit durer, comme votre application, les bios et les QR codes, plutôt qu’un lien wa.me brut.
// GET https://chat.example.com/ava : le seul lien que vous imprimez, publiez et mettez dans les e-mails.
export async function GET() {
const res = await fetch(
`https://api.whasp.ai/v1/numbers/pick?text=${encodeURIComponent("Salut, c'est moi")}`,
{ headers: { Authorization: `Bearer ${process.env.WHASP_API_KEY}` } },
);
if (!res.ok) return new Response("Réessayez dans une minute.", { status: 503 }); // 409 no_capacity
const { wa_link } = await res.json();
return Response.redirect(wa_link, 302);
}
Mettez ce lien dans le profil. Un remplaçant garde le profil, et la ligne d’info accepte pour l’instant jusqu’à 139 caractères en ASCII simple. Une URL courte placée là passe à chaque futur remplaçant et se trouve là où les utilisateurs peuvent la lire. Liens wa.me et QR codes explique où les placer.
Que préparer avant le premier ban ?
N’importe quel numéro piloté comme une personne peut être banni : préparez-vous tant que tout est calme.
- Testez le gestionnaire avec un exemple de payload
number.replaced, y compris un doublon et une mauvaise signature. - Prévoyez un événement manqué. whasp retente une livraison jusqu’à 12 fois avec un délai croissant, puis abandonne et supprime le corps, qui ne peut donc plus être renvoyé.
GET /v1/webhook/deliveriesliste les 7 derniers jours.GET /v1/numbersliste vos numéros : un numéro que vous ne reconnaissez pas est le remplaçant, et les champsstatusetreplaced_bymontrent ce qui est arrivé à l’ancien. - Faites tourner plus d’un numéro quand vous le pouvez. Un bannissement ne déplace alors que les utilisateurs de ce numéro. Combien de numéros il faut à votre agent donne le calcul.
- Indexez l’historique par utilisateur, et gardez un numéro de téléphone sur chaque fiche utilisateur.
Que faire ensuite ?
Ajoutez le gestionnaire number.replaced cette semaine, puis mettez le lien stable dans votre application et dans la ligne d’info de votre profil. Collectez désormais un e-mail à l’inscription. L’API complète est décrite dans la documentation.
Questions
Que deviennent mes conversations quand WhatsApp bannit un numéro ?
Sur whasp, rien ne bouge. Le numéro banni est retiré et vous recevez un remplaçant avec le même label, le même profil et la même période payée. Le nouveau numéro démarre sans conversation, et c'est votre système qui détient l'historique : vous le restaurez quand chaque utilisateur écrit.
Le numéro de remplacement peut-il écrire en premier à mes utilisateurs existants ?
Non. Un envoi vers quelqu'un qui n'a jamais écrit à ce numéro renvoie 409 chat_not_open, et la règle vaut aussi pour les numéros de remplacement. Les utilisateurs doivent écrire en premier, en général en touchant un lien wa.me que vous leur envoyez par e-mail, push ou dans votre application.
Combien d'utilisateurs puis-je déplacer vers le nouveau numéro par jour ?
Un numéro whasp autorise 40 nouvelles conversations par 24 heures et 100 conversations actives par 30 jours, et passe en at_risk au-delà de 80 % d'une limite. Environ 25 invitations par jour vous gardent sous ce seuil et laissent de la place aux nouvelles inscriptions.
Comment annoncer le nouveau numéro aux utilisateurs si je n'ai que leur conversation WhatsApp ?
Vous ne pouvez pas les joindre depuis le nouveau numéro, et la conversation avec le numéro banni est muette. Préparez-vous en amont : collectez un e-mail ou une connexion à l'application à l'inscription, et publiez sur votre domaine un lien stable qui redirige toujours vers un numéro sain.
Et si mon webhook a raté l'événement number.replaced ?
whasp retente une livraison jusqu'à 12 fois avec un délai croissant, puis abandonne et supprime le corps. Listez vos numéros avec GET /v1/numbers : un numéro que vous ne reconnaissez pas est le remplaçant, et les champs status et replaced_by montrent ce qui est arrivé à l'ancien.


