Validate emails in WordPress and WooCommerce
Our WordPress plugin checks the email address a visitor submits before the form is accepted, and stops the results you choose with your own message. It works with WPForms, Contact Form 7, Gravity Forms, Elementor forms (Elementor Pro), WooCommerce registration and the WooCommerce checkout, both the classic checkout and the Checkout block.
The plugin is not in the WordPress.org plugin directory yet. Until it is, you install it from the zip on this page.
Before you start
- WordPress 6.0 or newer, PHP 8.1 or newer, and at least one of the form plugins above.
- An API key for the site. Create one on the API keys page with the permission "Validate addresses" (add "Read balance and usage" to see your balance in WordPress), and give it a daily credit cap, so a busy form or a wave of spam can never spend more than you expect.
Install the plugin
- Download email-validation.zip. It is built from the plugin's tested source and holds only the files the plugin needs.
- In WordPress, open Plugins → Add New Plugin → Upload Plugin, choose the zip and click Install Now, then Activate.
- Open Settings → Email validation, paste your API key and save. The key is kept on your server and never shown again. You can instead add
define( 'EVORG_API_KEY', 'your key' );towp-config.php. - Click Check the connection. It checks a free test address, then shows your credit balance if the key may read it.
To update the plugin later, download the zip again and upload it the same way: WordPress offers to replace the installed version, and your settings stay.
Forms
On the settings page you choose:
- The results to stop. Each check returns one of the five result statuses. By default forms stop
invalid(the address cannot receive mail) anddisposable(a throwaway inbox), and letvalid,catch_allandunknownthrough. - The forms to check: WPForms, Contact Form 7, Gravity Forms, Elementor forms, WooCommerce checkout and WooCommerce registration, each on or off.
- What happens when a check fails (no credits left, the API out of reach): the form goes through by default. You can choose to stop it instead.
- The message a visitor sees, and whether to add a suggested fix for a likely typo ("Did you mean …?").
- The region: the default API, or the EU endpoint for EU data residency.
The plugin checks a field only after the form's own checks passed, so an empty or malformed address gets the form's usual message and costs nothing. WPForms is checked after its spam checks. Contact Form 7, Gravity Forms and Elementor check fields before their spam filters, so spam submissions there can use credits. Two limits bound that: one visitor (one IP address) can cause at most 20 paid checks an hour, after which the form is treated as if the check could not be done (your setting above decides), and the daily credit cap on the key bounds the total.
WooCommerce checkout
The plugin checks the billing email when the customer places the order, on the classic checkout and on the Checkout block. A stopped order shows your message at the checkout, and the customer can correct the address and place the order again. The checkout has its own settings, in the WooCommerce checkout section of the settings page:
- Stop the order for these results. Only
invalidby default. Stoppingdisposableis your choice.catch_allandunknownare never stopped unless you tick them: a real customer can get them, and a lost order costs more than an unchecked address. - When a check fails. A check that times out or cannot be done (no credits, the API out of reach, a refused key) lets the order through by default. You can choose to stop the order instead.
- Message. What the customer sees when the order is stopped. Left empty, the checkout uses the forms' message.
- Order note. Off by default. When on, the order gets a private note with the result, such as Email validation: the billing email is valid (accepted)., or the error code of a check that could not be done.
The checkout is turned on or off with WooCommerce checkout under Check these forms. The plugin is compatible with WooCommerce's High-Performance Order Storage (HPOS) and with the Cart and Checkout blocks.
What is kept, and what is logged
- The address is never stored by the plugin. It is sent to the API in the body of an HTTPS request. The plugin remembers the result (its status and reason only) for an hour under a hash of the address, so a form sent again, or a checkout placed again after a declined payment, is not checked or charged again. An
unknownresult is asked again next time. - Logs never hold the address. At checkout, a stopped order and a check that could not be done are written to WooCommerce → Status → Logs under the source email-validation-org, with the status and reason or the error code. A passing order is not logged.
- The order note holds the status, never the address.
- The visitor limit keeps a salted hash of the visitor's IP address (for IPv6, of its /64) with a count, for at most an hour; never the address itself.
- What the API keeps is in the privacy policy.
Try it for free
Test addresses cost nothing and contact no mail server. With the default settings, no_mailbox@sandbox.email-validation.org is stopped and accepted@sandbox.email-validation.org passes, on a form and at checkout; disposable@sandbox.email-validation.org is stopped on a form but passes the checkout until you tick disposable there. The test addresses page lists them all.
Costs
Each completed check costs 1 credit, as with any validation request. unknown results, errors and test addresses are free (credits and billing). A form or a checkout sent again with the same address within an hour is not charged again.
For developers
A filter decides last. It receives the message (or null when the address passes), the API's result (or a WP_Error when the check could not be done), the address and the form: the checkout is woo_checkout. Return a message to stop the form or the order, or null to let it through. For example, to stop throwaway inboxes at checkout only:
add_filter( 'evorg_blocked_message', function ( $message, $result, $email, $form ) {
if ( 'woo_checkout' === $form && is_array( $result ) && 'disposable' === $result['status'] ) {
return 'Please use an address you check: we send your receipt there.';
}
return $message;
}, 10, 4 );The filter evorg_visitor_limit sets the paid checks one visitor may cause per hour (default 20; 0 turns the limit off; administrators are never limited). Behind a reverse proxy or CDN that WordPress does not see through, every visitor can appear with the proxy's address and share one limit: return the real visitor address from the filter evorg_visitor_ip (only from a header your proxy sets and visitors cannot).