WordPress Gravity Forms Email Verification with HubSpot Integration
Clean Your HubSpot CRM Without Losing Conversions
If you’re using Gravity Forms on WordPress to capture leads and syncing them to HubSpot, you know the challenge: fake email addresses, typos, and spam submissions clutter your CRM. But blocking submissions creates user friction and kills conversions.
This guide shows you how to verify email addresses in the background using checkboxHQ while maintaining 100% form conversion rates. Your users get instant confirmation, while you get clean, verified contacts in HubSpot.
What You’ll Build
By the end of this guide, you’ll have:
-
Non-blocking email verification - Users get instant confirmation
-
Background validation - Email verified after form submission
-
Automatic HubSpot sync with rich verification metadata
-
6+ custom contact properties tracking email quality metrics
-
Error handling for API failures and edge cases
-
Lead scoring enhancement - Verified emails get +15-20 points
-
Production-ready setup with proper logging and monitoring
-
Zero User Friction - Forms submit instantly, verification happens silently in background
Prerequisites
Required Accounts & Tools
- WordPress site (5.0 or higher) with admin access
- Gravity Forms (2.5+ recommended) - Purchase License
- HubSpot account (Free tier works, Marketing Hub recommended)
- checkboxHQ account - Register for free
- Basic PHP knowledge (copy-paste friendly, but helpful to understand)
Required Plugins
- Gravity Forms (core plugin)
- Gravity Forms HubSpot Add-On (free from GF downloads)
Architecture Overview
Here’s how the non-blocking system works:
User fills form → Form submits immediately (<1 second) ↓ User sees confirmation page ↓ Background verification starts ↓ checkboxHQ API calls (3 endpoints): 1. /verify_disposable/ (1 credit) 2. /verify_dns/ (2 credits) 3. /verify_public_provider/ (1 credit) ↓ Results stored in WordPress database ↓ HubSpot sync triggered with verification data → Contact created/updated with custom properties → Lead scoring applied → Workflows triggeredKey Benefits:
- Zero user friction: Instant form confirmation, no waiting
- 100% conversion rate: Never blocks legitimate users
- Complete validation: Gets disposable, DNS, and provider data
- Rich HubSpot data: 6+ custom properties for segmentation
- Background processing: Verification happens while user reads confirmation
Credit Usage per Verification:
- Total: 4 credits (1 + 2 + 1)
- On 5,000 verifications/month plan: Can verify 1,250 leads/month
Step 1: Install and Configure Gravity Forms
1.1 Install Gravity Forms
If you haven’t already:
- Purchase Gravity Forms license from gravityforms.com
- Download the plugin ZIP file
- In WordPress admin: Plugins → Add New → Upload Plugin
- Upload
gravityforms.zipand activate - Enter your license key: Forms → Settings → License
1.2 Install HubSpot Add-On
- Go to Forms → Add-Ons
- Find HubSpot Add-On and click “Install”
- Activate the add-on
- Click “Settings” next to HubSpot Add-On
- Authenticate with your HubSpot account (OAuth flow)
Step 2: Get Your checkboxHQ API Key
2.1 Sign Up for checkboxHQ
- Visit checkboxhq.com/signup
- Create your free account (100 verifications/month included)
- Confirm your email address
2.2 Generate API Key
- Log into your checkboxHQ dashboard
- Navigate to API Keys section
- Click “Create New API Key”
- Name it:
wordpress-gravity-forms - Copy the API key
Security Note: Keep this API key secure. Never commit it to public repositories or share it in support tickets.
Step 3: Create Your Lead Capture Form
3.1 Build the Form
- In WordPress admin, go to Forms → New Form
- Name it: “Contact Us - Email Verified”
- Add these fields:
Required Fields:
- Name (Type: Name - First & Last)
- Email (Type: Email) ← This is what we’ll verify
- Company (Type: Text)
- Phone (Type: Phone, optional)
Optional Fields:
- Message (Type: Textarea)
- How did you hear about us? (Type: Dropdown)
3.2 Configure Email Field Validation
- Click on the Email field to edit
- Under General tab:
- Check “Required”
- Enable “Confirm Email” (optional but recommended)
- Under Advanced tab:
- Add CSS Class:
verify-email - Note the Field ID (usually
2if it’s your second field)
- Add CSS Class:
3.3 Enable HubSpot Integration
- Click Settings (top right of form builder)
- Click HubSpot tab
- Choose “Create a new feed”
- Name: “Verified Lead to HubSpot”
- Map fields:
- Email → HubSpot Contact Email
- First Name → First Name
- Last Name → Last Name
- Company → Company Name
- Phone → Phone Number
Important: Don’t save yet - we’ll add custom properties in Step 5.
Step 4: Add Email Verification to WordPress
4.1 Add API Key to WordPress Config
For security, store your API key in wp-config.php (not in theme files):
- Connect to your WordPress site via FTP/SFTP or hosting file manager
- Edit
wp-config.php(in your WordPress root directory) - Add this line above the
/* That's all, stop editing! */comment:
// checkboxHQ API Configurationdefine('CHECKBOXHQ_API_KEY', 'cbx_your_actual_api_key_here');define('CHECKBOXHQ_API_BASE_URL', 'https://api.checkboxhq.com');Replace cbx_your_actual_api_key_here with your real API key from Step 2.2.
4.2 Create Email Verification Function
Add this code to your theme’s functions.php file or a custom plugin:
Location: wp-content/themes/your-theme/functions.php
<?php/** * checkboxHQ Email Verification for Gravity Forms * * NON-BLOCKING email verification: User gets instant form confirmation, * email verification happens in background, results sync to HubSpot * * This maintains conversion rates while cleaning your email list */
// Prevent direct accessif (!defined('ABSPATH')) exit;
/** * Verify email using checkboxHQ API (comprehensive check) * * Combines disposable + DNS checks for complete validation * Uses 3 credits total per verification (1 + 2) * * @param string $email Email address to verify * @return array Verification result with all metadata */function checkboxhq_verify_email_complete($email) { // Validate email format first (basic check) if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { return [ 'success' => false, 'is_valid' => false, 'is_disposable' => false, 'has_mx_record' => false, 'has_a_record' => false, 'is_public_provider' => false, 'provider' => null, 'reason' => 'invalid_format', 'message' => 'Invalid email format' ]; }
// Get API credentials from wp-config.php $api_key = defined('CHECKBOXHQ_API_KEY') ? CHECKBOXHQ_API_KEY : ''; $api_base = defined('CHECKBOXHQ_API_BASE_URL') ? CHECKBOXHQ_API_BASE_URL : 'https://api.checkboxhq.com';
// Check if API key is configured if (empty($api_key)) { error_log('checkboxHQ Error: API key not configured in wp-config.php'); return [ 'success' => false, 'reason' => 'api_not_configured', 'message' => 'Email verification not configured' ]; }
$results = [ 'success' => true, 'email' => $email, 'is_valid' => true, // Default assumption 'is_disposable' => false, 'has_mx_record' => false, 'has_a_record' => false, 'is_public_provider' => false, 'provider' => null, 'mx_records' => [], 'a_records' => [] ];
// Step 1: Check if disposable email (1 credit) $disposable_check = checkboxhq_api_call($api_base . '/api/v1/verify_disposable/', $api_key, ['email' => $email]);
if ($disposable_check && isset($disposable_check['is_disposable'])) { $results['is_disposable'] = $disposable_check['is_disposable']; $results['is_valid'] = $disposable_check['is_valid_format']; $results['provider'] = $disposable_check['provider']; }
// Step 2: Check DNS records (2 credits) $dns_check = checkboxhq_api_call($api_base . '/api/v1/verify_dns/', $api_key, ['email' => $email]);
if ($dns_check && isset($dns_check['has_mx_record'])) { $results['has_mx_record'] = $dns_check['has_mx_record']; $results['has_a_record'] = $dns_check['has_a_record']; $results['mx_records'] = $dns_check['mx_records'] ?? []; $results['a_records'] = $dns_check['a_records'] ?? [];
// Override provider if DNS check provides more info if (!empty($dns_check['provider'])) { $results['provider'] = $dns_check['provider']; } }
// Step 3: Check if public provider (1 credit) - Optional for lead scoring $public_check = checkboxhq_api_call($api_base . '/api/v1/verify_public_provider/', $api_key, ['email' => $email]);
if ($public_check && isset($public_check['is_public_provider'])) { $results['is_public_provider'] = $public_check['is_public_provider']; }
// Determine overall validity // Email is valid if: has valid format + has MX records + NOT disposable $results['is_deliverable'] = $results['is_valid'] && $results['has_mx_record'] && !$results['is_disposable'];
// Determine reason for invalidity if (!$results['is_valid']) { $results['reason'] = 'invalid_format'; } elseif ($results['is_disposable']) { $results['reason'] = 'disposable'; } elseif (!$results['has_mx_record']) { $results['reason'] = 'no_mx_records'; } else { $results['reason'] = 'valid'; }
return $results;}
/** * Make API call to checkboxHQ endpoint * * @param string $endpoint Full API endpoint URL * @param string $api_key API key for authentication * @param array $data Request payload * @return array|null Response data or null on error */function checkboxhq_api_call($endpoint, $api_key, $data) { $args = [ 'method' => 'POST', 'timeout' => 10, 'headers' => [ 'x-api-key' => $api_key, // Correct header format 'Content-Type' => 'application/json', 'User-Agent' => 'WordPress-GravityForms/' . get_bloginfo('version') ], 'body' => json_encode($data) ];
$response = wp_remote_post($endpoint, $args);
// Handle network errors if (is_wp_error($response)) { error_log('checkboxHQ API Error (' . $endpoint . '): ' . $response->get_error_message()); return null; }
$status_code = wp_remote_retrieve_response_code($response); $response_body = wp_remote_retrieve_body($response);
// Handle non-200 responses if ($status_code !== 200) { error_log('checkboxHQ API Error: HTTP ' . $status_code . ' - ' . $response_body); return null; }
return json_decode($response_body, true);}
/** * NON-BLOCKING: Verify email AFTER form submission * * User gets instant confirmation, verification happens in background * Results are stored and synced to HubSpot * * @param array $entry Form entry data * @param array $form Form configuration */add_action('gform_after_submission', 'checkboxhq_verify_after_submission', 10, 2);function checkboxhq_verify_after_submission($entry, $form) { // Find email field foreach ($form['fields'] as $field) { if ($field->type !== 'email') { continue; }
$email = rgar($entry, $field->id);
// Skip empty emails if (empty($email)) { continue; }
// Perform verification (non-blocking - happens after user sees confirmation) $verification = checkboxhq_verify_email_complete($email);
// Store verification result in entry meta gform_update_meta($entry['id'], 'checkboxhq_verification', $verification); gform_update_meta($entry['id'], 'checkboxhq_verification_date', current_time('mysql'));
// Log result error_log(sprintf( 'checkboxHQ Verification - Email: %s, Deliverable: %s, Disposable: %s, Has MX: %s, Reason: %s', $email, $verification['is_deliverable'] ? 'Yes' : 'No', $verification['is_disposable'] ? 'Yes' : 'No', $verification['has_mx_record'] ? 'Yes' : 'No', $verification['reason'] ?? 'unknown' ));
// Only process first email field break; }}
/** * Optional: Add email validation note to entry * * @param array $entry Form entry data * @param array $form Form configuration */add_action('gform_after_submission', 'checkboxhq_add_entry_note', 11, 2);function checkboxhq_add_entry_note($entry, $form) { $verification = gform_get_meta($entry['id'], 'checkboxhq_verification');
if ($verification && isset($verification['reason'])) { $note = 'Email Verification: ' . ucfirst($verification['reason']);
if ($verification['is_disposable']) { $note .= ' (Disposable Email - Review Required)'; } elseif (!$verification['has_mx_record']) { $note .= ' (No MX Records - Likely Invalid)'; } elseif ($verification['is_deliverable']) { $note .= ' ✓ (Verified & Deliverable)'; }
GFFormsModel::add_note($entry['id'], 0, 'checkboxHQ', $note, 'checkboxhq'); }}Save the file and verify there are no PHP errors by visiting your site.
Step 5: Sync Verified Leads to HubSpot with Custom Properties
5.1 Create Custom Properties in HubSpot
To track email verification status in HubSpot:
- Log into HubSpot
- Go to Settings (gear icon)
- Navigate to Data Management → Properties
- Click Create property
- Create these contact properties:
Property 1: Email Verification Status
- Object type:
Contact - Group:
Contact Information - Label:
Email Verification Status - Field type:
Dropdown select - Internal name:
email_verification_status - Options:
valid,disposable,no_mx_records,invalid_format - Description:
Result of email validation check
Property 2: Email Deliverable
- Object type:
Contact - Group:
Contact Information - Label:
Email Deliverable - Field type:
Single checkbox - Internal name:
email_deliverable - Description:
Whether the email passed full deliverability check (valid format + MX records + not disposable)
Property 3: Email Provider
- Object type:
Contact - Group:
Contact Information - Label:
Email Provider - Field type:
Single-line text - Internal name:
email_provider - Description:
Detected email service provider (Gmail, Outlook, company domain, etc.)
Property 4: Disposable Email Flag
- Object type:
Contact - Group:
Contact Information - Label:
Disposable Email Flag - Field type:
Single checkbox - Internal name:
disposable_email_flag - Description:
Indicates if email is from a temporary/disposable service
Property 5: Public Email Provider
- Object type:
Contact - Group:
Contact Information - Label:
Public Email Provider - Field type:
Single checkbox - Internal name:
public_email_provider - Description:
Indicates if email is from public provider (Gmail, Yahoo, etc.) vs business domain
Property 6: Email Has MX Records
- Object type:
Contact - Group:
Contact Information - Label:
Email Has MX Records - Field type:
Single checkbox - Internal name:
email_has_mx_records - Description:
Technical validation - domain configured to receive email
Property 7: Email Verified Date
- Object type:
Contact - Group:
Contact Information - Label:
Email Verified Date - Field type:
Date picker - Internal name:
email_verified_date - Description:
Date when email was last verified
5.2 Map Verification Data to HubSpot
Add this code to your functions.php (below the previous code):
/** * Add verification data to HubSpot contact properties * * @param array $entry Gravity Forms entry * @param array $form Form configuration */add_filter('gform_hubspot_contact_properties', 'checkboxhq_add_hubspot_properties', 10, 3);function checkboxhq_add_hubspot_properties($properties, $entry, $form) { // Get verification data from entry meta $verification = gform_get_meta($entry['id'], 'checkboxhq_verification');
if ($verification && $verification['success']) { // Add custom properties to HubSpot contact $properties['email_verification_status'] = $verification['reason']; $properties['email_deliverable'] = $verification['is_deliverable'] ? 'true' : 'false'; $properties['email_verified_date'] = date('Y-m-d');
// Additional enrichment data if (!empty($verification['provider'])) { $properties['email_provider'] = $verification['provider']; }
// Flag disposable emails if ($verification['is_disposable']) { $properties['disposable_email_flag'] = 'true'; }
// Flag public providers (Gmail, Yahoo, etc.) for lead scoring if (isset($verification['is_public_provider']) && $verification['is_public_provider']) { $properties['public_email_provider'] = 'true'; }
// MX record status (technical validation) $properties['email_has_mx_records'] = $verification['has_mx_record'] ? 'true' : 'false';
// Optional: Boost lead score for verified emails if ($verification['is_deliverable']) { if (isset($properties['hs_lead_score'])) { $properties['hs_lead_score'] = intval($properties['hs_lead_score']) + 15; }
// Extra points for business emails (non-public providers) if (isset($verification['is_public_provider']) && !$verification['is_public_provider']) { $properties['hs_lead_score'] = intval($properties['hs_lead_score']) + 5; } } }
return $properties;}5.3 Update HubSpot Feed Mapping
Since verification happens AFTER submission, all contacts sync to HubSpot (with verification data):
-
Go back to your form: Forms → Select your form → Settings → HubSpot
-
Edit the HubSpot feed
-
Ensure these fields are mapped:
- Email → HubSpot Contact Email
- First Name → First Name
- Last Name → Last Name
- Company → Company Name
- Phone → Phone Number
-
Save the feed
Note: Unlike blocking verification, we DON’T use conditional logic. All submissions sync to HubSpot with their verification status. You can then use HubSpot workflows to route verified vs unverified leads differently.
Step 6: Configure HubSpot Workflows (Recommended)
6.1 Create Lead Routing Workflow - High Quality Leads
Route verified business emails to sales immediately:
- Automation → Workflows → Create workflow
- Choose “Contact-based” workflow
- Name: “Hot Lead - Verified Business Email”
- Enrollment trigger:
Email Verification Statusisvalid- AND
Email Deliverableistrue - AND
Public Email Providerisfalse(business domain, not Gmail)
- Actions:
- Increase HubSpot Score by 20 points
- Add to list: “High Quality Leads”
- Send internal notification to sales team
- Create task: “Hot lead - follow up within 1 hour”
- Set lifecycle stage to “SQL” (Sales Qualified Lead)
- Save and activate
6.2 Create Lead Routing Workflow - Requires Review
Flag disposable/invalid emails for manual review:
- Automation → Workflows → Create workflow
- Name: “Flag Suspicious Leads”
- Enrollment trigger:
Disposable Email Flagistrue- OR
Email Has MX Recordsisfalse
- Actions:
- Decrease HubSpot Score by 10 points
- Add to list: “Needs Manual Review”
- Send internal notification to operations team
- Create task: “Review lead quality before contacting”
- Add tag: “suspicious-email”
- Save and activate
6.3 Create Lead Scoring Workflow - Consumer Emails
Handle public email providers (Gmail, Yahoo) differently:
- Automation → Workflows → Create workflow
- Name: “Consumer Email Lead Scoring”
- Enrollment trigger:
Email Deliverableistrue- AND
Public Email Provideristrue
- Actions:
- Increase HubSpot Score by 10 points (lower than business emails)
- Add to list: “Consumer Email Leads”
- Set lifecycle stage to “MQL” (Marketing Qualified Lead)
- Enroll in nurture sequence (longer sales cycle)
- Save and activate
6.4 Automated Re-verification Workflow
Re-verify old contacts periodically (optional):
- Automation → Workflows → Create workflow
- Name: “Re-verify Email Quarterly”
- Enrollment trigger:
Email Verified Dateis more than 90 days ago
- Actions:
- Send webhook to WordPress endpoint (trigger re-verification)
- Update
Email Verified Dateto today
- Save and activate
Step 7: Testing Your Integration
7.1 Test with Valid Email
- Go to your form page on the website
- Fill out the form with a real email address (use your own)
- Click submit
- Expected behavior:
- Form submits instantly (<1 second)
- Confirmation message appears immediately
- Check WordPress admin: Forms → Entries → View Entry
- Look for “Email Verification” note (added within 5-10 seconds)
- Contact appears in HubSpot within 2-3 minutes
- Custom properties populated:
email_verification_status: validemail_deliverable: trueemail_has_mx_records: true
7.2 Test with Disposable Email
- Fill out form with a disposable email (e.g.,
[email protected]) - Click submit
- Expected behavior:
- Form still submits instantly (non-blocking!)
- User sees confirmation message
- Entry created in Gravity Forms
- Entry note shows: “Email Verification: Disposable (Disposable Email - Review Required)”
- HubSpot contact created with:
email_verification_status: disposabledisposable_email_flag: trueemail_deliverable: false
7.3 Test with Invalid Email (Typo)
- Fill out form with typo email (e.g.,
[email protected]) - Click submit
- Expected behavior:
- Form submits instantly
- Entry note shows: “Email Verification: No_mx_records (No MX Records - Likely Invalid)”
- HubSpot contact created with:
email_verification_status: no_mx_recordsemail_has_mx_records: falseemail_deliverable: false
7.4 Monitor Background Verification
Watch verification happen in real-time:
- Submit a form
- Immediately go to Forms → Entries
- Click on the latest entry
- Scroll to Entry Meta or Notes section
- Refresh page after 5-10 seconds
- Note should appear with verification result
7.5 Check WordPress Debug Logs
If something isn’t working:
- Enable WordPress debug logging in
wp-config.php:
define('WP_DEBUG', true);define('WP_DEBUG_LOG', true);define('WP_DEBUG_DISPLAY', false);- Check
/wp-content/debug.logfor entries like:
checkboxHQ Verification - Email: [email protected], Deliverable: Yes, Disposable: No, Has MX: Yes, Reason: validcheckboxHQ API Error (/api/v1/verify_disposable/): HTTP 401 - Invalid API keyStep 8: Monitor and Optimize
8.1 Track Verification Metrics
Create a HubSpot report to monitor email quality:
- Reports → Create report → Custom report builder
- Data source: Contacts
- Add metrics:
- Total contacts created
- Contacts by
Email Verification Status - Form submissions vs verified leads
- Date range: Last 30 days
Key metrics to watch:
- Verification pass rate: Should be >85% for organic traffic
- Disposable email blocks: High numbers = bot traffic or fraud
- Invalid email blocks: High numbers = typos or fake submissions
8.2 Set Up Email Alerts
Get notified if verification API fails:
Add this to functions.php:
/** * Send admin alert if verification API is down */add_action('checkboxhq_api_error', 'checkboxhq_send_admin_alert');function checkboxhq_send_admin_alert($error_message) { $admin_email = get_option('admin_email'); $subject = '[ALERT] checkboxHQ Email Verification Down'; $message = "The checkboxHQ email verification API is experiencing issues:\n\n"; $message .= "Error: " . $error_message . "\n\n"; $message .= "Forms are currently accepting all submissions (fail-open mode).\n"; $message .= "Please check your API key and account status at checkboxhq.com\n";
wp_mail($admin_email, $subject, $message);}Trigger this action in the checkboxhq_verify_email function when errors occur.
Advanced Configurations
Option 1: Reduce API Credit Usage (Use Only DNS Check)
If you want to save credits, use only DNS verification (2 credits instead of 4):
/** * Lightweight verification - DNS check only * Uses 2 credits per verification */function checkboxhq_verify_email_lightweight($email) { if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { return ['success' => false, 'reason' => 'invalid_format']; }
$api_key = defined('CHECKBOXHQ_API_KEY') ? CHECKBOXHQ_API_KEY : ''; $api_base = defined('CHECKBOXHQ_API_BASE_URL') ? CHECKBOXHQ_API_BASE_URL : 'https://api.checkboxhq.com';
// Only call DNS verification endpoint $dns_check = checkboxhq_api_call($api_base . '/api/v1/verify_dns/', $api_key, ['email' => $email]);
return [ 'success' => true, 'email' => $email, 'is_deliverable' => $dns_check['has_mx_record'] ?? false, 'has_mx_record' => $dns_check['has_mx_record'] ?? false, 'has_a_record' => $dns_check['has_a_record'] ?? false, 'provider' => $dns_check['provider'] ?? null, 'reason' => ($dns_check['has_mx_record'] ?? false) ? 'valid' : 'no_mx_records' ];}
// Replace in checkboxhq_verify_after_submission function:$verification = checkboxhq_verify_email_lightweight($email);Option 2: Priority Verification (Verify Only High-Value Forms)
Only verify specific forms to save credits:
/** * Only verify high-priority forms (e.g., demo requests, enterprise inquiries) */add_action('gform_after_submission', 'checkboxhq_selective_verification', 10, 2);function checkboxhq_selective_verification($entry, $form) { // Only verify these form IDs $priority_form_ids = [1, 5, 12]; // Replace with your form IDs
if (!in_array($form['id'], $priority_form_ids)) { return; // Skip verification for other forms }
// Proceed with verification... foreach ($form['fields'] as $field) { if ($field->type === 'email') { $email = rgar($entry, $field->id); $verification = checkboxhq_verify_email_complete($email); gform_update_meta($entry['id'], 'checkboxhq_verification', $verification); break; } }}Option 3: Cache Verification Results
Avoid re-verifying the same email across multiple forms:
/** * Cache verification results for 24 hours */function checkboxhq_verify_email_cached($email) { $cache_key = 'checkboxhq_' . md5(strtolower(trim($email))); $cached = get_transient($cache_key);
if ($cached !== false) { error_log('checkboxHQ: Using cached result for ' . $email); return $cached; }
$result = checkboxhq_verify_email_complete($email);
// Cache for 24 hours (DAY_IN_SECONDS = 86400) set_transient($cache_key, $result, DAY_IN_SECONDS);
return $result;}
// Update checkboxhq_verify_after_submission to use cached version:$verification = checkboxhq_verify_email_cached($email);Option 4: Whitelist Trusted Domains
Skip verification for known partner/customer domains:
/** * Whitelist trusted email domains */function checkboxhq_is_whitelisted_domain($email) { $whitelisted_domains = [ 'youragency.com', 'trustedpartner.com', 'enterpriseclient.com' ];
$domain = substr(strrchr($email, "@"), 1); return in_array(strtolower($domain), $whitelisted_domains);}
// Add to checkboxhq_verify_after_submission:if (checkboxhq_is_whitelisted_domain($email)) { gform_update_meta($entry['id'], 'checkboxhq_verification', [ 'success' => true, 'is_deliverable' => true, 'reason' => 'whitelisted', 'provider' => 'trusted_domain' ]); return; // Skip API call}Option 5: Batch Verification with WP-Cron
Verify in batches every 5 minutes to reduce server load:
/** * Queue entries for batch verification */add_action('gform_after_submission', 'checkboxhq_queue_for_batch', 10, 2);function checkboxhq_queue_for_batch($entry, $form) { foreach ($form['fields'] as $field) { if ($field->type === 'email') { $email = rgar($entry, $field->id);
// Add to verification queue $queue = get_option('checkboxhq_verification_queue', []); $queue[] = [ 'entry_id' => $entry['id'], 'email' => $email, 'timestamp' => time() ]; update_option('checkboxhq_verification_queue', $queue);
break; } }}
/** * Process verification queue every 5 minutes */add_action('checkboxhq_process_batch', 'checkboxhq_batch_verify');function checkboxhq_batch_verify() { $queue = get_option('checkboxhq_verification_queue', []);
if (empty($queue)) { return; }
// Process up to 50 at a time $batch = array_splice($queue, 0, 50);
foreach ($batch as $item) { $verification = checkboxhq_verify_email_complete($item['email']); gform_update_meta($item['entry_id'], 'checkboxhq_verification', $verification); }
// Update queue update_option('checkboxhq_verification_queue', $queue);}
// Schedule cron job (add to theme activation)if (!wp_next_scheduled('checkboxhq_process_batch')) { wp_schedule_event(time(), 'five_minutes', 'checkboxhq_process_batch');}
// Add custom cron intervaladd_filter('cron_schedules', 'checkboxhq_add_cron_interval');function checkboxhq_add_cron_interval($schedules) { $schedules['five_minutes'] = [ 'interval' => 300, 'display' => 'Every 5 Minutes' ]; return $schedules;}Option 6: Flag But Don’t Block Suspicious Emails
Add warnings to suspicious entries without blocking submission:
/** * Add colored flags to Gravity Forms entries list */add_filter('gform_entries_field_value', 'checkboxhq_flag_suspicious_entries', 10, 4);function checkboxhq_flag_suspicious_entries($value, $form_id, $field_id, $entry) { // Only modify email field display $form = GFAPI::get_form($form_id); $field = GFFormsModel::get_field($form, $field_id);
if ($field->type !== 'email') { return $value; }
$verification = gform_get_meta($entry['id'], 'checkboxhq_verification');
if (!$verification) { return $value . ' <span style="color: orange;">⏳ Verifying...</span>'; }
if ($verification['is_disposable']) { return $value . ' <span style="color: red; font-weight: bold;">⚠️ DISPOSABLE</span>'; }
if (!$verification['has_mx_record']) { return $value . ' <span style="color: red;">❌ Invalid</span>'; }
if ($verification['is_deliverable']) { return $value . ' <span style="color: green;">✓ Verified</span>'; }
return $value;}Troubleshooting Common Issues
Issue 1: “checkboxHQ API Error: HTTP 401”
Causes:
- API key incorrect or not set in
wp-config.php - Using wrong header format
- API key revoked or expired
Solutions:
- Verify API key in
wp-config.php:
define('CHECKBOXHQ_API_KEY', 'cbx_actual_key_here');- Test API directly with curl:
curl -X POST https://api.checkboxhq.com/api/v1/verify_disposable/ \ -H "x-api-key: cbx_your_key" \ -H "Content-Type: application/json" \- Log into checkboxhq.com and verify API key is still active
- Regenerate API key if needed: Dashboard → API Keys → Regenerate
Issue 2: HubSpot Contact Not Created
Causes:
- HubSpot Add-On not authenticated
- Email field not mapped
- Conditional logic blocking sync
Solutions:
- Go to Forms → Settings → HubSpot and re-authenticate
- Check field mappings in HubSpot feed configuration
- Disable conditional logic temporarily to test
- Check HubSpot API limits (10,000 requests/day on free tier)
Issue 3: Custom Properties Not Appearing in HubSpot
Causes:
- Properties not created in HubSpot
- Internal name mismatch
- Add-On needs update
Solutions:
- Verify property internal names match exactly:
email_verification_statusemail_deliverableemail_verified_date
- Check if properties are visible to API: Settings → Properties → [Property] → Field type
- Update Gravity Forms HubSpot Add-On to latest version
Issue 4: Verification Data Not Appearing in Entry
Causes:
- WP-Cron not running
- PHP execution timeout
- Large backlog of entries to process
Solutions:
- Check if WP-Cron is running:
// Add to functions.php temporarilyadd_action('init', function() { error_log('Next checkboxhq_process_batch: ' . wp_next_scheduled('checkboxhq_process_batch'));});- Manually trigger verification:
// In WordPress admin, go to Tools → WP-Cron// Or trigger manually in functions.php:do_action('checkboxhq_process_batch');- Increase PHP max_execution_time:
// In wp-config.php:set_time_limit(300); // 5 minutes- Process verification queue manually in WordPress admin
Issue 5: High Credit Usage
Causes:
- Verifying all 3 endpoints for every email (4 credits each)
- Bot submissions inflating verification count
- Testing forms repeatedly
Solutions:
- Use lightweight verification (DNS only - 2 credits):
// See Advanced Configurations → Option 1- Implement caching to avoid re-verifying same emails:
// See Advanced Configurations → Option 3- Add reCAPTCHA to forms to block bots:
// Install Gravity Forms reCAPTCHA add-on- Monitor credit usage:
// Check your dashboard at checkboxhq.com/billing- Only verify priority forms (see Advanced Configurations → Option 2)
Security Best Practices
1. Protect Your API Key
DO:
- Store in
wp-config.php(not in database or theme files) - Use environment variables on staging/production
- Rotate keys quarterly
- Restrict key to specific domains in checkboxHQ dashboard
DON’T:
- Hardcode in theme files
- Commit to Git repositories
- Share in support tickets
- Use same key across multiple sites
2. Implement Rate Limiting
Prevent abuse by limiting verification requests:
/** * Rate limit verification requests (max 100/hour per IP) */function checkboxhq_check_rate_limit() { $ip = $_SERVER['REMOTE_ADDR']; $cache_key = 'checkboxhq_rate_' . md5($ip); $requests = get_transient($cache_key) ?: 0;
if ($requests >= 100) { return false; // Rate limit exceeded }
set_transient($cache_key, $requests + 1, 3600); // 1 hour return true;}
// Add to checkboxhq_verify_email function:if (!checkboxhq_check_rate_limit()) { return [ 'valid' => false, 'reason' => 'rate_limit', 'message' => 'Too many verification attempts. Please try again later.' ];}3. Sanitize User Input
Always sanitize email input before API calls:
$email = sanitize_email($email_value);$email = strtolower(trim($email));4. Handle PII Compliantly
For GDPR/CCPA compliance:
- Add privacy notice to form: “We verify email addresses to prevent spam”
- Don’t log full email addresses in production
- Delete verification metadata after 30 days
- Provide data deletion on user request
/** * Auto-delete verification data after 30 days */add_action('gform_delete_entry', 'checkboxhq_cleanup_verification_data');function checkboxhq_cleanup_verification_data($entry_id) { global $wpdb; $wpdb->delete( $wpdb->prefix . 'gf_entry_meta', ['entry_id' => $entry_id, 'meta_key' => 'checkboxhq_verification_%'], ['%d', '%s'] );}Performance Optimization
1. Cache Verification Results
Avoid re-verifying the same email:
/** * Cache verification results for 24 hours */function checkboxhq_verify_email_cached($email) { $cache_key = 'checkboxhq_verify_' . md5(strtolower($email)); $cached = get_transient($cache_key);
if ($cached !== false) { return $cached; }
$result = checkboxhq_verify_email($email); set_transient($cache_key, $result, DAY_IN_SECONDS);
return $result;}2. Async API Calls with Action Scheduler
For background processing (requires Action Scheduler plugin):
/** * Queue verification for background processing */add_action('gform_after_submission', 'checkboxhq_queue_verification', 10, 2);function checkboxhq_queue_verification($entry, $form) { as_enqueue_async_action('checkboxhq_process_verification', [ 'entry_id' => $entry['id'], 'form_id' => $form['id'] ]);}
add_action('checkboxhq_process_verification', 'checkboxhq_background_verify', 10, 2);function checkboxhq_background_verify($entry_id, $form_id) { $entry = GFAPI::get_entry($entry_id); $form = GFAPI::get_form($form_id);
// Verify email and update HubSpot // (code similar to synchronous version)}3. Optimize HubSpot API Calls
Batch updates to reduce API usage:
/** * Batch HubSpot contact updates (run hourly) */add_action('checkboxhq_batch_hubspot_sync', 'checkboxhq_sync_verified_contacts');function checkboxhq_sync_verified_contacts() { // Get all entries from last hour with verified emails // Update HubSpot in batches of 100 // (implementation requires HubSpot PHP SDK)}Monitoring and Analytics
Key Metrics to Track
In WordPress:
- Total form submissions
- Verification success rate
- Blocked disposable emails
- Blocked invalid emails
- API response times
In HubSpot:
- Contacts with
email_verification_status = valid - Lead quality (bounce rate, engagement)
- Sales velocity for verified vs unverified leads
Create a WordPress Dashboard Widget
/** * Add checkboxHQ stats to WordPress dashboard */add_action('wp_dashboard_setup', 'checkboxhq_add_dashboard_widget');function checkboxhq_add_dashboard_widget() { wp_add_dashboard_widget( 'checkboxhq_stats', 'checkboxHQ Email Verification Stats', 'checkboxhq_dashboard_widget_display' );}
function checkboxhq_dashboard_widget_display() { global $wpdb;
// Query last 30 days of form submissions $stats = $wpdb->get_results(" SELECT COUNT(*) as total, SUM(CASE WHEN meta_value LIKE '%valid\":true%' THEN 1 ELSE 0 END) as verified, SUM(CASE WHEN meta_value LIKE '%disposable\":true%' THEN 1 ELSE 0 END) as disposable FROM {$wpdb->prefix}gf_entry_meta WHERE meta_key LIKE 'checkboxhq_verification_%' AND entry_id IN ( SELECT id FROM {$wpdb->prefix}gf_entry WHERE date_created >= DATE_SUB(NOW(), INTERVAL 30 DAY) ) ");
echo '<p><strong>Last 30 Days:</strong></p>'; echo '<ul>'; echo '<li>Total Verified: ' . $stats[0]->verified . '</li>'; echo '<li>Blocked Disposable: ' . $stats[0]->disposable . '</li>'; echo '<li>Success Rate: ' . round(($stats[0]->verified / $stats[0]->total) * 100, 1) . '%</li>'; echo '</ul>';}Cost Analysis & ROI
checkboxHQ Pricing
- Free Tier: 100 credits/month
- Starter: $19/month for 50,000 credits (~12,500 full verifications)
- Growth: $39/month for 200,000 credits (~50,000 full verifications)
- Scale: $59/month for 500,000 credits (~125,000 full verifications)
Credit Usage:
- Full verification (disposable + DNS + public provider): 4 credits
- Lightweight (DNS only): 2 credits
- With caching enabled: Reduces usage by 30-50% for repeat submissions
HubSpot Cost Savings
Marketing contacts pricing (Marketing Hub):
- 1,000 contacts = $45/month
- 10,000 contacts = $800/month
ROI Example:
- Current: 1,000 form submissions/month
- 25% are fake/invalid (250 contacts)
- HubSpot cost for fake contacts: $11.25/month (at $45/1000)
- Plus: Sales time wasted on fake leads = ~$500/month (10 hours @ $50/hr)
- Total waste: $511.25/month
With checkboxHQ:
- Cost: $19/month (Starter plan for up to 12,500 verifications)
- Blocks/flags 250 fake contacts
- Saves sales team 10 hours/month
- Net savings: $491.25/month
- Annual savings: $5,895
- ROI: 2,456%
Additional Benefits:
- Improved email deliverability (fewer bounces)
- Better lead scoring accuracy
- Reduced CRM clutter
- Enhanced marketing attribution
- Higher sales team morale (less wasted time)
Migration Guide: Moving from Other Validators
From ZeroBounce
Replace ZeroBounce API calls:
// OLD: ZeroBounce$response = wp_remote_get("https://api.zerobounce.net/v2/validate?api_key={$key}&email={$email}");
// NEW: checkboxHQ$response = wp_remote_post('https://api.checkboxhq.com/v1/verify', [ 'headers' => ['Authorization' => 'Bearer ' . $api_key], 'body' => json_encode(['email' => $email])]);From NeverBounce
Mapping response fields:
// NeverBounce response format$result = $data['result']; // 'valid', 'invalid', 'disposable', 'catchall', 'unknown'
// checkboxHQ equivalent$is_valid = $data['deliverable'] === true;$is_disposable = $data['disposable'] === true;Next Steps
1. Scale Your Integration
Once your basic setup is working:
- Add verification to other Gravity Forms on your site
- Implement verification on WordPress comment forms
- Add verification to WooCommerce checkout
- Integrate with other CRMs (Salesforce, Pipedrive)
2. Improve Lead Quality Further
Combine email verification with:
- Phone number validation (checkboxHQ phone API)
- Company domain verification (block free emails like Gmail)
- Geolocation checks (flag mismatched countries)
- Behavioral scoring (time on page before submission)
3. Automate Lead Enrichment
Use verified emails to trigger:
- Clearbit enrichment (add company data)
- LinkedIn Sales Navigator lookup
- Predictive lead scoring models
- Personalized email sequences
Additional Resources
Documentation
Support
- checkboxHQ Support: [email protected]
- Gravity Forms Support: support.gravityforms.com
- Community Forum: community.gravityforms.com
Code Examples
- GitHub Repository: github.com/checkboxhq/wordpress-gravity-hubspot
- Code Snippets Library: docs.checkboxhq.com/snippets
Conclusion
You now have a production-grade, non-blocking email verification system that:
- Maintains 100% conversion rates - Forms submit instantly
- Verifies in background - Zero user friction
- Enriches HubSpot CRM with 7 custom properties
- Improves lead quality by identifying disposable/invalid emails
- Reduces sales waste - Team focuses on real prospects
- Enhances lead scoring - Verified business emails get priority
- Protects sender reputation - Fewer bounces from bad emails
- Provides actionable data - Segment by email quality
The Non-Blocking Advantage:
Unlike blocking verification (which can lose legitimate leads), this approach:
- Never rejects a user during submission
- Allows you to review flagged leads manually
- Maintains conversion rates while cleaning your database
- Gives sales context to prioritize outreach
What’s next?
- Test your integration with various email types
- Monitor verification stats in Gravity Forms entries
- Review HubSpot workflows to route leads by email quality
- Track ROI - measure time saved and HubSpot cost reduction
- Consider upgrading checkboxHQ plan as traffic grows
- Explore advanced features like batch verification and webhooks
Pro Tips:
- Review “Needs Manual Review” list in HubSpot weekly
- Train sales team to check verification status before outreach
- Use verification data in reporting to track lead source quality
- Set up alerts for sudden spikes in disposable email submissions (indicates bot attack)
- Re-verify old contacts quarterly to maintain database hygiene
Frequently Asked Questions
Q: Will this slow down my form submissions?
A: No! Forms submit instantly. Verification happens in the background after the user sees the confirmation page. Zero user friction.
Q: What happens if the checkboxHQ API is down?
A: Forms continue to work normally. Verification simply won’t happen for those submissions. You can manually verify them later or re-trigger verification when the API is back up.
Q: Do I need to verify every email field on every form?
A: No. You can selectively verify only high-priority forms (see Advanced Configurations → Option 2) to save credits.
Q: How many API credits does each verification use?
A: Full verification uses 4 credits (1 for disposable check + 2 for DNS + 1 for public provider). You can use DNS-only mode for 2 credits per verification.
Q: Does this work with Gravity Forms Webhooks add-on?
A: Yes! Verification completes before webhooks fire, so verification data is included in webhook payloads.
Q: Can I customize which emails get flagged?
A: Absolutely. You control the logic - flag disposable, public providers, missing MX records, or any combination. See the verification function for customization points.
Q: Is this GDPR compliant?
A: Yes. Email verification is legitimate interest for fraud prevention. Add privacy notice: “We verify email addresses to prevent spam and ensure service quality.”
Q: Can I use this with multiple forms on my site?
A: Yes! The code automatically applies to ALL Gravity Forms with email fields. Use selective verification (Option 2) to target specific forms.
Q: What about testing in a staging environment?
A: Use a separate checkboxHQ API key for staging to keep production analytics clean. checkboxHQ offers unlimited test API keys.
Q: How do I handle contacts that were created before I added verification?
A: Create a HubSpot workflow to tag unverified contacts, then either: (a) export and bulk verify, or (b) use the re-verification workflow (Step 6.4) to verify them gradually.
Q: What’s the difference between disposable and public provider checks?
A: Disposable = temporary services like Mailinator, Guerrilla Mail (usually spam). Public provider = Gmail, Yahoo, Outlook (legitimate but consumer emails, not business). Both are valid but signal different lead quality.
Q: Can verification happen faster than 5-10 seconds?
A: Yes! The default code runs on form submission (instant). The 5-10 second timeframe accounts for API latency. Typically, verification happens in less than 1 second.
Need help? Email [email protected] for personalized assistance.