EmailVerify LogoEmailVerify

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éthodeIdéal pourComplexité
WebhooksVérification en temps réelFaible
ZapierAutomatisation sans codeFaible
Intégration APIWorkflows personnalisésMoyenne

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

  1. Accédez à votre formulaire dans Typeform
  2. Cliquez sur ConnectWebhooks
  3. Cliquez sur Add a webhook
  4. Entrez l'URL de votre webhook : https://votresite.com/api/typeform/webhook
  5. 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

  1. Déclencheur : Typeform → New Entry
  2. 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 :

  1. Déclencheur : Webhooks by Zapier → Catch Hook
  2. Filtre : Continuer uniquement si status = "invalid" ou disposable = true
  3. 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}}"

Ressources connexes

On this page