Typeform
Email checker for Typeform with webhook integration.
Intégrez EmailVerify avec Typeform pour vérifier les adresses e-mail au fur et à mesure de la soumission des réponses, garantissant des prospects de haute qualité et des informations de contact valides.
Méthodes d'intégration
| Méthode | Idéal pour | Complexité |
|---|---|---|
| Webhooks | Vérification en temps réel | Faible |
| Zapier | Automatisation sans code | Faible |
| Intégration API | Workflows personnalisés | Moyenne |
Méthode 1 : Intégration par Webhook
Configurez un webhook pour vérifier les e-mails lorsque les réponses du formulaire sont soumises.
Créer un point de terminaison Webhook
// pages/api/typeform/webhook.js
import crypto from 'crypto';
import EmailVerify from '@emailverify/sdk';
const bv = new EmailVerify(process.env.EMAILVERIFY_API_KEY);
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
// Vérifier la signature Typeform
const signature = req.headers['typeform-signature'];
if (!verifySignature(req.body, signature)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const { form_response } = req.body;
// Extraire l'e-mail des réponses
const email = extractEmail(form_response.answers);
if (!email) {
return res.status(200).json({ message: 'No email found' });
}
// Vérifier l'e-mail
const result = await bv.verify({ email });
// Traiter en fonction du résultat
await processVerification(form_response, email, result);
res.status(200).json({ success: true });
}
function verifySignature(payload, signature) {
const secret = process.env.TYPEFORM_WEBHOOK_SECRET;
const hash = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('base64');
return `sha256=${hash}` === signature;
}
function extractEmail(answers) {
const emailAnswer = answers.find(
answer => answer.type === 'email'
);
return emailAnswer?.email || null;
}
async function processVerification(response, email, result) {
const responseId = response.token;
// Stocker le résultat de vérification
await storeResult(responseId, {
email,
status: result.status,
score: result.score,
disposable: result.is_disposable,
verified_at: new Date().toISOString(),
});
// Prendre des mesures en fonction du résultat
if (result.status === 'invalid' || result.is_disposable) {
// Signaler ou notifier
await sendNotification({
type: 'invalid_email',
response_id: responseId,
email,
reason: result.status === 'invalid'
? 'Invalid email address'
: 'Disposable email detected',
});
} else if (result.status === 'valid') {
// Ajouter au CRM, liste d'e-mails, etc.
await addToCRM(email, response);
}
}Configurer le Webhook dans Typeform
- Accédez à votre formulaire dans Typeform
- Cliquez sur Connect → Webhooks
- Cliquez sur Add a webhook
- Entrez l'URL de votre webhook :
https://votresite.com/api/typeform/webhook - Copiez le secret et ajoutez-le à vos variables d'environnement
Méthode 2 : Intégration Zapier
Connectez Typeform à EmailVerify en utilisant Zapier pour une automatisation sans code.
Configuration Zap
- Déclencheur : Typeform → New Entry
- Action : Webhooks by Zapier → POST
Configuration du Webhook
URL: https://api.emailverify.ai/v1/verify
Method: POST
Headers:
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Data:
{
"email": "{{email_field}}",
"webhook_url": "https://hooks.zapier.com/hooks/catch/xxx/yyy"
}Traiter les résultats
Ajoutez un deuxième Zap pour traiter les résultats de vérification :
- Déclencheur : Webhooks by Zapier → Catch Hook
- Filtre : Continuer uniquement si status = "invalid" ou disposable = true
- Action : Gmail → Send Email (notifier l'équipe) ou Google Sheets → Create Row (journal)
Méthode 3 : Intégration API
Créez une intégration personnalisée en utilisant les deux API.
Gestionnaire de réponses Typeform
// services/typeform-verification.ts
import EmailVerify from '@emailverify/sdk';
interface TypeformAnswer {
type: string;
email?: string;
text?: string;
field: {
id: string;
ref: string;
type: string;
};
}
interface TypeformResponse {
token: string;
submitted_at: string;
answers: TypeformAnswer[];
}
interface VerificationResult {
responseId: string;
email: string;
status: string;
score: number;
isValid: boolean;
isDisposable: boolean;
}
class TypeformVerificationService {
private bv: EmailVerify;
constructor() {
this.bv = new EmailVerify(process.env.EMAILVERIFY_API_KEY!);
}
async verifyResponse(response: TypeformResponse): Promise<VerificationResult | null> {
const email = this.extractEmail(response.answers);
if (!email) {
return null;
}
const result = await this.bv.verify({ email });
return {
responseId: response.token,
email,
status: result.status,
score: result.score,
isValid: result.status === 'valid' && !result.is_disposable,
isDisposable: result.is_disposable,
};
}
private extractEmail(answers: TypeformAnswer[]): string | null {
const emailAnswer = answers.find(a => a.type === 'email');
return emailAnswer?.email || null;
}
async verifyBulkResponses(responses: TypeformResponse[]): Promise<VerificationResult[]> {
const emails = responses
.map(r => this.extractEmail(r.answers))
.filter((email): email is string => email !== null);
if (emails.length === 0) {
return [];
}
// Utiliser la vérification en masse
const job = await this.bv.verifyBulk(emails);
// Attendre la fin
let status;
do {
await this.sleep(5000);
status = await this.bv.getBulkJobStatus(job.job_id);
} while (status.status !== 'completed');
const results = await this.bv.getBulkJobResults(job.job_id);
// Mapper les résultats aux réponses
return responses
.map(response => {
const email = this.extractEmail(response.answers);
if (!email) return null;
const result = results.results.find(r => r.email === email);
if (!result) return null;
return {
responseId: response.token,
email,
status: result.status,
score: result.score,
isValid: result.status === 'valid' && !result.is_disposable,
isDisposable: result.is_disposable,
};
})
.filter((r): r is VerificationResult => r !== null);
}
private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
export const typeformVerification = new TypeformVerificationService();Récupérer les réponses Typeform
// services/typeform-api.ts
interface TypeformAPIResponse {
items: TypeformResponse[];
page_count: number;
total_items: number;
}
class TypeformAPI {
private apiKey: string;
private baseUrl = 'https://api.typeform.com';
constructor(apiKey: string) {
this.apiKey = apiKey;
}
async getResponses(formId: string, options: {
since?: string;
until?: string;
page_size?: number;
} = {}): Promise<TypeformAPIResponse> {
const params = new URLSearchParams();
if (options.since) params.append('since', options.since);
if (options.until) params.append('until', options.until);
if (options.page_size) params.append('page_size', options.page_size.toString());
const response = await fetch(
`${this.baseUrl}/forms/${formId}/responses?${params}`,
{
headers: {
'Authorization': `Bearer ${this.apiKey}`,
},
}
);
if (!response.ok) {
throw new Error(`Typeform API error: ${response.statusText}`);
}
return response.json();
}
async getAllResponses(formId: string): Promise<TypeformResponse[]> {
const allResponses: TypeformResponse[] = [];
let hasMore = true;
let before: string | undefined;
while (hasMore) {
const params: Record<string, string> = { page_size: '1000' };
if (before) params.before = before;
const queryString = new URLSearchParams(params).toString();
const response = await fetch(
`${this.baseUrl}/forms/${formId}/responses?${queryString}`,
{
headers: {
'Authorization': `Bearer ${this.apiKey}`,
},
}
);
const data = await response.json();
if (data.items.length === 0) {
hasMore = false;
} else {
allResponses.push(...data.items);
before = data.items[data.items.length - 1].token;
}
}
return allResponses;
}
}
export const typeformAPI = new TypeformAPI(process.env.TYPEFORM_API_KEY!);Exemple d'utilisation
import { typeformAPI } from './services/typeform-api';
import { typeformVerification } from './services/typeform-verification';
// Vérifier les réponses récentes
async function verifyRecentResponses(formId: string) {
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
const { items } = await typeformAPI.getResponses(formId, {
since: yesterday.toISOString(),
page_size: 100,
});
console.log(`Vérification de ${items.length} réponses...`);
const results = await typeformVerification.verifyBulkResponses(items);
// Générer un rapport
const valid = results.filter(r => r.isValid).length;
const invalid = results.filter(r => !r.isValid).length;
console.log(`Résultats : ${valid} valides, ${invalid} invalides`);
// Signaler les réponses invalides
for (const result of results) {
if (!result.isValid) {
await flagInvalidResponse(result);
}
}
return results;
}
async function flagInvalidResponse(result: VerificationResult) {
// Ajouter à la liste signalée, envoyer une notification, etc.
console.log(`Signalement de la réponse ${result.responseId} : ${result.email}`);
}Validation de formulaire en temps réel
Bien que Typeform ne prenne pas en charge la validation personnalisée de manière native, vous pouvez utiliser une page de destination avec Typeform intégré.
Page de destination avec pré-validation
<!DOCTYPE html>
<html>
<head>
<title>Formulaire de contact</title>
<style>
.form-container {
max-width: 500px;
margin: 50px auto;
padding: 20px;
}
.pre-check {
margin-bottom: 20px;
}
.pre-check input {
width: 100%;
padding: 12px;
font-size: 16px;
border: 1px solid #ddd;
border-radius: 4px;
}
.pre-check .status {
margin-top: 8px;
font-size: 14px;
}
.pre-check .status.valid { color: #28a745; }
.pre-check .status.invalid { color: #dc3545; }
.pre-check .status.checking { color: #666; }
.typeform-container {
display: none;
}
.typeform-container.visible {
display: block;
}
</style>
</head>
<body>
<div class="form-container">
<div class="pre-check">
<label>D'abord, vérifions votre e-mail :</label>
<input type="email" id="email" placeholder="votre@email.com" />
<div class="status" id="status"></div>
</div>
<div class="typeform-container" id="typeform">
<div data-tf-widget="FORM_ID" data-tf-hidden="email="></div>
</div>
</div>
<script src="//embed.typeform.com/next/embed.js"></script>
<script>
const emailInput = document.getElementById('email');
const statusEl = document.getElementById('status');
const typeformContainer = document.getElementById('typeform');
let verifiedEmail = null;
emailInput.addEventListener('blur', async function() {
const email = this.value;
if (!email || !email.includes('@')) {
return;
}
statusEl.textContent = 'Vérification de l\'e-mail...';
statusEl.className = 'status checking';
try {
const response = await fetch('/api/verify-email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email }),
});
const result = await response.json();
if (result.valid) {
statusEl.textContent = '✓ E-mail vérifié ! Veuillez continuer ci-dessous.';
statusEl.className = 'status valid';
verifiedEmail = email;
// Afficher Typeform avec l'e-mail pré-rempli
showTypeform(email);
} else {
statusEl.textContent = '✗ ' + (result.message || 'Veuillez entrer un e-mail valide');
statusEl.className = 'status invalid';
}
} catch (error) {
// Autoriser en cas d'erreur
statusEl.textContent = '';
showTypeform(email);
}
});
function showTypeform(email) {
typeformContainer.classList.add('visible');
// Mettre à jour le champ caché
const widget = typeformContainer.querySelector('[data-tf-widget]');
widget.setAttribute('data-tf-hidden', `email=${encodeURIComponent(email)}`);
// Réinitialiser l'intégration Typeform
window.tf.createWidget();
}
</script>
</body>
</html>Workflow de traitement des réponses
Gestionnaire de workflow complet
// services/workflow.js
import { typeformAPI } from './typeform-api';
import { typeformVerification } from './typeform-verification';
class ResponseWorkflow {
async processNewResponse(response) {
// Étape 1 : Vérifier l'e-mail
const verification = await typeformVerification.verifyResponse(response);
if (!verification) {
return { status: 'no_email' };
}
// Étape 2 : Router en fonction du résultat de vérification
if (!verification.isValid) {
return await this.handleInvalidEmail(response, verification);
}
// Étape 3 : Traiter la réponse valide
return await this.handleValidEmail(response, verification);
}
async handleInvalidEmail(response, verification) {
// Enregistrer l'e-mail invalide
await this.logToDatabase({
response_id: response.token,
email: verification.email,
status: 'rejected',
reason: verification.isDisposable ? 'disposable' : 'invalid',
submitted_at: response.submitted_at,
});
// Notifier l'équipe
await this.sendSlackNotification({
text: `⚠️ Soumission d'e-mail invalide détectée`,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*E-mail :* ${verification.email}\n*Raison :* ${verification.isDisposable ? 'E-mail jetable' : 'E-mail invalide'}\n*Score :* ${verification.score}`,
},
},
],
});
return { status: 'rejected', verification };
}
async handleValidEmail(response, verification) {
// Extraire toutes les données du formulaire
const formData = this.extractFormData(response);
// Ajouter au CRM
await this.addToCRM({
email: verification.email,
...formData,
source: 'typeform',
email_score: verification.score,
});
// Ajouter à la liste d'e-mails
await this.addToMailchimp(verification.email, formData);
// Enregistrer le succès
await this.logToDatabase({
response_id: response.token,
email: verification.email,
status: 'processed',
submitted_at: response.submitted_at,
});
return { status: 'processed', verification };
}
extractFormData(response) {
const data = {};
for (const answer of response.answers) {
const fieldRef = answer.field.ref;
switch (answer.type) {
case 'text':
case 'short_text':
case 'long_text':
data[fieldRef] = answer.text;
break;
case 'email':
data[fieldRef] = answer.email;
break;
case 'number':
data[fieldRef] = answer.number;
break;
case 'choice':
data[fieldRef] = answer.choice?.label;
break;
case 'choices':
data[fieldRef] = answer.choices?.labels;
break;
default:
data[fieldRef] = answer[answer.type];
}
}
return data;
}
async logToDatabase(data) {
// Implémentez votre journalisation en base de données
}
async sendSlackNotification(message) {
await fetch(process.env.SLACK_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(message),
});
}
async addToCRM(data) {
// Implémentez votre intégration CRM
}
async addToMailchimp(email, data) {
// Implémentez votre intégration Mailchimp
}
}
export const responseWorkflow = new ResponseWorkflow();Bonnes pratiques
1. Toujours vérifier les signatures de webhook
function verifySignature(payload, signature) {
const secret = process.env.TYPEFORM_WEBHOOK_SECRET;
const hash = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('base64');
return `sha256=${hash}` === signature;
}2. Gérer les erreurs API avec élégance
try {
const result = await bv.verify({ email });
// Traiter le résultat
} catch (error) {
console.error('La vérification a échoué :', error);
// Ne pas rejeter la soumission en cas d'erreurs API
await processWithoutVerification(response);
}3. Utiliser des champs cachés pour les e-mails pré-vérifiés
Lors de l'utilisation d'une page de pré-validation, passez l'e-mail vérifié en tant que champ caché :
data-tf-hidden="email={{verified_email}}"