Intégrer l’OTP WhatsApp Natif : L’API d’Authentification Sans Configuration de Wasel
Par l’équipe d’ingénierie Wasel · Février 2026
Les mots de passe à usage unique (OTP) par SMS sont la norme en matière de vérification téléphonique depuis des décennies. Ils fonctionnent, but présentent de réels problèmes : retards de livraison, coûts internationaux, fraude par échange de carte SIM (SIM-swapping) et obligation pour les utilisateurs de jongler entre les applications pour copier un code à 6 chiffres.
WhatsApp change tout cela. Avec plus de 2 milliards d’utilisateurs actifs — et un taux de pénétration frôlant les 100 % sur des marchés comme le Maroc, l’Algérie et l’ensemble de la région MENA — l’OTP WhatsApp livre les codes via un canal que vos utilisateurs gardent ouvert toute la journée. Et le type de modèle AUTHENTICATION de Meta rend l’expérience encore meilleure : un bouton natif “Copier le code” copie directement le code dans le presse-papiers, de sorte que les utilisateurs n’ont jamais besoin de lire ou de saisir quoi que ce soit.
Cet article explique comment nous avons intégré l’OTP WhatsApp dans l’API externe de Wasel, les décisions que nous avons prises en cours de route, et pourquoi le résultat est plus simple à intégrer pour les développeurs que la plupart des alternatives.
Le Modèle d’Authentification Meta — Ce que c’est réellement
Avant de construire quoi que ce soit, nous avons passé du temps à lire attentivement la documentation de Meta. En effet, il existe une idée fausse très répandue : les modèles OTP WhatsApp NE SONT PAS pré-construits ou partagés par Meta. Chaque compte WhatsApp Business (WABA) doit enregistrer les siens.
Mais voici l’élément clé : le corps du texte est entièrement standardisé et verrouillé par Meta. Vous ne pouvez pas rédiger de texte personnalisé. Chaque modèle d’authentification, quelle que soit la langue, dit la même chose :
{{1}} est votre code de vérification. Pour votre sécurité, ne partagez pas ce code.
Vous ne contrôlez que deux ajouts optionnels :
- Un pied de page de recommandation de sécurité (toujours affiché par défaut)
- Un avis d’expiration du code (
Ce code expire dans N minutes.)
Le type de bouton est également fixé à l’une de ces trois options :
- COPY_CODE — l’utilisateur appuie sur “Copier le code” et le code va dans le presse-papiers (le plus courant)
- ONE_TAP — remplit automatiquement votre application Android native via un broadcast receiver
- ZERO_TAP — livraison entièrement silencieuse vers votre application native
Et ce qui est crucial : Les modèles AUTHENTICATION contournent le processus de révision habituel et sont automatiquement approuvés par Meta instantanément. Vous les créez et ils sont en ligne immédiatement.
Cela change complètement l’architecture.
La Mauvaise Approche : Demander aux Développeurs de Gérer les Modèles
Dans notre première conception, les développeurs devaient passer un paramètre template_name :
POST /external/v1/otp/send{ "phone": "+212600000000", "template_name": "my_otp_template", "lang": "fr"}C’était une erreur pour plusieurs raisons :
- Le développeur ne sait pas quel modèle créer. Il devrait lire la documentation de Meta, apprendre le format des composants AUTHENTICATION, créer le modèle dans WhatsApp Manager, attendre l’approbation, puis le référencer par son nom.
- Il n’y a rien à personnaliser de toute façon. Le corps est fixé par Meta. Le nom du modèle n’a aucune importance pour l’expérience de l’utilisateur.
- Chaque langue nécessite un modèle séparé. Une application prenant en charge 3 langues aurait besoin de 3 modèles, et le développeur devrait les suivre pour passer le bon nom.
La bonne approche est un OTP sans configuration — le développeur passe un numéro de téléphone et une langue, et tout le reste est automatique.
L’Architecture d’Auto-Provisionnement
Nous avons fixé un nom de modèle interne unique : wassel_otp. Ce nom n’est jamais exposé au développeur. Voici ce qui se passe lors du premier appel :
Développeur : POST /otp/send { "phone": "+212600000000", "lang": "fr" }
Wassel : 1. Vérification BDD — l'org #42 a-t-elle un modèle "wassel_otp" en "fr" ? Non. 2. Appel de l'API Meta Graph : POST /<WABA_ID>/message_templates { "name": "wassel_otp", "category": "AUTHENTICATION", "language": "fr", "components": [BODY+FOOTER+COPY_CODE_BUTTON] } 3. Meta approuve automatiquement → renvoie le template ID 4. Sauvegarde dans la table MessageTemplate (org_id=42, lang=fr, status=approved) 5. Génération du code à 6 chiffres 6. Envoi du message modèle au +212600000000 avec le code comme paramètre 7. Sauvegarde l'enregistrement WhatsAppOtp (status=pending, expires_in=10min) 8. Retourne { otp_id, expires_at } au développeurPour les appels suivants (même organisation, même langue) : l’étape 1 trouve un résultat en base de données, les étapes 2 à 4 sont ignorées. Le premier appel prend environ 1 seconde supplémentaire, tous les appels suivants sont instantanés.
Lorsque Meta a déjà le modèle mais pas notre base de données (par exemple après une migration), nous interceptons l’erreur “already exists” et écrivons l’enregistrement local de toute façon — pas de plantage, pas de doublons.
Le Cycle de Vie de l’OTP
Chaque OTP suit une machine à états :
pending → verified (code correct, délai respecté, limite d'essais respectée)pending → expired (délai écoulé avant vérification)pending → failed (limite d'essais dépassée)
Tout nouvel appel send() pour le même numéro expire instantanément et automatiquement les OTP "pending" existants.La table WhatsAppOtp stocke :
| Colonne | Rôle |
|---|---|
org_id | Isolation multi-locataire (multi-tenant) |
phone | Destinataire au format E.164 |
code | Le code généré (haché en production) |
reference | ID d’utilisateur/session fourni par l’appelant |
status | pending · verified · expired · failed |
attempts | Nombre de mauvaises tentatives |
max_attempts | Limite configurable (par défaut 3) |
expires_at | Calculé à partir de ttl_minutes |
wa_message_id | L’ID de message Meta pour le suivi de la livraison |
Le champ reference est essentiel pour les applications multi-sessions. Si un utilisateur ouvre simultanément deux flux de paiement, chacun obtient une référence unique et les recherches d’OTP sont restreintes à celle-ci. Sans reference, un appel de vérification cible toujours l’OTP le plus récent en attente pour le numéro donné.
La Charge Utile d’Envoi — Ce qui Part Réellement Chez Meta
Lors de l’envoi d’un modèle AUTHENTICATION de type COPY_CODE, la charge utile envoyée à l’API Messages est :
{ "messaging_product": "whatsapp", "recipient_type": "individual", "to": "+212600000000", "type": "template", "template": { "name": "wassel_otp", "language": { "code": "fr" }, "components": [ { "type": "body", "parameters": [{ "type": "text", "text": "847291" }] }, { "type": "button", "sub_type": "COPY_CODE", "index": "0", "parameters": [{ "type": "coupon_code", "coupon_code": "847291" }] } ] }}À noter que le code apparaît deux fois : l’une pour pallier la variable corporelle {{1}}, l’autre comme paramètre coupon_code pour déclencher le bouton de copie. C’est le format qu’exige Meta.
L’utilisateur verra :
847291 est votre code de vérification. Pour votre sécurité, ne partagez pas ce code. Ce code expire dans 10 minutes. [Copier le code] ← appuyer ici copie “847291” dans le presse-papiers.
L’API Développeur — La Plus Simple Possible
L’API finale expose trois endpoints :
POST /external/v1/otp/sendPOST /external/v1/otp/verifyGET /external/v1/otp/statusEnvoi :
curl -X POST https://api.wassel.app/external/v1/otp/send \ -H "X-API-Key: wsk_live_..." \ -H "Content-Type: application/json" \ -d '{"phone": "+212600000000", "reference": "checkout_abc123"}'{ "success": true, "otp_id": 42, "phone": "+212600000000", "expires_at": "2026-02-26T12:10:00Z", "status": "pending"}Vérification :
curl -X POST https://api.wassel.app/external/v1/otp/verify \ -H "X-API-Key: wsk_live_..." \ -H "Content-Type: application/json" \ -d '{"phone": "+212600000000", "code": "847291", "reference": "checkout_abc123"}'{ "success": true, "verified": true, "reason": "verified", "otp_id": 42}Pas de noms de modèles, pas de configuration WABA, pas besoin du Meta Developer Console. Un développeur possédant une clé API Wassel peut avoir un OTP WhatsApp actif dans son application en moins de 10 minutes.
Considérations de Sécurité
Génération du code. Les codes sont des chaînes numériques à 6 chiffres générées avec random.choices(string.digits). Pour un code à 6 chiffres, l’espace de forçage brut est de 1 000 000 de combinaisons. Avec la limite par défaut à 3 tentatives avant verrouillage, la probabilité d’une attaque par force brute réussie sur un OTP donné est de 3/1 000 000 = 0,0003 %.
Limitation des tentatives. Chaque mauvaise saisie incrémente le compteur attempts. Lorsque attempts > max_attempts, l’état de l’OTP passe à failed et il ne peut plus être utilisé — un nouvel OTP doit être demandé.
Expiration. Les OTP dont le délai est dépassé sont rejetés sans même incrémenter le compteur de tentatives (il n’y a plus rien à exploiter sur un code mort). Le contrôle du délai précède celui des tentatives.
Invalidation lors d’un renvoi. Lorsqu’un nouvel OTP est demandé pour le même téléphone, tous les OTP précédemment pending pour ce numéro passent de façon atomique à expired. Cela empêche les attaques par rejeu à l’aide d’un ancien code valide qui n’aurait pas encore été vérifié.
Portée de la référence. Lorsque reference est fournie, la recherche de l’OTP y est rattachée. Cela permet d’éviter les collisions inter-sessions dans les applications à flux multiples.
Intégrité du canal de distribution. Le code est distribué exclusivement via WhatsApp, qui nécessite le terminal physique et la carte SIM qui y est adossée. C’est en soi beaucoup plus sécurisé que le SMS (aucune faille liées au protocole SS7) et que l’e-mail (qui peut être accédé depuis des appareils indignes de confiance).
Prochaines Étapes
- Hooks de détection des fraudes — signalement des numéros de téléphone ayant un volume important de demandes d’OTP.
- Webhooks lors de vérification — poussez automatiquement une notification vers votre backend dès qu’un code est vérifié par un utilisateur, vous évitant ainsi d’avoir à consulter le statut (polling).
- Remplissage automatique (One-tap autofill) — pour les équipes qui construisent des applications natives Android, nous sommes en train de travailler sur la prise en charge du type de bouton
ONE_TAPqui envoie le code directement dans l’application sans passer par le presse-papiers.
Essayez-le
L’OTP WhatsApp est disponible sur tous les forfaits Wassel. Générez une clé d’API à partir de votre tableau de bord et suivez le Guide d’Intégration Frontend pour vous lancer.
Pour toute question, contactez-nous au tech@wasel.ma ou signalez un problème au sein de la communauté de développeurs.