EmailVerify LogoEmailVerify

Jotform

Email checker for Jotform. Verify emails in Jotform submissions and workflows.

Integrate EmailVerify with JotForm to verify email addresses in real-time and ensure high-quality form submissions.

Integration Methods

MethodBest ForComplexity
WebhookServer-side verificationLow
WidgetReal-time validationMedium
ZapierNo-code automationLow

Method 1: Webhook Integration

Set up a webhook to verify emails when forms are submitted.

Configure JotForm Webhook

  1. Open your form in JotForm
  2. Go to SettingsIntegrations
  3. Search for WebHooks
  4. Add your webhook URL: https://yoursite.com/api/jotform/webhook

Webhook Handler

// pages/api/jotform/webhook.js
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' });
    }

    // JotForm sends data as form-urlencoded
    const { rawRequest, formID, submissionID } = req.body;

    // Parse the raw request
    const formData = JSON.parse(rawRequest || '{}');

    // Find email field
    const email = findEmailField(formData);

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

    // Verify email
    const result = await bv.verify({ email });

    // Process result
    await processVerification({
        formId: formID,
        submissionId: submissionID,
        email,
        verification: result,
    });

    res.status(200).json({ success: true });
}

function findEmailField(formData) {
    // JotForm field names are like q3_email, q5_yourEmail, etc.
    for (const [key, value] of Object.entries(formData)) {
        if (typeof value === 'string' && value.includes('@')) {
            // Basic email pattern check
            if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
                return value;
            }
        }
    }
    return null;
}

async function processVerification({ formId, submissionId, email, verification }) {
    // Store result in your database
    await storeResult({
        formId,
        submissionId,
        email,
        status: verification.status,
        score: verification.score,
        isDisposable: verification.is_disposable,
        verifiedAt: new Date().toISOString(),
    });

    // Handle invalid emails
    if (verification.status === 'invalid' || verification.is_disposable) {
        await handleInvalidEmail({
            formId,
            submissionId,
            email,
            reason: verification.status === 'invalid'
                ? 'Invalid email address'
                : 'Disposable email detected',
        });
    }
}

async function handleInvalidEmail({ formId, submissionId, email, reason }) {
    // Option 1: Send notification
    await sendNotification({
        type: 'invalid_email',
        formId,
        submissionId,
        email,
        reason,
    });

    // Option 2: Update JotForm submission (mark as spam)
    await updateSubmission(submissionId, {
        flag: 'spam',
        note: `Email verification failed: ${reason}`,
    });
}

Update JotForm Submission

Use the JotForm API to mark or update submissions:

async function updateSubmission(submissionId, updates) {
    const response = await fetch(
        `https://api.jotform.com/submission/${submissionId}?apiKey=${process.env.JOTFORM_API_KEY}`,
        {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams({
                'submission[flag]': updates.flag || '0',
                'submission[new]': '0',
            }),
        }
    );

    return response.json();
}

Method 2: Custom Widget

Create a custom JotForm widget for real-time email validation.

Widget Code

<!-- widget.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Email Verification Widget</title>
    <link href="https://cdn.jotfor.ms/stylebuilder/static/form-common.css" rel="stylesheet">
    <style>
        .widget-container {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
        }

        .email-input {
            width: 100%;
            padding: 10px;
            font-size: 14px;
            border: 1px solid #ccc;
            border-radius: 4px;
            box-sizing: border-box;
        }

        .email-input:focus {
            outline: none;
            border-color: #4a90d9;
        }

        .email-input.valid {
            border-color: #28a745;
        }

        .email-input.invalid {
            border-color: #dc3545;
        }

        .status-message {
            margin-top: 5px;
            font-size: 12px;
        }

        .status-message.verifying {
            color: #666;
        }

        .status-message.valid {
            color: #28a745;
        }

        .status-message.invalid {
            color: #dc3545;
        }

        .loader {
            display: inline-block;
            width: 12px;
            height: 12px;
            border: 2px solid #ccc;
            border-top-color: #666;
            border-radius: 50%;
            animation: spin 1s linear infinite;
        }

        @keyframes spin {
            to { transform: rotate(360deg); }
        }
    </style>
</head>
<body>
    <div class="widget-container">
        <input
            type="email"
            id="email"
            class="email-input"
            placeholder="Enter your email"
        />
        <div id="status" class="status-message"></div>
    </div>

    <script src="https://cdn.jotfor.ms/static/prototype.forms.js"></script>
    <script src="https://cdn.jotfor.ms/static/jotform.forms.js"></script>
    <script>
        var JFCustomWidget = {
            subscribe: function(event, callback) {
                if (event === 'ready') {
                    callback();
                }
            }
        };

        // Configuration from widget settings
        var widgetConfig = {};

        JFCustomWidget.subscribe('ready', function() {
            // Get widget settings
            var settings = JFCustomWidget.getWidgetSettings();
            widgetConfig.apiEndpoint = settings.apiEndpoint || '/api/verify-email';
            widgetConfig.blockDisposable = settings.blockDisposable !== 'false';

            initWidget();
        });

        function initWidget() {
            var emailInput = document.getElementById('email');
            var statusEl = document.getElementById('status');
            var verifyTimeout;
            var isValid = false;

            emailInput.addEventListener('blur', function() {
                var email = this.value.trim();

                if (!email) {
                    clearStatus();
                    return;
                }

                if (!isValidFormat(email)) {
                    showStatus('invalid', 'Please enter a valid email format');
                    return;
                }

                clearTimeout(verifyTimeout);
                verifyTimeout = setTimeout(function() {
                    verifyEmail(email);
                }, 300);
            });

            function verifyEmail(email) {
                showStatus('verifying', '<span class="loader"></span> Verifying...');

                fetch(widgetConfig.apiEndpoint, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ email: email })
                })
                .then(function(response) { return response.json(); })
                .then(function(result) {
                    if (result.status === 'valid' && !result.disposable) {
                        showStatus('valid', '✓ Email verified');
                        isValid = true;
                    } else if (result.status === 'invalid') {
                        showStatus('invalid', '✗ Please enter a valid email');
                        isValid = false;
                    } else if (result.disposable && widgetConfig.blockDisposable) {
                        showStatus('invalid', '✗ Please use a permanent email');
                        isValid = false;
                    } else {
                        showStatus('valid', '✓ Email verified');
                        isValid = true;
                    }

                    // Send value to JotForm
                    JFCustomWidget.sendData({
                        value: email,
                        valid: isValid
                    });
                })
                .catch(function(error) {
                    console.error('Verification error:', error);
                    // Allow on error
                    isValid = true;
                    clearStatus();
                    JFCustomWidget.sendData({ value: email, valid: true });
                });
            }

            function showStatus(type, message) {
                statusEl.innerHTML = message;
                statusEl.className = 'status-message ' + type;
                emailInput.className = 'email-input ' + (type === 'verifying' ? '' : type);
            }

            function clearStatus() {
                statusEl.innerHTML = '';
                statusEl.className = 'status-message';
                emailInput.className = 'email-input';
            }

            function isValidFormat(email) {
                return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
            }
        }
    </script>
</body>
</html>

Host and Register Widget

  1. Host the widget HTML on your server
  2. In JotForm, go to Form BuilderAdd Form ElementWidgets
  3. Search for your widget or use "Custom HTML"
  4. Configure the widget URL

Method 3: Zapier Integration

Connect JotForm to EmailVerify using Zapier.

Zap Setup

Step 1: Trigger

  • App: JotForm
  • Event: New Submission

Step 2: Verify Email

  • App: Webhooks by Zapier
  • Event: POST
  • Configuration:
    URL: https://api.emailverify.ai/v1/verify
    Headers:
      Authorization: Bearer YOUR_API_KEY
      Content-Type: application/json
    Data:
      {"email": "{{email_field}}"}

Step 3: Filter

  • Only continue if: {{status}} does not equal valid

Step 4: Action (for invalid emails)

  • App: Gmail
  • Event: Send Email
  • Configuration:
    To: admin@yourcompany.com
    Subject: Invalid Email Submission - JotForm
    Body: Email {{email}} failed verification. Status: {{status}}

JotForm API Integration

For advanced workflows, use the JotForm API directly.

Fetch and Verify Submissions

// services/jotform.js
import EmailVerify from '@emailverify/sdk';

const bv = new EmailVerify(process.env.EMAILVERIFY_API_KEY);
const jotformApiKey = process.env.JOTFORM_API_KEY;

class JotFormService {
    async getFormSubmissions(formId, options = {}) {
        const params = new URLSearchParams({
            apiKey: jotformApiKey,
            limit: options.limit || 100,
            offset: options.offset || 0,
            filter: JSON.stringify(options.filter || {}),
        });

        const response = await fetch(
            `https://api.jotform.com/form/${formId}/submissions?${params}`
        );

        const data = await response.json();
        return data.content;
    }

    async verifySubmissions(formId) {
        const submissions = await this.getFormSubmissions(formId, {
            filter: { 'status:ne': 'DELETED' }
        });

        const results = [];

        for (const submission of submissions) {
            const email = this.findEmailInSubmission(submission);

            if (!email) continue;

            const verification = await bv.verify({ email });

            results.push({
                submissionId: submission.id,
                email,
                status: verification.status,
                score: verification.score,
                isDisposable: verification.is_disposable,
            });

            // Update submission with verification result
            if (verification.status === 'invalid' || verification.is_disposable) {
                await this.flagSubmission(submission.id);
            }
        }

        return results;
    }

    findEmailInSubmission(submission) {
        const answers = submission.answers || {};

        for (const [questionId, answer] of Object.entries(answers)) {
            if (answer.type === 'control_email') {
                return answer.answer;
            }

            // Also check text fields that might contain email
            if (answer.answer && typeof answer.answer === 'string') {
                if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(answer.answer)) {
                    return answer.answer;
                }
            }
        }

        return null;
    }

    async flagSubmission(submissionId) {
        const response = await fetch(
            `https://api.jotform.com/submission/${submissionId}?apiKey=${jotformApiKey}`,
            {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: new URLSearchParams({
                    'submission[flag]': '1',
                }),
            }
        );

        return response.json();
    }

    async addSubmissionNote(submissionId, note) {
        // JotForm doesn't have a direct note API, but you can update custom fields
        // or use a separate tracking system
    }
}

export const jotformService = new JotFormService();

Usage

import { jotformService } from './services/jotform';

// Verify all submissions for a form
async function verifyFormSubmissions(formId) {
    const results = await jotformService.verifySubmissions(formId);

    const stats = {
        total: results.length,
        valid: results.filter(r => r.status === 'valid' && !r.isDisposable).length,
        invalid: results.filter(r => r.status === 'invalid').length,
        disposable: results.filter(r => r.isDisposable).length,
    };

    console.log('Verification Results:', stats);

    return results;
}

Conditional Logic Integration

Use verification results to control form behavior.

Pre-submit Validation

// Embed this in your form page
<script>
document.addEventListener('DOMContentLoaded', function() {
    const form = document.querySelector('form[id^="form_"]');
    const emailField = document.querySelector('input[type="email"]');

    let emailVerified = false;
    let verificationResult = null;

    // Verify on blur
    emailField.addEventListener('blur', async function() {
        const email = this.value;

        if (!email) return;

        try {
            const response = await fetch('/api/verify-email', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ email }),
            });

            verificationResult = await response.json();
            emailVerified = verificationResult.status === 'valid' &&
                           !verificationResult.disposable;

            updateFieldUI(emailField, verificationResult);
        } catch (error) {
            // Allow on error
            emailVerified = true;
        }
    });

    // Validate on submit
    form.addEventListener('submit', function(e) {
        if (!emailVerified) {
            e.preventDefault();

            alert('Please enter a valid email address to continue.');
            emailField.focus();
            return false;
        }
    });

    function updateFieldUI(field, result) {
        // Remove existing classes
        field.classList.remove('valid', 'invalid');

        // Find or create status element
        let statusEl = field.parentNode.querySelector('.email-status');
        if (!statusEl) {
            statusEl = document.createElement('span');
            statusEl.className = 'email-status';
            field.parentNode.appendChild(statusEl);
        }

        if (result.status === 'valid' && !result.disposable) {
            field.classList.add('valid');
            statusEl.textContent = '✓ Email verified';
            statusEl.style.color = '#28a745';
        } else {
            field.classList.add('invalid');
            statusEl.textContent = result.disposable
                ? '✗ Please use a permanent email'
                : '✗ Please enter a valid email';
            statusEl.style.color = '#dc3545';
        }
    }
});
</script>

Bulk Verification for Existing Data

Process historical submissions:

async function bulkVerifyJotFormData(formId) {
    console.log('Starting bulk verification...');

    // Get all submissions
    const allSubmissions = [];
    let offset = 0;
    const limit = 100;

    while (true) {
        const submissions = await jotformService.getFormSubmissions(formId, {
            limit,
            offset,
        });

        if (submissions.length === 0) break;

        allSubmissions.push(...submissions);
        offset += limit;

        console.log(`Fetched ${allSubmissions.length} submissions...`);
    }

    console.log(`Total submissions: ${allSubmissions.length}`);

    // Extract emails
    const emailMap = new Map();
    for (const submission of allSubmissions) {
        const email = jotformService.findEmailInSubmission(submission);
        if (email) {
            emailMap.set(submission.id, email);
        }
    }

    console.log(`Found ${emailMap.size} emails to verify`);

    // Bulk verify
    const emails = [...emailMap.values()];
    const job = await bv.verifyBulk(emails);

    console.log(`Job ID: ${job.job_id}`);

    // Wait for completion
    let status;
    do {
        await sleep(5000);
        status = await bv.getBulkJobStatus(job.job_id);
        console.log(`Progress: ${status.progress_percent}%`);
    } while (status.status !== 'completed');

    // Get results
    const results = await bv.getBulkJobResults(job.job_id);

    // Process results
    const stats = { valid: 0, invalid: 0, disposable: 0 };

    for (const result of results.results) {
        if (result.status === 'valid') stats.valid++;
        else stats.invalid++;
        if (result.is_disposable) stats.disposable++;

        // Find and flag invalid submissions
        if (result.status === 'invalid' || result.is_disposable) {
            for (const [submissionId, email] of emailMap.entries()) {
                if (email === result.email) {
                    await jotformService.flagSubmission(submissionId);
                    break;
                }
            }
        }
    }

    console.log('Verification complete:', stats);
    return stats;
}

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

Best Practices

1. Handle API Errors Gracefully

try {
    const result = await verifyEmail(email);
    // Process result
} catch (error) {
    console.error('Verification failed:', error);
    // Don't block submission on API errors
    return { valid: true };
}

2. Cache Results

const cache = new Map();
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours

async function verifyWithCache(email) {
    const cached = cache.get(email);
    if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
        return cached.result;
    }

    const result = await bv.verify({ email });
    cache.set(email, { result, timestamp: Date.now() });

    return result;
}

3. Rate Limiting for Webhooks

const rateLimiter = new Map();

function checkRateLimit(ip, limit = 10, windowMs = 60000) {
    const now = Date.now();
    const requests = rateLimiter.get(ip) || [];

    // Remove old requests
    const recent = requests.filter(time => now - time < windowMs);

    if (recent.length >= limit) {
        return false;
    }

    recent.push(now);
    rateLimiter.set(ip, recent);

    return true;
}

On this page