Validation email dans les formulaires
Intégrez la validation email directement dans vos formulaires HTML, côté navigateur.
Prérequis
Créez une API Key avec allowedDomains restreint à votre domaine :
- Dashboard → Settings → API Keys → Créer
- Renseignez
allowedDomains: ["votresite.fr"] - Désactivez
smtp(gratuit, pas de crédits consommés)
Clé restreinte obligatoire
Une clé sans allowedDomains exposée dans du HTML peut être utilisée par n'importe qui.
Avec allowedDomains, la clé ne fonctionne que depuis votre domaine.
Intégration vanilla JS
<form id="signup-form">
<label for="email">Adresse email</label>
<input type="email" id="email" name="email" required />
<span id="email-feedback" style="font-size: 0.85rem; margin-top: 4px; display: block;"></span>
<button type="submit" id="submit-btn">S'inscrire</button>
</form>
<script>
const API_KEY = 'ywc_live_votre_cle_avec_allowed_domains';
const emailInput = document.getElementById('email');
const feedback = document.getElementById('email-feedback');
const submitBtn = document.getElementById('submit-btn');
let debounceTimer;
emailInput.addEventListener('input', () => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => validateEmail(emailInput.value), 600);
});
async function validateEmail(email) {
if (!email || !email.includes('@')) return;
feedback.textContent = '⏳ Vérification...';
feedback.style.color = '#6B7280';
try {
const res = await fetch('https://api.yeswecheck.fr/v2/email/validate', {
method: 'POST',
headers: {
'X-API-Key': API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
});
const data = await res.json();
switch (data.status) {
case 'valid':
feedback.textContent = '✅ Email valide';
feedback.style.color = '#10B981';
submitBtn.disabled = false;
break;
case 'invalid':
feedback.textContent = '❌ Email invalide — vérifiez l\'adresse';
feedback.style.color = '#EF4444';
submitBtn.disabled = true;
break;
case 'risky':
// Jetable, role account, aléatoire
const reason = data.disposable?.isDisposable
? 'adresse temporaire non acceptée'
: data.roleAccount?.isRoleAccount
? 'adresse générique non acceptée'
: 'adresse à risque';
feedback.textContent = `⚠️ Email ${reason}`;
feedback.style.color = '#F59E0B';
submitBtn.disabled = true;
break;
case 'unknown':
feedback.textContent = '🟡 Email non vérifié — vous pouvez continuer';
feedback.style.color = '#6B7280';
submitBtn.disabled = false;
break;
}
// Suggestion de correction de typo
if (data.typo?.hasTypo && data.typo?.suggestions?.length > 0) {
const suggestion = data.typo.suggestions[0];
feedback.innerHTML += ` — Vouliez-vous dire <a href="#" onclick="useTypo('${suggestion}')">${suggestion}</a> ?`;
}
} catch (err) {
feedback.textContent = '';
submitBtn.disabled = false; // En cas d'erreur, laisser passer
}
}
function useTypo(suggestion) {
emailInput.value = suggestion;
validateEmail(suggestion);
return false;
}
</script>
Intégration React
import { useState, useCallback } from 'react';
const API_KEY = process.env.NEXT_PUBLIC_YESWECHECK_API_KEY; // allowedDomains configuré
interface ValidationResult {
status: 'valid' | 'invalid' | 'risky' | 'unknown';
score: number;
typo?: { hasTypo: boolean; suggestions: string[] };
disposable?: { isDisposable: boolean };
roleAccount?: { isRoleAccount: boolean };
}
export function EmailInput() {
const [email, setEmail] = useState('');
const [result, setResult] = useState<ValidationResult | null>(null);
const [loading, setLoading] = useState(false);
const validate = useCallback(async (value: string) => {
if (!value.includes('@')) return;
setLoading(true);
try {
const res = await fetch('https://api.yeswecheck.fr/v2/email/validate', {
method: 'POST',
headers: { 'X-API-Key': API_KEY!, 'Content-Type': 'application/json' },
body: JSON.stringify({ email: value }),
});
setResult(await res.json());
} finally {
setLoading(false);
}
}, []);
return (
<div>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
onBlur={(e) => validate(e.target.value)}
placeholder="[email protected]"
/>
{loading && <span>⏳ Vérification...</span>}
{result && !loading && (
<span style={{ color: result.status === 'valid' ? '#10B981' : result.status === 'invalid' ? '#EF4444' : '#F59E0B' }}>
{result.status === 'valid' && '✅ Email valide'}
{result.status === 'invalid' && '❌ Email invalide'}
{result.status === 'risky' && '⚠️ Email risqué'}
{result.status === 'unknown' && '🟡 Non vérifié'}
</span>
)}
{result?.typo?.hasTypo && result.typo.suggestions[0] && (
<button onClick={() => { setEmail(result.typo!.suggestions[0]); validate(result.typo!.suggestions[0]); }}>
Corriger en "{result.typo.suggestions[0]}" ?
</button>
)}
</div>
);
}
Stratégies de filtrage recommandées
Formulaire d'inscription (strict)
// Bloquer invalid + risky
const shouldBlock = ['invalid', 'risky'].includes(data.status);
Newsletter (souple)
// Bloquer uniquement les invalides définitifs
const shouldBlock = data.status === 'invalid';
// Accepter risky avec avertissement, unknown sans message
Contact B2B (anti-jetable)
// Bloquer invalides et adresses jetables
const shouldBlock = data.status === 'invalid' ||
(data.status === 'risky' && data.disposable?.isDisposable);
Rate limits côté client
Pour éviter des appels excessifs, utilisez un debounce :
- Debounce 600ms : n'appeler l'API qu'après 600ms sans frappe
- Validation on blur : valider uniquement quand l'utilisateur quitte le champ
- Cache local : stocker le résultat pour ne pas re-valider le même email
// Cache simple (session)
const cache = new Map();
async function validateWithCache(email) {
if (cache.has(email)) return cache.get(email);
const result = await callApi(email);
cache.set(email, result);
return result;
}