Create a Custom Order Status
In this tutorial I am going to show you how to add a new order status in WooCommerce programmatically. I am not a big fan of using plugins when it is just enough to deal with a couple lines of code.
When registering a status, we are using register_post_status()
function which makes as think that WooCommerce order are custom post types and WooCommerce order statuses are just post statuses. Obviously.
I can prove that with a screenshot of WordPress database if you wish. Under post_status column for order post types you can find statuses like wc-pending
, wc-processing
, wc-on-hold
, wc-completed
, wc-cancelled
, wc-refunded
, wc-failed
.
Enough talk, let’s create a custom status now.
/*
* Register a custom order status
*
* @author Misha Rudrastyh
* @url https://rudrastyh.com/woocommerce/order-statuses.html
*/
add_action( 'init', 'misha_register_awaiting_shipping_status' );
function misha_register_awaiting_shipping_status() {
register_post_status(
'wc-misha-shipping',
array(
'label' => 'Awaiting shipping',
'public' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Awaiting shipping (%s)', 'Awaiting shipping (%s)' )
)
);
}
// Add registered status to list of WC Order statuses
add_filter( 'wc_order_statuses', 'misha_add_status_to_list' );
function misha_add_status_to_list( $order_statuses ) {
$order_statuses[ 'wc-misha-shipping' ] = 'Awaiting shipping';
return $order_statuses;
}
As a result you may immediately find the new status appeared on edit order page.

Now let’s break it down in details.
Let’s take a look at register_post_status()
parameters first.
register_post_status(
'wc-misha-shipping',
array(
'label' => 'Awaiting shipping',
'public' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Awaiting shipping (%s)', 'Awaiting shipping (%s)' )
)
);
wc-misha-shipping
is just an order status slug, and as you probably noticed, it should begin withwc-
, it is a requirement, not a suggestion, because some of WooCommerce functions can work only with statuses that begin withwc-
. Please also limit it to 20 alphabetical chars and dashes.public
– if you set it to false, the orders with that status won’t be displayed anywhere, even in admin.label
parameter here used without any translation function, but if you create a plugin or a theme, don’t forget to wrap it in__()
, like__( 'Awaiting shipping', 'text-domain' )
.show_in_admin_status_list
parameter set totrue
just allows your custom order status to appear here:

label_count
is kind of interesting parameter in order what it accepts in it. It allows you to set text for admin status list (screenshot above). You can only pass values via_n_noop()
function there which already makes it translation-ready. First parameter is a singular form, the second one – plural form.
Everything is clear with register_post_status()
, right? Let’s jump to the next part of the code.
Filter hook wc_order_statuses
allows you to change the array of statuses before they are going to be displayed anywhere on the website. It also allows you to remove any of the default order statuses which is not recommended and can be done only with caution. Or you can change the statuses order!
For example, if you would like our custom status to be displayed just before “Completed” status, you can use this code:
function misha_add_status_to_list( $order_statuses ) {
$new = array();
foreach ( $order_statuses as $id => $label ) {
if ( 'wc-completed' === $id ) { // before "Completed" status
$new[ 'wc-misha-shipping' ] = 'Awaiting shipping';
}
$new[ $id ] = $label;
}
return $new;
}

Please read my another tutorial where I show how to add a custom order status to bulk actions.

Misha Rudrastyh
Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.
Need some developer help? Contact me
Very Handy – thanks.
Hi Misha,
This is a great article and really helped me add some order statuses. Is there any way to get the new statuses in the ‘Bulk Action’ menu?
Hi Drew,
it looks like the idea for the new post. Currently I have no ready code. I will reply to your comment when it will be ready (I hope within a week, until Tuesday).
That would be brilliant, thanks.
Hi Drew,
Done! This is the tutorial special for you :)
Hi!
Thanks for the great tutorial. I added the source code in my functions.php of the child theme in WordPress but nothing happens, all options are still available. Is there any change to how this works in the recently released WooCommerce 3? Because I had some renamed before in my functions.php and that has stopped working as well since upgrading to WC3.
Thanks
Alex
Hi Alex,
just made a test — for me it works on WooCommerce 3.0.4
Hi, thank you for this tutorial.
Could you help me, how to I remove an order status when there are only “downloadable products” in the order.
I have a custom order status called “dispatch” which I manually trigger to go to the Logistics department once Accounts can confirm that payment was made successfully. But I do not want this status option in the backend when the order contain only downloadable products.
Thank you
Hey,
I didn’t test this code, but try it out:
hello, thank you very much for this tutorial but I have a question. let’s say I want to show certain statuses for orders that are being shipped by a certain shipping company.
for example if an order is shipped by company ‘x’ then statuses are a, b and c. if an order is shipped by company ‘y’ then statuses are 1,2 and 3. can I do that?
Hello thank you so much for amazing post. can you give me any idea when order status change to custom order status then email to customer.
Hi Misha,
Thanks for the code sample. I have added a custom shipping status and all works. Thank you.
Anyhow here is a question on statuses from a recent-edge case :
Customer had paid and status went from ‘on hold’ to ‘processing’. However the customer wanted a last minute change of order items ( no cost difference ). We would like to edit those order items but orders are only editable ‘on hold’.
I thought to work with the ‘wc_order_is_editable’ filter – and perhaps just always return true. But this is a bit hacky and I would welcome your advice. Simply changing the WC status drop-down has no effect or am I missing something?
Hi Josh,
Hmmm… I think
wc_order_is_editable
should be ok. Just in case someone else need it:Yes I thought that might be the way to go. Works well, thank you.
Hi! Great post, thanks! Is it do-able to have custom statuses not automatically be followed up by core statuses? Let’s say a order has status ‘Payment Required’ and customer wants to pay but cancels the payment. Now the order gets status ‘Cancelled’ or ‘Failed’ (core status) automatically but I want it to get a custom status like ‘Payment still Required’ or something.
Thanks for your time!
Hi,
Thank you!
Hmmm… I think you could try this hook
woocommerce_order_status_failed
or this onewoocommerce_order_status_cancelled
.Thanks for your reply.
I managed to get it to work with following hook:
Great work and passion, Misha!
I have successfully integrated your code to add a new order status.
Now, how does one create an email template and trigger the email upon updating an order status to this new order status?
Do you have a tutorial for that or could you recommend a resource?
TIA
Hey Howard,
At this moment I can not recommend you a resource, maybe I will publish a tutorial about it later.
Hi Misha! Love all your coding help that you write about.
This code used to work for me but it does not work anymore. I’m not sure if this has to do with either the WordPress updates or the plugins that I have installed. I have installed plugins that adds additional bulk actions to the drop down. I also noticed that there is a new one named “Move to Trash” which is at the top of the dropdown.
Are you able to see if this code still works for you using the most up to date WP? Thanks!
Hi and thank you for your comment!
It seems like everything is working on my WordPress 5.3.2 and WooCommerce 3.8.1.
Everything’s working perfect but i have one question … How to change status label of woocommerce orders page [header] too …
Great post as usually!
I had an issue however with setting status from cli (command line).
What I’ve noticed is that you need to “first save” new status in CMS to be able to use it in CLI
wp --user='your-user-admin-here' wc shop_order update 3719 --status='misha-shipping'
One more time, thank you a lot for share !
If you ever planning to go to Europe let me know as I own you plenty of coffes ;)
🙏
Code works on Woocommerce 7.8.2 with WordPress 6.2.2.