Documentation PhoneVerify
Envoyez et vérifiez des codes OTP via WhatsApp, Telegram et Signal — depuis VOS propres numéros/bots émetteurs, sans API officielle payante.
Parcours en 4 étapes
- Compte → inscrivez-vous (auth.html) — email ou Google.
- Clé API → dashboard → « Clés API & intégration » → générez une clé
pvk_…(recopiable à tout moment). - Émetteur → « Numéro émetteur » → liez WhatsApp (QR) ou un compte/bot Telegram (voir § Émetteur).
- Envoi →
POST /v1/otp/requestavec le numéro destinataire → le code part de votre émetteur →POST /v1/otp/verify.
Intégration de A à Z dans votre application
Tout ce dont une application externe a besoin : une seule clé API (X-API-Key). L'émetteur lié à la clé est utilisé automatiquement.
1 — Demander un code
curl -X POST https://votre-domaine.fr/v1/otp/request \
-H "X-API-Key: pvk_…" -H "Content-Type: application/json" \
-d '{"phone":"+33612345678","channel":"whatsapp","fallback_channels":["telegram"]}'| Champ | Type | Description |
|---|---|---|
| phone requis | string | Numéro destinataire au format E.164 (+2376…). |
| channel | string | telegram | whatsapp | signal — canal préféré. |
| fallback_channels | string[] | Canaux de repli tentés dans l'ordre en cas d'échec. |
Réponse 200 : {"request_id","channel_used","attempted_channels","expires_in"}.
Stockez request_id côté serveur, associé à la session utilisateur.
2 — Vérifier le code
curl -X POST https://votre-domaine.fr/v1/otp/verify \
-H "X-API-Key: pvk_…" -H "Content-Type: application/json" \
-d '{"request_id":"3f4530…","code":"428193"}'3 — Exemple complet Node.js
const API = "https://votre-domaine.fr", KEY = process.env.OTP_KEY;
async function requestOtp(phone) {
const r = await fetch(`${API}/v1/otp/request`, {
method: "POST",
headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
body: JSON.stringify({ phone, channel: "telegram",
fallback_channels: ["whatsapp"] }),
});
const d = await r.json();
if (!r.ok) {
// Erreur : voir d.channel_errors (lien Telegram, whatsapp_not_linked…)
throw new Error(JSON.stringify(d));
}
return d.request_id; // stockez-le côté serveur
}
async function verifyOtp(request_id, code) {
const r = await fetch(`${API}/v1/otp/verify`, {
method: "POST",
headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
body: JSON.stringify({ request_id, code }),
});
return (await r.json()).status; // "verified" | "invalid_code" | …
}4 — Exemple complet Python
import httpx, os
API, KEY = "https://votre-domaine.fr", os.environ["OTP_KEY"]
H = {"X-API-Key": KEY}
def request_otp(phone: str) -> str:
r = httpx.post(f"{API}/v1/otp/request", headers=H, json={
"phone": phone, "channel": "whatsapp",
"fallback_channels": ["telegram"],
}, timeout=15)
r.raise_for_status()
return r.json()["request_id"] # à persister côté serveur
def verify_otp(request_id: str, code: str) -> bool:
r = httpx.post(f"{API}/v1/otp/verify", headers=H, json={
"request_id": request_id, "code": code,
}, timeout=15)
return r.json().get("status") == "verified"5 — Points d'attention d'intégration
- Gardez la clé côté serveur — jamais dans du code client public.
request_id: associez-le à votre session, ne le régénérez pas (usage unique).- Un code expiré/verrouillé nécessite un nouveau
request(cooldown entre deux envois). - Sur 502 : lisez
channel_errors— il contient la cause exacte et, pour Telegram, un lien de livraison à transmettre au destinataire.
Numéro émetteur (qui ENVOIE les codes)
Chaque clé API possède sa propre session émettrice — vos OTP partent de VOS numéros, pas d'un compte partagé de la plateforme.
| Canal | Émetteur | Configuration (dashboard → Numéro émetteur) |
|---|---|---|
| Votre numéro WhatsApp | « Afficher le QR » → scannez avec le numéro émetteur (Appareils connectés). « Changer de numéro » réinitialise. Une session par clé. | |
| Telegram (bot) | Votre bot | Créez un bot @BotFather → collez le token → validé via getMe. Sans bot personnel : bot par défaut de la plateforme. |
| Telegram universel | Votre compte Telegram | Numéro → code de connexion → connecté. Envoie à tout numéro sans action du destinataire (expérimental). |
| Signal | Votre numéro Signal | Enregistrement signal-cli (captcha + SMS) — voir docs/DEPLOYMENT.md. |
Changer d'émetteur : WhatsApp → « Changer de numéro » (nouveau QR) ; Telegram → « Retirer » ou nouveau token ; Universel → reconnecter un autre compte.
Côté destinataire — ce qui est requis
| Canal | Prérequis destinataire |
|---|---|
| Un compte WhatsApp actif sur le numéro. Rien d'autre — le message arrive directement. | |
| Telegram (bot) | Au choix : (a) /start + « Partager mon contact » une fois → lié à vie ; (b) cliquer le lien de livraison t.me/bot?start=otp_… renvoyé dans channel_errors → le bot lui envoie le code (aucune liaison préalable). |
| Telegram universel | Rien — le message arrive directement sur tout compte Telegram existant. |
| Signal | Un compte Signal actif sur le numéro. |
Numéros de tous pays acceptés (E.164). Un numéro sans compte sur le canal demandé → 404 user_not_found : proposez un canal de repli.
Référence API
Base : https://votre-domaine.fr/v1 — Auth : X-API-Key: pvk_…
/v1/otp/requestGénère + envoie un code. Corps : phone, channel, fallback_channels[]. → request_id, expires_in.
/v1/otp/verifyVérifie le code. Corps : request_id, code. → verified|invalid_code|expired|locked + attempts_remaining.
/v1/channelsÉtat des canaux pour votre clé : enabled + healthy (votre session émettrice, pas celle d'un autre).
Codes de statut de vérification
verified | Code correct — la demande est consommée (usage unique). |
invalid_code | Mauvais code, attempts_remaining indique les essais restants. |
expired | TTL dépassé ou request_id inconnu (même réponse — anti-oracle). |
locked | Trop de tentatives — nouvelle demande requise. |
Endpoints émetteur
/v1/wa-qrQR de liaison WhatsApp de VOTRE session : {"connected","qr","phone"}. QR en ASCII — affichable tel quel.
/v1/wa-resetRéinitialise votre session émettrice (nouveau numéro possible).
/v1/tg-auth/startEnvoie le code de connexion du compte universel : {"phone":"+…"}.
/v1/tg-auth/confirmValide la session : {"phone","code","password?"}. → compte émetteur actif pour toutes vos clés.
Messages personnalisés
Le texte des OTP est brandé par compte : nom d'entreprise saisi dans « Mon entreprise » + template optionnel.
Placeholders : {company} {code} {minutes}.
Défaut : {company} : votre code {code} — valable {minutes} min. Ne le partagez jamais.
Erreurs & statuts
| HTTP | status | Signification / action |
|---|---|---|
| 200 | sent / verified | OK. |
| 200 | invalid_code / expired / locked | Vérification échouée — voir table ci-dessus. |
| 401 | unauthorized | Clé absente, invalide ou révoquée. |
| 422 | invalid_phone | Numéro non E.164 ou invalide. |
| 429 | rate_limited / resend_cooldown | Quota dépassé — champ retry_after (secondes). |
| 502 | send_failed | Tous les canaux ont échoué → channel_errors + fallback_channels. |
Détails dans channel_errors
whatsapp_not_linked→ session émettrice non liée : scannez le QR (Numéro émetteur).telegram_chat_not_linked→ contient le lien de livraisont.me/…à transmettre au destinataire.tg_user_bridge:404:telegram_user_not_found→ le numéro n'a pas de compte Telegram.canal inconnu ou desactive→ canal non configuré côté serveur.
Limites & quotas
- Validité du code :
OTP_TTL_SECONDS(défaut 300 s). - Tentatives :
OTP_MAX_ATTEMPTS(5) puis verrouillage de la demande. - Cooldown renvoi :
OTP_RESEND_COOLDOWN_SECONDSentre deux codes sur un même numéro. - Rate-limit :
OTP_RATE_LIMIT_PER_PHONE/heure +GATEWAY_IP_RATE_LIMIT/min par IP.
Sur 429, respectez retry_after avant de réessayer.
Sécurité & risques
- Codes jamais stockés en clair (HMAC-SHA256 sel + poivre), comparaison temps constant, TTL strict.
- Logs : aucun code ni numéro en clair — empreintes tronquées uniquement.
- Clés API : gardez-les côté serveur ; révoquez toute clé compromise (dashboard).
- Risques connecteurs : WhatsApp universel/Telegram universel = comptes automatisés non officiels — risque de bannissement du numéro émetteur. Numéros dédiés + quotas bas. Détails :
docs/RISKS.md.
FAQ
Pourquoi mon destinataire doit-il cliquer un lien Telegram ? Un bot ne peut écrire qu'à un utilisateur l'ayant démarré — c'est le protocole. Le lien t.me/bot?start=otp_… livre le code en un clic sans liaison préalable. L'émetteur « universel » (compte utilisateur) supprime cette friction.
« La session WhatsApp a expiré » — refaites le QR dans « Numéro émetteur » ; la session persiste dans le volume Docker ensuite.
Puis-je avoir plusieurs émetteurs ? Oui — une session WhatsApp par clé API. Créez autant de clés que d'émetteurs voulus.
Comment changer de plan ? Dashboard → « Plans & abonnement ».
Où sont les stats ? « Vue d'ensemble » (compteurs + graphique 14 j) et « Vérifications » (50 derniers événements, numéros masqués).
Console de test
Testez l'API en direct — identique à la console du dashboard.