Mastering custom WooCommerce Order Statuses for your Online Store

Learn to create your own custom WooCommerce Statuses for your Orders.

In this tutorial we'll discover how to add our own custom order statuses, and leverage them correctly.

WooCommerce is one of the most popular e-Commerce solutions for WordPress-based websites. In this tutorial we’ll discover how to add our own custom order statuses, and leverage them correctly.

Note: For the following actions and filters, you’ll probably want to wrap them in your own custom plugin so everything is neat and tidy.


Step 1: Register a new Post Status

Since order statuses in WooCommerce are simply Post Statuses rebranded, we can leverage the register_post_status  function to add our new order status. Let’s create an ‘init’ hook and add our new custom Order Status:

add_action( 'init', function() {

    $slug = 'wc-custom-status';
    $label = 'My Custom Status';
        register_post_status( $slug, [
            'label'                     => $label,
            'public'                    => true,
            'exclude_from_search'       => false,
            'show_in_admin_all_list'    => true,
            'show_in_admin_status_list' => true,
            'label_count'               => _n_noop( $label . ' <span class="count">(%s)</span>', $label . ' <span class="count">(%s)</span>' )

Step 2: Add our new status to WooCommerce’s order statuses.

Now that we’ve registered our statuses, let’s go ahead and make sure WooCommerce recognizes them as Order Statuses. For that we’ll use the wc_order_status filter.

add_filter( 'wc_order_statuses', function( $order_statuses ) {
    $slug = 'wc-custom-status';
    $label = 'My Custom Status';

        $new_order_statuses = [
            $slug => $label

        return array_merge( $new_order_statuses, $order_statuses );
    } );

Step 3: Stock Management

Alright, we’re looking good! But, some order statuses in WooCommerce have an effect on the stock levels of the products in the order. For example, when an order is Pending Payment no stock is reduced. But when an order’s status changes to say, Processing, On Hold, or Completed, then that triggers WooCommerce to reduce stock. Conversely if an order is set to Cancelled or Pending, stock is returned back to the products.

If you wanted to add this functionality, I find that two snippets are required.

Reducing Stock:
(note that we’re excluding the ‘wc-‘ prefix for our slugs here)

add_action( 'woocommerce_order_status_custom-status', 'wc_maybe_reduce_stock_levels' );


    add_filter('woocommerce_order_is_paid_statuses', function( $statuses ){
        return array_merge($statuses, ['custom-status']);

Returning Stock:
(note that we’re excluding the ‘wc-‘ prefix for our slugs here)

add_action( 'woocommerce_order_status_custom-status', 'wc_maybe_increase_stock_levels' );


    add_filter('woocommerce_order_is_pending_statuses', function( $statuses ){
        return array_merge($statuses, ['custom-status']);

Step 4: Bulk Actions

Sometimes it can be handy to have these new status in the WooCommerce Order’s bulk actions dropdown. WooCommerce has some by default, namely the ‘Mark as ….’ options in the dropdown. To create this ourselves, we’ll need to add our own options to this dropdown, and add a handler to process the bulk-update request.

Let’s add our options first:

add_filter( 'bulk_actions-edit-shop_order', function ( $bulk_actions ) {

    $slug = 'wc-custom-status';
$action = "mark_" . $slug;
    $label = 'My Custom Status';

        $bulk_actions[ $action  ] = $label;

        return $bulk_actions;

Now we’ll have to write a handler to actually make these bulk changes. Note that it’s the $action variable from before that is used in our action hook below:

add_action( 'admin_action_mark_wc-custom-status', function() {

    $slug = 'wc-custom-status';

        // if an array with order IDs is not present, abort
        if( !isset( $_REQUEST['post'] ) && !is_array( $_REQUEST['post'] ) )  return;

        // Loop through the Post Ids and update each of them to the new status.
        foreach( $_REQUEST['post'] as $order_id ) {

            $order = new WC_Order( $order_id );
            $order_note = 'This orders status was changed by bulk edit:';
            $order->update_status( $slug, $order_note, true );


        // And it's usually best to redirect the user back to the main order page, with some confirmation variables we can use in our notice:
       $location = add_query_arg( array(
            'post_type' => 'shop_order',
            $slug => 1, // We'll use this as confirmation
            'changed' => count( $post_ids ), // number of changed orders
            'ids' => join( $post_ids, ',' ), // list of ids
            'post_status' => 'all'
        ), 'edit.php' );

        wp_redirect( admin_url( $location ) );
    } );

// Now let's add our Admin Notice!
add_action('admin_notices', function() {

        global $pagenow, $typenow;
        $status = false;

        $listeningStatuses = [

        foreach( $listeningStatuses as $listeningStatus )
            if( isset($_REQUEST[ $listeningStatus ]) && $_REQUEST[ $listeningStatus ] == 1 )
                $status = $listeningStatus;

        if( $typenow == 'shop_order'
            && $pagenow == 'edit.php'
            && $status
            && isset( $_REQUEST['changed'] ) ) {

            $message = sprintf( _n( 'Order status changed.', '%s order statuses changed.', $_REQUEST['changed'] ), number_format_i18n( $_REQUEST['changed'] ) );
            echo "<div class=\"notice notice-success updated\"><p>{$message}</p></div>";



And that just about wraps it up!

You’re now able to add your own custom statuses, and apply them to individual or bulk orders. Plus, you can now use these statuses to take or return stock from your products. Some interesting order statuses might look like:

  1. Needs Confirmation
  2. Possible Scam
  3. Sent to Dropshipper
  4. Waiting on Stock

Are there any custom order statuses that you might find useful?

Written by Shawn Wernig

Shawn Wernig

Lead Creative at Eggplant Studios

Shawn Wernig is the lead creative behind Eggplant Studios. While not full time (let's face it, more than full time) designing websites for his clients, Shawn enjoys good beer, double-doubles, and hiding from his phone.