WooCommerce 2.0 introduced a lot of new features to the core plugin, one being a new class-based implementation for emails. This makes it really easy to add your own custom email types to WooCommerce. In this article, we’re going to create a sample custom email that’s sent when an order with expedited shipping is received. A lot of larger sites disable new order emails if they receive a lot of orders, but most will still want to be aware of orders that need to be shipped immediately. Feel free to skip to the completed plugin if you want to simply use it as a base for your own custom email πŸ™‚

First, add a folder to your site’s wp-content/plugins directory named woocommerce-expedited-order-email. Fire up your favorite text editor or IDE and then create a blank PHP file named woocommerce-expedited-order-email.php. Now add this code to the file and save:

 * Plugin Name: WooCommerce Custom Expedited Order Email
 * Plugin URI: https://www.skyverge.com/blog/how-to-add-a-custom-woocommerce-email/
 * Description: Demo plugin for adding a custom WooCommerce email that sends admins an email when an order is received with expedited shipping
 * Author: SkyVerge
 * Author URI: http://www.skyverge.com
 * Version: 0.1
 * License: GNU General Public License v3.0
 * License URI: http://www.gnu.org/licenses/gpl-3.0.html

if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

If you’ve never done this before, congrats on creating your first WordPress plugin πŸ™‚ We won’t cover much else about creating WordPress plugins in the rest of this article, so if you want to learn more about that, the best place to start is Writing a Plugin in the WP codex. Finally, browse to Plugins page in the WordPress admin and activate the plugin. Wheee, it’s aliiiveeeee! Sadly it doesn’t do anything yet. Deactivate the plugin and read on.

WooCommerce loads all the default emails inside the WC_Emails class. These class names are run through a the woocommerce_email_classes filter, which allows us to add our own custom email class, or even remove default ones that aren’t needed. We’re going to add a custom one using a simple function:

 *  Add a custom email to the list of emails WooCommerce should load
 * @since 0.1
 * @param array $email_classes available email classes
 * @return array filtered available email classes
function add_expedited_order_woocommerce_email( $email_classes ) {

    // include our custom email class
    require( 'includes/class-wc-expedited-order-email.php' );

    // add the email class to the list of email classes that WooCommerce loads
    $email_classes['WC_Expedited_Order_Email'] = new WC_Expedited_Order_Email();

    return $email_classes;

add_filter( 'woocommerce_email_classes', 'add_expedited_order_woocommerce_email' );

Add this code to your woocommerce-expedited-order-email.php after the above section. This simply adds our custom email class to the list of emails that WooCommerce loads. Note that if you tried to activate and run the plugin, you’ll get a fatal error because we haven’t actually created our custom email class yet. If you don’t quite understand how this works, read this article on using filters.

Now the real fun begins! We’re going to build our custom email class that will handle the actual email content as well as the trigger for when it should be sent. Create a new folder named includes inside the existing woocommerce-expedited-order-email folder and add a new blank PHP file named class-wc-expedited-order-email.php. Add this code which will act as a very basic skeleton:


if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

 * A custom Expedited Order WooCommerce Email class
 * @since 0.1
 * @extends \WC_Email
class WC_Expedited_Order_Email extends WC_Email {

} // end \WC_Expedited_Order_Email class

This code creates a simple email class that extends the abstract WC_Email, which gives us access to all of the parent email class methods & members. We’ll override some of them to make the class specific to our needs. Note that the following code should all go inside the class we just created above. Let’s start with the constructor:

     * Set email defaults
     * @since 0.1
    public function __construct() {

        // set ID, this simply needs to be a unique name
        $this->id = 'wc_expedited_order';

        // this is the title in WooCommerce Email settings
        $this->title = 'Expedited Order';

        // this is the description in WooCommerce email settings
        $this->description = 'Expedited Order Notification emails are sent when a customer places an order with 3-day or next day shipping';

        // these are the default heading and subject lines that can be overridden using the settings
        $this->heading = 'Expedited Shipping Order';
        $this->subject = 'Expedited Shipping Order';

        // these define the locations of the templates that this email should use, we'll just use the new order template since this email is similar
        $this->template_html  = 'emails/admin-new-order.php';
        $this->template_plain = 'emails/plain/admin-new-order.php';

        // Trigger on new paid orders
        add_action( 'woocommerce_order_status_pending_to_processing_notification', array( $this, 'trigger' ) );
        add_action( 'woocommerce_order_status_failed_to_processing_notification',  array( $this, 'trigger' ) );

        // Call parent constructor to load any other defaults not explicity defined here

        // this sets the recipient to the settings defined below in init_form_fields()
        $this->recipient = $this->get_option( 'recipient' );

        // if none was entered, just use the WP admin email as a fallback
        if ( ! $this->recipient )
            $this->recipient = get_option( 'admin_email' );

This sets up the email and adds a title and description that’s displayed under WooCommerce > Settings > Emails. It also sets a default heading/subject for the email and defines the templates used when sending the email. Much of these defaults are the same as the default new order email. Finally, it adds the triggers for when the email should be sent. In our case we’re simply sending the email when an order moves from the “pending”/”failed” to “processing” order status. Let’s add the trigger method now, which is where the email is actually sent:

     * Determine if the email should actually be sent and setup email merge variables
     * @since 0.1
     * @param int $order_id
    public function trigger( $order_id ) {

        // bail if no order ID is present
        if ( ! $order_id )

        // setup order object
        $this->object = new WC_Order( $order_id );

        // bail if shipping method is not expedited
        if ( ! in_array( $this->object->get_shipping_method(), array( 'Three Day Shipping', 'Next Day Shipping' ) ) )

        // replace variables in the subject/headings
        $this->find[] = '{order_date}';
        $this->replace[] = date_i18n( woocommerce_date_format(), strtotime( $this->object->order_date ) );

        $this->find[] = '{order_number}';
        $this->replace[] = $this->object->get_order_number();

        if ( ! $this->is_enabled() || ! $this->get_recipient() )

        // woohoo, send the email!
        $this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );

This is where the magic happens! This method passes in the Order ID, which we use to instantiate a new WC_Order. With this object we can then check if the shipping method is expedited. In this example, simple flat rate shipping is used, but you could just as easily check the shipping ID if FedEx/UPS was being used. If the shipping method isn’t expedited, we return which prevents the email from being sent. We’re not quite finished with the class yet, we still need to define the content that is used for the email:

     * get_content_html function.
     * @since 0.1
     * @return string
    public function get_content_html() {
        woocommerce_get_template( $this->template_html, array(
            'order'         => $this->object,
            'email_heading' => $this->get_heading()
        ) );
        return ob_get_clean();

     * get_content_plain function.
     * @since 0.1
     * @return string
    public function get_content_plain() {
        woocommerce_get_template( $this->template_plain, array(
            'order'         => $this->object,
            'email_heading' => $this->get_heading()
        ) );
        return ob_get_clean();

These are copied from the default Admin New Order email because we’re using the same exact template. If you were using a custom template, these methods are helpers to pass whatever data you need to the custom template. Note here they are passing the order object and the email heading string.

Finally we need to define the settings for the email, so an admin can easily change the recipients or subject/headings, or disable the email altogether. This code is again copied from the default Admin New Order email class:

     * Initialize Settings Form Fields
     * @since 0.1
    public function init_form_fields() {

        $this->form_fields = array(
            'enabled'    => array(
                'title'   => 'Enable/Disable',
                'type'    => 'checkbox',
                'label'   => 'Enable this email notification',
                'default' => 'yes'
            'recipient'  => array(
                'title'       => 'Recipient(s)',
                'type'        => 'text',
                'description' => sprintf( 'Enter recipients (comma separated) for this email. Defaults to <code>%s</code>.', esc_attr( get_option( 'admin_email' ) ) ),
                'placeholder' => '',
                'default'     => ''
            'subject'    => array(
                'title'       => 'Subject',
                'type'        => 'text',
                'description' => sprintf( 'This controls the email subject line. Leave blank to use the default subject: <code>%s</code>.', $this->subject ),
                'placeholder' => '',
                'default'     => ''
            'heading'    => array(
                'title'       => 'Email Heading',
                'type'        => 'text',
                'description' => sprintf( __( 'This controls the main heading contained within the email notification. Leave blank to use the default heading: <code>%s</code>.' ), $this->heading ),
                'placeholder' => '',
                'default'     => ''
            'email_type' => array(
                'title'       => 'Email type',
                'type'        => 'select',
                'description' => 'Choose which format of email to send.',
                'default'     => 'html',
                'class'       => 'email_type',
                'options'     => array(
                    'plain'     => 'Plain text',
                    'html'      => 'HTML', 'woocommerce',
                    'multipart' => 'Multipart', 'woocommerce',

You can add your own settings here by following the same array format as above. And with that, we’re finished! Head over to the Plugins page and click “Activate”. Assuming you haven’t made any errors, you should see “plugin activated”. Go to WooCommerce > Settings > Emails and you should see our new email added:


Woohoo! There’s a lot more to the WooCommerce email system and I encourage you to read through the different email types on the WooCommerce repository to more fully understand them.

The completed plugin from the walkthrough above is available on Github, or you can just download a zip.

Have questions or ideas for a custom email? Let us know in the comments!

Published by Max Rice

Max is one of our co-founders, CEO, resident webhook expert, and coffeescript lover. He's a top WooCommerce contributor, unit test aficionado, survivor of coding with timezones, and spends much of his time being the chief bottleneck at SkyVerge.


  1. Great post and perfectly timed. Next week I am changing Subscriptions to move away from filtering core emails to providing customisable renewal order emails. I’ll be using the WC Email API so will follow along closely with this tutorial to make that change easier. Thanks for publishing!

  2. This is just what I need to get my emails working for my WooCommerce Auction plugin. I will defiantly be trying this out. Thank you.

  3. Max, thanks a lot, I was having a really hard time figuring out how the e-mails we’re triggered. Thank you so much!

  4. Hi There!
    Thanks for this code, I think I can work with it. However i’m a little bit stuck. I want it to send an email when the option local delivery is chosen. (using the plugin that downloadable, and then edditing the file). Would appreciate it a LOT!

    • * changing Three Day Shipping’, ‘Next Day Shipping’ to ‘local delivery’
      I think it’s a problem because it’s not a shipping class but a shipping method; how should I adress that otherwise?

      • The get_shipping_method() should still populate with “local delivery” or similar so you can use the same code. Try doing var_dump( $this->object->get_shipping_method() ); inside the trigger method to find out what the local delivery method name is. You could then return if the shipping method isn’t equal to that.

  5. How can I style the order payment information on the customer order confirmation email?

    I think the order payment is called by these lines of code:

    Any help is appreciated. Thanks.

  6. thanks for this xtreme email.
    can you tell me this plugin working on pending order?

  7. What does this piece of code do? And what would happen if it was removed?

    // bail if shipping method is not expedited
    if ( ! in_array( $this->object->get_shipping_method(), array( ‘Three Day Shipping’, ‘Next Day Shipping’ ) ) )

    • That line simply causes the email not to be dispatched if the shipping method is not expedited. It could certainly be removed, and then the email would be sent regardless of the shipping type.

      • Hi,..

        hmm.. i try to remove this line but mail wasnΒ΄t send. I need custom mail template to be send on seperate e-mail when new order is placed. I find where to change mail template – now i just need mail to be send regardles of the shipping type.

        Is there any way that plugin can send mail regardless of the shipping type?

        best regards, Johnny

        • You can use the sample code to dispatch an email regardless of shipping type by removing the lines 16-18 within the function named trigger( $order_id ) above

  8. Great post! I have never messed with the php code and it was fun to begin learning via your post. You wrote it well and it was easy to follow.

    I successfully created your plugin with a slight modification… I changed it to “Local Pickup” and everything worked. However, the email is sent to me rather than to the customer. I would like the customized email sent to those customers who choose local pickup. (Basically, they are picking up the candy from my house and I would rather not send every customer my address and cell phone number but only those who request local pickup.)

    What lines of code do I need to change and where?

    • Hi Kevin, you’ll want to override the customer processing order template (in woocommerce/templates/emails/customer-processing-order.php) and make your modifications there. Here is a good reference for how to override the template files πŸ™‚

  9. Hi,

    I think I may have finally found what I need after searching around for days!

    Background is I have worldwide ‘vendors’ all going to be fulfilling orders from their local region. I’m using Table Rate shipping to set 4 different shipping methods for 4 ‘zones’. So all European countries get a ‘Europe’ shipping method. Asian countries get ‘Asia’ etc. All of this is handled by Woocommerce’s Table Rate Shipping.

    However I then need order emails to be routed depending on these shipping methods. So each order notifies the correct vendor. Looking to adapt your code to setup rules. So all Orders from customers within Asia notify the Asian vendor for them to complete.

    I was originally looking at http://www.woocommerce.com/products/advanced-notifications/ and wondering if I could adapt the filters/rules somehow to include the customers delivery address. But this seems to focus only on the product rather than the order details.

    Do you think I’m in the right place? Thanks, Ash

    • Hey Ash, I know this response is coming a tad late, but it sounds like you’re in the right place. You may well be able to adapt the Advanced Notifications plugin (I can’t say for sure as I’ve never really used it and don’t know what it offers in terms of customizations). Alternatively, you could use some of the core WooCommerce email filters, such as 'woocommerce_email_recipient_customer_processing_order' to modify the email recipients based on the shipping address of the current order.

  10. First of all I want to thank you for posting this information and helping people not as expert in these areas. Considering this publication would like to know if you can tell me if it is possible and how to customize the emails and further that some specific according to the type of product delivered. Ie if you buy a downloadable product receiving a confirmation email to another different one downloadable product but less expensive. Thanks for your help so valuable.

    • Hi, unfortunately there’s no easy way to customize emails based on the products present in the order without a lot of intense customization.

  11. Hi, nice tutorial. Is it possible to do something similar with “low in stock” notifications? They are sent only to admin email, I need to have them sent to another email.



    • Hey Massimo, doing this doesn’t even require anything so complex as creating a custom email type actually. If you simply want that same “low stock” notification sent to other or additional emails, you can configure this in WooCommerce by going to WooCommerce > Settings > Inventory and setting the “Notification Recipient” to a comma-separated list of emails, ie “justin@example.com, massimo@example.com” etc

  12. Hi! It’s possible to add a custom e-mail to custumer when the admin change the order to cancelled?

    • Hey Alex, it’s definitely possible, and you can reuse all the code from the article above, basically just changing the email “trigger” from 'woocommerce_order_status_pending_to_processing_notification' to the 'woocommerce_order_status_cancelled' action, and then creating the appropriate text in your email templates.

      • Hi. Thank you for the tutorial! I was looking for a way to create custom email to send to the customers when their order been cancelled.
        I changed the trigger but it does not send the email to customer when I change the order status from pending to cancelled.

      • Hi Justin. I am looking to do the same thing as alex. but this is not working. Its not sending the email to customers when I change the status to cancelled.

        • Ok, turns out there is actually more to this than just swapping the action out. The first thing is removing the lines that look for the expedited shipping method, so delete:

          // bail if shipping method is not expedited
          if ( ! in_array( $this->object->get_shipping_method(), array( 'Three Day Shipping', 'Next Day Shipping' ) ) )

          The other more challenging piece is that the action we were using to register the email ('woocommerce_email_classes') is fired *after* the order status update action ('woocommerce_order_status_cancelled') so our email isn’t hooked up in time to be fired. The solution isn’t too complex (and is borrowed from WooCommerce core) but it seemed like it would be easier to provide a new sample plugin rather than trying to stuff all that code in a comment, so enjoy!


          • i have a plugin that allows custom order statuses. I creadted one for “shipped”. Can this cancelled one, be edited to use that shipment status? For example, if I went through and used “shipped” instead of “cancelled”, wouldn’t it essentially do the same thing?

          • It would get you close, but you still need to init the mailer with your custom status change as I mentioned in the comment here πŸ™‚

          • Hi Justin
            Thanks for this great tutorial. I took your zip file above for cancelled order email notification and changed it for “Failed” order notifications.
            For some reason, it does not work. I am triggering off the “woocommerce_order_status_failed” hook like so:
            add_action( 'woocommerce_order_status_failed', array( $this, 'trigger' ) );

            I see Failed Orders in the Admin Dashboard, but I do not receive any emails. I changed the recipient info like this:
            $this->recipient = get_option( ‘admin_email’ );

            Any ideas?

          • I also tried using the zipped ‘cancelled order email’ plugin without success. This is 2 years old so maybe it is just out of date..

            I did some trouble shooting and the constructer for the class is executing, but the ‘trigger’ never triggers…

            Any help appreciated! Learning my way to guru status…

          • Following up on my own question. I used the following to register the email trigger, replacing the registration in the zipped plugin. Working for me, not 100% sure why!

            * Register “woocommerce_order_status_cancelled” as an email trigger
            add_filter( ‘woocommerce_email_actions’, ‘so_25353766_filter_actions’ );
            function so_25353766_filter_actions( $actions ){
            $actions[] = “woocommerce_order_status_cancelled”;
            return $actions;

  13. Hi,

    Thanks for the awesome tutorial!

    I would like to send an email when a custom event happen (I make a verification process for registered user, and add a new user meta, is_verified). I want to send email when user is verified (is_verified changes from 0 to 1). I’m pretty new with WordPress & Woocommerce. I just read some references, is what I need to register a new hook when is_verified is changed (and change the trigger afterwards)? I know maybe it’s a little bit our of topic, but could you give me any guidance on this?


    • Hey Fransiska, good question. You can do this pretty easily with a custom action by adding something like the following in your verification process: do_action( 'user_is_verified' ); and then in your email class you can use add_action( 'user_is_verified', array( $this, 'trigger' ) );. There’s other ways you could go about this, but that should be fairly straightforward

    • Great plugin!
      1) I tried doing this myself and ftp’ing the files over, but it didn’t pick up on setting -> woocommerce -> emails, it worked fine when I installed via the plugin option. I was just wondering since I’m new to WP.

      2) Also, once I save the “cancelled order”, the screen blanks out. Is there a way that can be fixed?


      • Hey Jason – were you using the .zip when you uploaded via FTP? You’ll upload a .zip through the WP admin, but must use the folder if uploading via FTP.

        As for the cancelled order issue, this sounds like something may have been copied over wrong. Try deleting and reinstalling the plugin and let me know if that solves it.

        • I FTP the files after I unzipped it locally to the appropriate folders, but it didn’t show up on my WP admin plugin screen or the setting ->emails. It works fine when I upload via plugin screen, just trying to see if I can install this without the plugin option.

          This screencast http://screencast-o-matic.com/watch/c2fUf5nwh6 shows before and after activating the cancel plugin. Before, I was able to save my emails and it would revert back to the original screen, but after activating the plugin, the screen will be partially blank after the save.

          Thx for getting back to me.

          • You’ll want to make sure the entire plugin was uploaded properly as this looks like it’s not even activated (you should see a “Expedited Order” email option when it’s activated properly).

    • Hi Stephen
      I was able to get the code to work. I found out with the help of a WooNinja that the hook I was using not a standard hook. Once I used the right hook, it all started working nicely.
      Just like you, the “trigger” was never being invoked for me either, until I replaced it with the correct hook. Now it works fine!
      I have my own “Failed Order Notification” plugin.

  14. Hi, great guide.
    i notice to you and other the filter woocommerce_resend_order_emails_available that you use to add your custom email to email available inside order in admin.
    So admin could also re-send the email. πŸ˜‰

    • Max, I’m using the cancelled plugin you included in the comment… which is activated. Thx

    • Hi Max, I installed the cancelled plugin you provided in the comments, its the last “tab” in email.

    • @zauker, how do you use this filter? I am having trouble getting this to work.


  15. Hi there…

    Thank you for the post. It is helpful, but I’m not sure I need to go to this far, I simply want to pass the coupon code through to the notification email template. For example…If someone uses coupon code DISCOUNT10 and receives $10 off their purchase, I would like there to be a discount line item that identifies the coupon code and the discount. Right now that isn’t coming through. Is there a trigger that accomplishes that?

    Thanks in advance for any help you can give.

    • Hey Erin, you’re correct, you don’t need to create an entire custom email class to simply add the coupon code used. Rather you follow an approach similar to what we describe in our Add Payment Type to WooCommerce Admin Email. With some obvious differences: you’re looking for $is_admin_email to be false, and you’re wanting to spit out the coupons codes. Now, the total order discount amount is already included in the new order email, so if you just want to get the names of the coupons used you can do something like implode( ', ', $order->get_used_coupons() ) If you want to take it further and actually add in a coupon line item you’ll want to look at the woocommerce/templates/order/order-details.php template, and specifically using the 'woocommerce_order_items_table' action to mimic what’s being done for line items in that template, with something similar for coupon codes. Hope this helps to get you startarted!

  16. Hello Justin – thank you for putting up this guide – it has been invaluable to me while I try and finish a new woocommerce plugin I have been working on for some time.

    I have created a custom order status and would like to trigger the email to send when the order status is changed to the custom status but for some reason add_action( ‘woocommerce_order_status_my_custom_status’, array( $this, ‘trigger’ ) ); doesn’t seem to work – do I need to add anything else to enable this trigger?

    Any help would be gratefully received

  17. Hi,
    Great tutorial.
    I have a problem, I have just upload the complete plugin to my site and when I go to the email option of woocomerce and change this plugin properties I get this error:
    Fatal error: Cannot redeclare class WC_Expedited_Order_Email in ../wp-content/plugins/woocommerce-expedited-order-email-master/includes/class-wc-expedited-order-email.php on line 171.

    I have the las woocomerce update.

  18. Hi!

    I want to be able to have an email sent to me when a user’s profile shipping address has been updated. How would I go about doing this? Thanks for your help!

  19. Hi,
    Thanks for this great tutorial.
    I have noticed that this donΒ΄t work on the new actualization of woocomerce.
    Do you know how to solve this problem?

  20. Hi,

    Great article. Very easy to follow.

    I would also like to have woocommerce set up so that existing emails that go to my customers only – ones generated when orders are changed from pending to processing, and ones generated when going from processing to completed – are also sent to me. Right now these emails are only going to my customers. Can your plugin (with modifications) help or is there a setting in woocommerce that can set this up for me.

    Thank You,

    • Hey Angelo, you’ll need some custom code to do this. One way would be to make use of the ‘woocommerce_email_recipient_(email id)’ actions that are provided by every email. You’d have to look up the id for all the emails you want to include yourself on (found in woocommerce/includes/emails/). So for the Processing Order email, which has an id of customer_processing_order you’d add something like the following to your theme’s functions.php:

      add_filter( 'woocommerce_email_recipient_customer_processing_order', 'email_include_admin' );
      function email_include_admin( $recipient ) {
      return $recipient . ',admin@example.com';

      Using your real email address of course in place of the sample one. You’d need to have a similar snippet for every email type that you wish to do this with. Hope this helps!

      • Great stuff. I will check it out and report back.

        Thanks for taking the time to answer my question.


  21. hello i like the work you have put in to help people but i was asking is there a plugin i can use to alert the admin of an order has pending through sms and also any plugin that is like flashes on your screen of an incoming order

    • Hi there, you could use the Twilio extension for WooCommerce to send SMS alters for any incoming orders and changes in order status, so that may solve both issues for you if you’re okay with using SMS entirely. I’m not aware of anything that can push notifications on your desktop for new orders, sorry!

  22. Bless you. But, all I can say is “what a complete and total nightmare”. Apparently, woocommerce does not realize that I am not interested in wracking my brain for 20-40 hours to leave newbie level… and figure this out.

  23. If anyone could be a plugin to easily customize woocommerce e-mail templates for the not tech savvy that would be great.
    I myself am savvy enough to install wordpress and run woocommerce and do some hacks… but the woocommerce e-mail templates are awful and so difficult to customize with all those hacks and filters… doing so and some kind of integration with mailchimp would be perfect…
    Business idea? πŸ˜›

    • Heh, we’ve got an idea for an extension that would make it easier to customize the emails but it’s still in the planning stages, stay tuned πŸ™‚

  24. hi there. I’m wondering how can I send an email based on the shipping country… Only if shipping to a certain country then end an email.

    • I just found the the following piece of code to add an additional email to send to on completed orders, im thinking i could use it and place a conditional for shipping country in there…seems logical, maybe your plugin is overkill for what I want to do?:

      add_filter( ‘woocommerce_email_recipient_customer_completed_order’, ‘your_email_recipient_filter_function’, 10, 2);

      function your_email_recipient_filter_function($recipient, $object) {
      $recipient = $recipient . ‘, me@myemail.com‘;
      return $recipient;

      • This code adds another recipient to the customer complete order email, which you can do through the WC Email settings page as well πŸ™‚

    • The $this->object represents the order object, so you can check the shipping country there and send (or not send) the email, like:

      if ( 'US' == $this->object->shipping_country ) {

      // etc

      • I am trying to get this to work with your plugin so I replaced your conditional

        // bail if shipping method is not expedited
        // if ( ! in_array( $this->object->get_shipping_method(), array( ‘Three Day Shipping’, ‘Next Day Shipping’ ) ) )
        // return;

        with what you suggested above but modified with a NOT

        if ( ! ‘CA’ == $this->object->shipping_country ) {

        But the email still sends even if the country is CA.

  25. Hi, I am wondering if this can be modified to send out an email when a particular payment method is used, for example if cash on delivery is chosen I would like an email to be sent to a particular email address.

    • You mean in addition to admin new order email that’s sent? It might be easier to just change the address that email is sent to instead.

  26. hi,
    tank’s a lot for this Post.
    i need help.
    how to send SMS to Customer after check out ?
    i need to read phone number at billing form and replace this :

    $client = new SoapClient(‘’, array(‘encoding’=>’UTF-8’));
    $result = $client-> __soapCall(‘Sendsms’,(array(
    ‘username’ =>’hdn’,
    ‘password’ =>’*******’,
    ‘reciver’ =>’09157047452′,
    ‘text’ =>’text’,
    ‘sender’ => ‘30009900903786’

  27. hi
    tank’s a lot.
    my problem is solved!
    and i need your help again. πŸ™‚
    how to send mail to customer and me( administrator of shop site ) after pay via payline ( this is a plugin for pay online )

  28. Hi, If you have this error
    Fatal error: Cannot redeclare class WC_Expedited_Order_Email in ../wp-content/plugins/woocommerce-expedited-order-email-master/includes/class-wc-expedited-order-email.php on line 171.

    You need to change:
    require( ‘includes/class-wc-cancelled-order-email.php’ );
    require_once( ‘includes/class-wc-cancelled-order-email.php’ );

    Tks for this plugin

  29. This is a great plugin. I’m just wondering how to make it detect if products from a specific category are in the cart, and if so, send an email to admin. I know product categories are accessible from the cart, but it doesn’t appear this data is passed to the checkout page.

    As a workaround, I set up the WooCommerce Conditional Content plugin to detect the condition, but I need it to change a global variable (or field in the shipping array) to true, which this Expedite Order Email plugin can query as the trigger.

    Any help on how to go about doing that would be greatly appreciated.

    • Product categories are indeed available after checkout when you use $order->get_items() and $order->get_product_from_item(). The $product object has a get_categories() method you can use in the plugin to check if the items in the order have whatever categories you’re interested in and send the email based on that πŸ™‚

  30. Hi! Thanks for this plugin, i was looking for a while for it. I’m writing a custom plugin which will send out custom emails to the users and i want to use the same email template as Woocommerce for consistent look & feel.

    Is it possible to somehow trigger the email so i can send it from a custom function?


  31. I have two custom order status, I send another email based on status. my problem is that I want to create an email template (not overwrite).

    I tried specifying the “default path” or “plugin path” to the function “wc_get_template” example:

    wc_get_template( “emails/customer-custom-notification-order.php”, array(
    ‘order’ => $this->object,
    ’email_heading’ => $this->get_heading(),
    ‘sent_to_admin’ => false,
    ‘plain_text’ => false
    ), ‘/wp-content/plugins/myplugin/woocommerce/’, ‘/wp-content/plugins/myplugin/woocommerce/’ );

    or using the filter “woocommerce_locate_template” :

    if ( $template_name == ’emails/customer-custom-notification-order.php’ ) {
    $template = “/wp-content/plugins/myplugin/woocommerce/emails/customer-custom-notification-order.php'”;
    return $template;

    but always get the message that the template is not found.

    • Try using a blank string for the 3rd parameter and the full path to your plugin’s template directory (using untrailingslashit( plugin_dir_path( __FILE__ ) )) as the 4th parameter.

      • Hi max! thanks for answering… I try but I have not been successful πŸ™

      • I have done a test, I wrote this in the constructor:

        echo wc_get_template( $this->template_html, array(
        ‘order’ => $this->object,
        ’email_heading’ => $this->get_heading(),
        ‘sent_to_admin’ => true,
        ‘plain_text’ => false
        ), untrailingslashit( _EM_ABSPATH_ ),
        untrailingslashit( _EM_ABSPATH_ ));

        (_EM_ABSPATH_ contains the path of my plugin)
        and the result was that he found the template because it prints the template, but in the form field says not found :/

  32. […] addition to the standard email types, you can add completely new and custom emails, though this is considered an advanced […]

  33. Hi, really tks for your tutorial~~

    I want to add email notification to pending email, that’s say, a New Order Email to Customer
    I try the plugin you paste in the comment (the cancel email zip), and simply change the action to woocommerce_order_status_cancelled to woocommerce_order_status_pending

    but it didn’t work ? should I change something else?? I’m using WC 2.1+

    • Your overall approach sounds correct, so if it’s not working out then it’s something to do with your environment or setup. Unfortunately without actually digging into your server I couldn’t really speculate on what that might be. Sorry about this!

      • Hi there,

        plugin works perfectly for the ‘woocommerce_order_status_pending_to_processing_notification’ hook. I’ve also tried to change the hook. Like Jeffrey I also tried to use the ‘woocommerce_order_status_pending’ hook to trigger the email to be sent. Doesn’t work for me either. I don’t think the ‘pending’ state goes through the update_status function? I tried to replace it with a few other hooks ( e.g. ‘woocommerce_checkout_order_processed’ – found in includes/class-wc-checkout.php: **do_action( ‘woocommerce_checkout_order_processed’, $order_id, $this->posted );** ). This also didn’t work. When I use this particular hook ( woocommerce_checkout_order_processed ), for instance in functions.php and use a placeholder/test wp_mail function, the mail is sent. Would the $order_id maybe not be passed at the right ‘stage’ using this hook? Any insight would be very much appreciated!

  34. Hi, very nice tutorial.
    I need a plugin where I want to send plain text notification email to a specific email(admin’s) with the subject is the billing phone number.
    Furthermore, I want to combine this plugin with android app SMS Gateway (https://play.google.com/store/apps/details?id=eu.apksoft.android.smsgateway) where the app scan an email and forward the message received through SMS to the phone number provided in the email subject.
    The final product should be functions like Twilio SMS notification, but the sender number would be my own phone number.
    Anyone here can help where to start?

  35. Hi guys,

    This plugin looks incredible and as per the others, I’m wondering if I can use this in my unique situation: when a customer’s order fails (payment issue, etc.), an email gets sent out.

    After diligently reading through the comments, it seems that the cancelled order email in the .zip file above and in response to Alex is a good starting point.

    How do I go about sending an email to the main contact address. I’m imagining the class is “woocommerce_order_status_failed”, and then I’d just need to change some headings around? Any help would be incredible!

    • Hey, you could definitely adapt this to send out an email when a payment fails. The “woocommerce_order_status_failed” action should do the trick. When you say main contact address, do you mean the shop admin email? If so you’d want to use get_option('admin_email') for the recipient on line 33 above: $this->recipient = $this->get_option( 'recipient' );

  36. Great article! We have several users managing orders and need a way of notifying them all when an order is marked as “processing” and when it is “complete”. Can your code be adjusted to do this? Would be wonderful! Many thanks for your help.

  37. Justin, thanks for this! Is there a way to add the custom email to the Order Actions metabox in the order admin, so I can resend the email? I’ve modified the plugin to send for completed orders but with attachments. There’s a class that handles this (WC_Meta_Box_Order_Actions) but I’m not sure how to use it in this context.

    Any help is appreciated. Thanks.

  38. Hello, great plugin! I also would be interested in getting this to work on custom order statuses…however, I’m new at this and don’t know how to init the mailer. Any advice / tutorials out there? I’m having a hard time finding instructions anywhere…

    Thks in advance!

  39. HI,
    nice plugin. where to change that the email is only sent on fast delivery orders?
    I want to sent an email to vendor on each order.
    This email should be styled different to the customers email.

    • Hey there, look at the part where we set the shipping methods to use:

      // bail if shipping method is not expedited
          if ( ! in_array( $this-&gt;object-&gt;get_shipping_method(), array( 'Three Day Shipping', 'Next Day Shipping' ) ) )

      We use ‘Three Day Shipping’ and ‘Next Day Shipping’, but you can use your own methods. If you want to style the email different, you’ll have to override the email template (take a look at the bottom of the settings page).

  40. // bail if shipping method is not expedited
    if ( ! in_array( $this->object->get_shipping_method(), array( ‘Three Day Shipping’, ‘Next Day Shipping’ ) ) )

    How to change to sent on every email, as a vendor notification email?
    I have german and english “Three Day Shipping” and german delivery times how could i manage that?

    thanks Tobias

    • If you want to send every new email to a vendor, just add their email to the list of emails under “Emails > New Order”. If the shipping method is defined in German, you’ll need to change the name of the shipping method to the German name when you search for it in the array. If it’s in German and English, you may want to create this plugin twice with different names – one for German, one for English.

  41. I’m in a bit of a pickle. My client’s site is set up so that billing info/customer profile is entered in the registration screen. I’ve been using this example, mixed a bit with the class-wc-email-customer-new-account.php class to try creating an email sent to the admin when someone registers on the website containing all of the extra information. I’m SO close, yet so far away. Everything seems to fire off ok, an the email is sent to the right address when someone registers. The problem is I can’t seem to get any values other than $user_login to pass to the new template (based upon the customer-new-account.php template). I’m not skilled at this sort of coding at all, so it’s been a struggle. Any insights would be helpful. Thx.

    • If you have the user ID, you can get access to the custom fields using get_user_meta(). I’d assign the custom fields to a few variables before passing them to your template πŸ™‚ Good luck!

  42. Hello,

    I want to add fields to the New Order section of email settings. How can I achieve this?

  43. I am looking into creating a custom email template that will send an email to a customer when the order status is “processing” if they have purchased a product that is in the product category of “waiting list”. And then I would like to send a second email when the order status is “completed” if the purchase was from the product category of “waiting list,” which would be sent when they have been accepted from the waiting list and can now purchase. It would also be ideal to be able to refrain from sending the default emails for processing and completed to the waiting list customers, but send the default emails for processing and completed for regular purchases! I might be getting myself into a bit of trouble! Is any of this possible with the help of your plugin? Thanks for any help you can provide!

  44. I have had good luck customizing your plugin for my needs! I needed to send an email to someone who bought a product from a certain product category. I modified the code to read:

    // bail if product category is not waiting-list
    if ( is_product_category(‘waiting-list’) )

    That will send an email to the customer if the product they bought is in the waiting-list category. If I want to change the class for customer-processing-order to NOT send an email to the same person who just signed up for our waiting-list would it be:

    // bail if product category is waiting-list
    if ( ! is_product_category(‘waiting-list’) )

    • Hey Caitlin, this is actually going to have to be more complicated. You’ll have to loop through the order line items, then check the product category for each product as a line item. The is_product_category() checks if you’re on a category archive page, not for the item’s category. You’ll have to check the terms for each product in the order for a product_cat that matches your category.

  45. Great post, thanks! I’m modifying the script so that it sends an email when the status of an order is changed to refunded. However, I can’t find an action hook that is triggered when status is changed.

    I suspect WooCommerce only loads the email class when certain order change statuses are made. The script does work if I change from pending -> processing but does *not* if I change from say pending -> refunded using (round line 40):

    add_action( ‘woocommerce_order_status_pending_to_refunded_notification’, array( $this, ‘trigger’ ) );
    add_action( ‘woocommerce_order_status_refunded’, array( $this, ‘trigger’ ) );

    Any thoughts as to anything else I could try to trigger the class?


  46. Hi I am using this to create a refund email

    I want the trigger to be when an order status is set to refund. This example requires me to modify the trigger

    // bail if shipping method is not expedited
    if ( ! in_array( $this->object->get_shipping_method(), array( ‘Three Day Shipping’, ‘Next Day Shipping’ ) ) )

    How do I change this to say if it is not order status refund then return ?

    • Hey Guy, since we have the order ID already in this function, we can add a condition before all of this to check for the status. Something like this should get you started:

      $order = new WC_Order( $order_id );
        //bail if not a refunded order
        if ( 'refunded' !== $order->status ) {
  47. Hi there! This is a great help you are giving us here. Thank you so much.

    I am using your plugin for cancelled orders. I am trying to modify it and also use it for Approved Orders, Semi-shipped and Re-shipped. This order statuses are custom. They are generated by a plugin (woo-custom-order-statuses). I have a very difficult time, because I understood (by previous comments) that I have to figure out how the plugin is naming the custom order statuses and then trigger them via your plugin. Am I correct? And if so, could you enlighten me how am I going to figure out how my custom order statuses are called? Thank you in advance for your time and effort!

    • Hey Christina, you’re correct that you’d need to find out how they’re named, but I’m afraid that none of us are familiar with that plugin. I’d try the order status name with hyphens between words, like “pending-payment” or “on-hold”.

  48. What a great tutorial. Thanks for sharing this. I think I am trying to do something far less sophisticated. I simply want to email every customer who purchased a particular product to tell them that there is a free update available but can’t for the life of me figure out how to do that. It seems so basic, I can’t believe there isn’t a big button in the admin panel that says contact all customers who bought X. Am I missing something obvious?

  49. Thanks – this code was tremendously useful. The way the hooks work is pretty cool, must say. Needed to add new admin email recipient and it worked like a charm!

  50. Thanks for the tutorial Max. I learned quite a bit from it. I’m wondering if you might have any pointers for my scenario. I have created a plugin to store a “contact email address” for each product in the shop. When a customer checks out, I need to loop through their order and send an email to each product’s contact address.

    Does that make sense? What would be the best way to accomplish this?



  51. Hi –
    thanks for this thorough tutorial. I would like to send an Email as soon as the user hits “Pay now (via PayPal)” on the checkout page. But I just can’t seem to find a list of triggers.

    I assume I would just have to change this action in your tutorial files. Can you tell me where to look for the triggers or, even better, which trigger it is I should use?

    I basically have to send a kind of order confirmation right before the customer pays. Since I only offer PayPal I guess one action/trigger will suffice – but what do I know…

    I would appreciate if you could help me in this matter. Thank you.

    • Hey Felix, this is certainly possible, but there’s not necessarily a unique trigger to do this. My initial thought is that you’d probably have to trigger the email when the order is generated but only if the gateway is PayPal Standard (as the order will be created when that button is clicked). A bit challenging, but doable – if this isn’t something you’re familiar with, we typically recommend Codeable for these kind of jobs. Here’s a 5% off coupon we have for new clients. πŸ™‚

      • Thanks for you reply, Beka. Unfortunately I am afraid I failed to properly state the problem: It does not have to be PayPal-only. PayPal is just the only payment-method I offer. The customer has to get the Email before he pays – that’s the eart of the issue.

  52. Thanks for the post. How can I link this email to the order status so that when i select the order status and save it, it will send out the email

    • We are currently working on an Order Status Manager extension that will do just that πŸ˜‰ – you’ll be able to create a custom status and an associated email.

  53. I was wondering if there is a way to separate my email to display a different line of text, depending on the purchased products category.

    I know that’s not very clear πŸ˜›

    For example:

    I have two products. Test1 and Test2. Test1 has the category ‘ICT Sale’, while Test2 has ‘Private Sale’.
    If someone purchases Test1 I would like the email to say ‘Thank you for buying an ICT product’
    If someone purchases Test2 I want it to say ‘This is a private sale!’

    Unfortunately I’m more fluent with JS than PHP and WP functions (and I’m assuming a if / else statement won’t work here :P)

    Thanks for any help in advance!

    • Hi there, unfortunately there’s no easy way to customize emails based on the products present in the order without a lot of intense customization.

  54. I have been struggling with the email notifications to customers from woocommerce as well. I am using a plugin to add a download button on the order completed and my account page and now want to remove the download links from the customer emails. the links won’t work in the emails because the downloads are limited to one so it just is not needed in the emails any longer. Any idea how I can delete the links? I would be happy to send a different customer email to customers who purchase downloadable products but I am at a total loss for customizing any of the customer emails..

  55. Hi,

    Thanks for this great stuff ! I’m currently using your code in a plugin which sends an email to the admin with an attached file uploaded from a new my-account section. I’ve replaced the add_action in the email class by a custom one, folllowing an advice you gave lately. I’m calling this custom action with do_action() from the main class of my plugin, but nothing happens. Displaying wp_filters, I see that my action is not declared, whereas at that point, WP is likely to have launched the woocommerce_email_classes() already. I’m stuck… :/
    Any idea ?

    Best regards,

  56. Hi,

    I would like to send automated email on the date of delivery (I am using Woocommerce Delivery Date Pro plugin to enable customers to select the delivery date), informing the customers that their order is due for delivery today.

    How can I achieve this?


  57. big thanks foe that generous article.
    i am not sure if i need this plugin, need you’r advice.

    i just need the woocomerce email notification to be align to the right.

    i am using avada theme, and a Hebrew RTL word-press.
    i installed the “WP Email Template Pro” and it’s working o.k, bur emails align to the left.

    can you please give me some advice how to do it?

    thank you

  58. Hello,

    Can you please help me in woocommerce locate template?

    I tried to use your instruction as well as this post http://www.skyverge.com/blog/override-woocommerce-template-file-within-a-plugin/

    I want to have my new email template will be loaded in my plugin, like “wc-custom-email/woocommerce/emails/create-new-user.php).

    But it always show not found template, not sure why, can you please help me on that?


  59. Hi!

    I would like to modify this to send an email copy to a specific e-mailaddress when order status changes to “completed”. Can you tell where and how to modify that?

  60. Hi! Is there a way to have the email sent only if the customer belong to a define role?

    We are assigning different customers to different sales guys and using the roles as a way to identify them, so i need an email to be send to them whenever one of their customers makes a purchase.

    Thanks a lot

  61. Thanks for sharing this content.

    With this content I created a Welcome Message that works manually, but not automatically after a order is created.

    How can I trigger this welcome notification always after a order is created?

    I tried to put some actions in my plugin constructor to trigger it:

    add_action( ‘woocommerce_payment_complete’, array( $this, ‘trigger’ ) );
    add_action( ‘woocommerce_checkout_order_processed’, array( $this, ‘trigger’ ) );
    add_action( ‘woocommerce_order_status_welcome_notification’, array( $this, ‘trigger’ ) );
    add_action( ‘woocommerce_order_status_pending_to_on-hold_notification’, array( $this, ‘trigger’ ) );
    add_action( ‘woocommerce_order_status_failed_to_on-hold_notification’, array( $this, ‘trigger’ ) );

    And I tried to put in the functions.php a trigger to do it:

    add_action( 'woocommerce_checkout_order_processed', 'custom_woocommerce_update_status' );
    function custom_woocommerce_update_status( $order_id ) {
        global $woocommerce;
         if ( !$order_id )
    if ($order-&gt;payment_method == 'PagSeguro') {
        $order = new WC_Order( $order_id );
        $order-&gt;update_status( 'pending' );

    Thanks for any help in advance!

  62. This is so helpful! I’m wondering if it’s possible to send a custom email based on product category. For instance, for products in digital downloads category, customer get Email A and for products in physical product category, customer gets Email B. My developer says this isn’t possible, but it seems like it shouldn’t be so hard. Is he right? Is this impossible to do?

    • Possible, but difficult — you’d have to create two custom emails to trigger on purchase (and disable the standard invoice): setup the order object / loop through products purchased and send email A only if it contains a product in the digital downloads category, or send email B instead if there are no digital downloads. Not straight-forward or simple, not impossible.

  63. Excellent article.
    Is it possible add one specific email to one specific product. So when a customer purchases/orders Product X they receive an email that is specific to the purchase of Product X. I imagine it is possible, I’m just wondering where and how you would alter the code.

  64. Can you help with question ?
    I need to send notification to different email than the email sent from regular buyers.
    I have certain categories that buyers can access only if they are logged, the categories showing based on the buyer role, if the buyers have that role they can see the categories and order.
    I have two roles for logged users and what to send the custom email if the buyer from role one is making the buy and send the regular email for all the rest buyers.
    Can you guide my how I can get the user role and if your example can work for me?

  65. Thanks for this! The only thing it doesn’t show is how to change the ‘sender’ email address. Is there a way to do this? Any assistance would be much appreciates, as I simply can’t figure it out!

  66. Hi there!
    Guys, please, can you tell if it’s possible to add a text field in order to send a specific text to a specific customer?
    I hope so!
    Thanks in advance!

  67. how to create a refund email…tried a lot..can any one make help

  68. How to show discount generated by Dynamic Pricing plugin in Admin Order and Order Email?

  69. Is there a way to display venue information from the Event Calendar plugin into the WooCommerce emails? I’ve managed to make all the other customizations I need, but we would love to show information on the events we’re selling tickets for within these emails. Would this be at all possible?

Hmm, looks like this article is quite old! Its content may be outdated, so comments are now closed.