EmailVerify LogoEmailVerify

Typeform

Email checker for Typeform. Verify emails in Typeform responses and integrations.

Typeform に EmailVerify を統合し、回答が送信されたときにメールアドレスを検証して、高品質なリードと有効な連絡先情報を確保しましょう。

連携方法

方法最適な用途複雑さ
Webhookリアルタイム検証
Zapierノーコード自動化
API 連携カスタムワークフロー

方法 1:Webhook 連携

フォーム回答が送信されたときにメールを検証する Webhook をセットアップします。

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' });
    }

    // 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;

    // 回答からメールを抽出
    const email = extractEmail(form_response.answers);

    if (!email) {
        return res.status(200).json({ message: 'No email found' });
    }

    // メールを検証
    const result = await bv.verify({ email });

    // 結果に基づいて処理
    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;

    // 検証結果を保存
    await storeResult(responseId, {
        email,
        status: result.status,
        score: result.score,
        disposable: result.is_disposable,
        verified_at: new Date().toISOString(),
    });

    // 結果に基づいてアクション
    if (result.status === 'invalid' || result.is_disposable) {
        // フラグまたは通知
        await sendNotification({
            type: 'invalid_email',
            response_id: responseId,
            email,
            reason: result.status === 'invalid'
                ? '無効なメールアドレス'
                : '使い捨てメールを検出',
        });
    } else if (result.status === 'valid') {
        // CRM やメールリストに追加
        await addToCRM(email, response);
    }
}

Typeform で Webhook を設定

  1. Typeform でフォームを開く
  2. ConnectWebhooks をクリック
  3. Add a webhook をクリック
  4. Webhook URL を入力:https://yoursite.com/api/typeform/webhook
  5. シークレットをコピーして環境変数に追加

方法 2:Zapier 連携

ノーコード自動化のために Zapier を使用して Typeform を EmailVerify に接続します。

Zap の設定

  1. トリガー:Typeform → New Entry
  2. アクション:Webhooks by Zapier → POST

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"
}

結果の処理

検証結果を処理する 2 番目の Zap を追加:

  1. トリガー:Webhooks by Zapier → Catch Hook
  2. フィルター:status = "invalid" または disposable = true の場合のみ続行
  3. アクション:Gmail → Send Email(チームに通知)または Google Sheets → Create Row(ログ)

方法 3:API 連携

両方の API を使用してカスタム連携を構築します。

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 [];
    }

    // 一括検証を使用
    const job = await this.bv.verifyBulk(emails);

    // 完了を待機
    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);

    // 結果を回答にマッピング
    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();

使用例

import { typeformAPI } from './services/typeform-api';
import { typeformVerification } from './services/typeform-verification';

// 最近の回答を検証
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(`${items.length} 件の回答を検証中...`);

  const results = await typeformVerification.verifyBulkResponses(items);

  // レポートを生成
  const valid = results.filter(r => r.isValid).length;
  const invalid = results.filter(r => !r.isValid).length;

  console.log(`結果: ${valid} 件有効、${invalid} 件無効`);

  // 無効な回答をフラグ
  for (const result of results) {
    if (!result.isValid) {
      await flagInvalidResponse(result);
    }
  }

  return results;
}

async function flagInvalidResponse(result: VerificationResult) {
  // フラグリストに追加、通知を送信など
  console.log(`回答をフラグ ${result.responseId}: ${result.email}`);
}

リアルタイムフォーム検証

Typeform はネイティブでカスタム検証をサポートしていませんが、埋め込み Typeform を使用したランディングページで対応できます。

事前検証ランディングページ

<!DOCTYPE html>
<html>
<head>
    <title>お問い合わせフォーム</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>まず、メールアドレスを確認させてください:</label>
            <input type="email" id="email" placeholder="your@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 = 'メールを確認中...';
            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 = '✓ メール確認済み!下のフォームに進んでください。';
                    statusEl.className = 'status valid';
                    verifiedEmail = email;

                    // 事前入力されたメールで Typeform を表示
                    showTypeform(email);
                } else {
                    statusEl.textContent = '✗ ' + (result.message || '有効なメールを入力してください');
                    statusEl.className = 'status invalid';
                }
            } catch (error) {
                // エラー時は許可
                statusEl.textContent = '';
                showTypeform(email);
            }
        });

        function showTypeform(email) {
            typeformContainer.classList.add('visible');

            // 隠しフィールドを更新
            const widget = typeformContainer.querySelector('[data-tf-widget]');
            widget.setAttribute('data-tf-hidden', `email=${encodeURIComponent(email)}`);

            // Typeform 埋め込みを再初期化
            window.tf.createWidget();
        }
    </script>
</body>
</html>

ベストプラクティス

1. 常に 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. API エラーを適切に処理

try {
    const result = await bv.verify({ email });
    // 結果を処理
} catch (error) {
    console.error('検証に失敗:', error);
    // API エラー時は送信を拒否しない
    await processWithoutVerification(response);
}

3. 事前検証済みメールには隠しフィールドを使用

事前検証ページを使用する場合、検証済みメールを隠しフィールドとして渡す:

data-tf-hidden="email={{verified_email}}"

関連リソース

On this page