Woman writing in notebook while working at laptop

How to create a WooCommerce custom order status

7 min read
Beka Rice

While WooCommerce includes several different order statuses, these may not fit the flow for your shop. With WooCommerce core, you can mark orders with one of the following statuses:

  • Completed
  • Processing
  • Pending payment
  • On hold
  • Refunded
  • Cancelled
  • Failed

However, some shops need more order statuses, such as “Awaiting shipment”, “Building”, or others to fit their fulfillment needs. You can easily add these yourself with a tiny bit of custom code.

Hub Signup

Let’s go over how to both add a WooCommerce custom order status and to remove an order status.

Adding a WooCommerce custom order status

Don’t want to add custom code for this? Try WooCommerce Order Status Manager instead.

In WooCommerce 2.1 or earlier, order statuses were stored as a taxonomy for the “Order” post type. WordPress taxonomies are categorizations, such as post categories and tags. However, WooCommerce 2.2 required a change to the order structure to support partial and automatic refunds from the WooCommerce admin. Order statuses are now saved as a custom post status, just like draftscheduled, or published for posts.

Adding a custom post status necessitates a bit more code now, but this is actually a bit more straightforward from a development perspective (you don’t have to run the code once then delete it). Be sure to properly add this code to your site—I’d recommend creating a plugin or using the Code Snippets plugin.

Let’s start with the full snippet for you impatient folks. smile This will add an “Awaiting shipment” order status to your shop. I’ll then break it down to explain what each part of this snippet does.

Explanation: what does this code do?

Let’s start with the first function in this code:

// Register new status
function register_awaiting_shipment_order_status() {
    register_post_status( 'wc-awaiting-shipment', array(
        'label'                     => 'Awaiting shipment',
        'public'                    => true,
        'exclude_from_search'       => false,
        'show_in_admin_all_list'    => true,
        'show_in_admin_status_list' => true,
        'label_count'               => _n_noop( 'Awaiting shipment <span class="count">(%s)</span>', 'Awaiting shipment <span class="count">(%s)</span>' )
    ) );
add_action( 'init', 'register_awaiting_shipment_order_status' );

This function is registering our custom status as a post status in WordPress. You can rename the function to suit your needs, but we’re going to build an “Awaiting shipment” order status, so we’ve called it register_awaiting_shipment_order_status() to reflect this.

We then build the post status using the WordPress register_post_status() function. Notice that the first thing in this function is the slug for our order status, prefixed by “wc-” for “WooCommerce”. If you want a different name for your order status, you’ll want to change this to a hyphenated, all lower-cased slug, such as wc-building or wc-packing-order.

Your custom status slug can only be a maximum of 20 characters, including dashes (as per the codex on register_post_status), so you need to be aware of this when creating your own slug.

We then create an array, which is a set of data that defines our custom post status. The label is what will be displayed, so you can change that to the exact name of your order status. You’ll then also want to insert this in the label_count instead of “Awaiting shipment”, but leave the rest of this array unchanged.

Finally, the action at the end runs this function and adds this order status to WordPress as our custom post status.

Now let’s talk about the second part:

// Add to list of WC Order statuses
function add_awaiting_shipment_to_order_statuses( $order_statuses ) {
    $new_order_statuses = array();
    // add new order status after processing
    foreach ( $order_statuses as $key => $status ) {
        $new_order_statuses[ $key ] = $status;
        if ( 'wc-processing' === $key ) {
            $new_order_statuses['wc-awaiting-shipment'] = 'Awaiting shipment';
    return $new_order_statuses;
add_filter( 'wc_order_statuses', 'add_awaiting_shipment_to_order_statuses' );

The add_awaiting_shipment_to_order_statuses() function is going to add this new custom post status into the list of available order statuses within the WooCommerce “Orders” and “Edit Orders” pages so that we can actually use it. We want to pass in the current order statuses so that we can go through them and insert our order status into the list where we’d like it.

We create an array for our new order statuses first, which will be our final product. We’ll then go through all order statuses and insert our own, so that by the time we spit out the new order statuses into the list, our new one is included.

The foreach loop here goes through the list of current order statuses until we find the one we want. In this case, I’m going to insert “Awaiting shipment” after the “Processing” order status. That’s what the if() part is doing here – we say, “If the order status is ‘processing’, add in ‘Awaiting shipment’ here, then continue going through order statuses.”

Notice that I use the slug and label I created in the first function to add this order status. I’ll now have “Awaiting shipment” displayed after “Processing” for my orders:

Order status - Awaiting shipment

You can insert your new status after any of the existing statuses; you can find a list of them here. For example, I could have looked for wc-pending instead and added my status after that.

That’s it! You can now add your own status and insert it anywhere into the order status list.

Removing a WooCommerce order status

If you’d like to remove your custom order status, you only have to remove this code from your site. If you’re using the Code Snippets plugin, you can simply deactivate the snippet. If you do so, your order statuses will default to “Pending payment”.

However, orders using your new status may not appear in your orders list when the code is removed since they’re using an unrecognized post status. I’d recommend changing the order statuses to a core status before deactivating or removing this snippet. The orders will still exist regardless, and you could navigate to them directly using the post id, but they won’t be recognized in the Orders list without this code unless they use a core order status.

I’m not going to go over how to remove any of the core order statuses, because this is a very bad idea. Even if you think you may not use any of them, most plugins (especially payment gateways or other plugins that tie to orders), expect them to be there.

For example, you may not think you’ll need the “Pending payment” status, but this is actually used by most gateways as the default order status until a valid payment is confirmed. For example, PayPal or other gateways that use IPNs to notify your site that a payment has been completed use “Pending payment”, then the status is updated to processing once the payment is received. For many extensions to work correctly, these core statuses must all be available – even if you don’t actively use them in your fulfillment cycle.