GeneratorsWooCommerceCustom Order Status

Custom Order Status Generator

A new order status, wired into the admin dropdown, the orders list filters and the bulk-action menu WooCommerce builds automatically from the registered list.

awaiting-pickup-order-status.php
Saved just now
The status
wc-awaiting-pickup — 18/20 characters
This code is identical whether or not High-Performance Order Storage is on — an order's status is a plain string either way, stored in the wc- prefixed form.
Visibility
Exclude from search
Orders in this status are skipped by the admin search box.
Show in “All” orders list
Counted and listed when no status filter is applied.
Show in the status filter list
Appears as its own filter link above the orders table, with a live count.
Admin badge colour
Give it a colour in the orders list
Adds a small admin_head style block targeting the status badge markup.
<?php
// Generated with WP CodeKit — powered by GrowQuest (https://growquest.io).
/**
 * Plugin Name:       Awaiting pickup order status
 * Description:       Registers the wc-awaiting-pickup order status.
 * Version:           1.0.0
 * Requires PHP:      7.4
 * Requires Plugins:  woocommerce
 * Text Domain:       acme
 */

defined( 'ABSPATH' ) || exit;

/**
 * Register the wc-awaiting-pickup post status.
 */
function acme_register_order_status() {
	register_post_status(
		'wc-awaiting-pickup',
	array(
		'label'                     => _x( 'Awaiting pickup', 'Order status', 'acme' ),
		'public'                    => false,
		'exclude_from_search'       => false,
		'show_in_admin_all_list'    => true,
		'show_in_admin_status_list' => true,
		'label_count'               => _n_noop(
			'Awaiting pickup <span class="count">(%s)</span>',
			'Awaiting pickup <span class="count">(%s)</span>',
			'acme'
		),
	)
	);
}
add_action( 'init', 'acme_register_order_status' );

/**
 * Add it to the list WooCommerce shows everywhere — the admin dropdown,
 * the orders list filters, and the bulk "Change status to" actions.
 *
 * @param array $order_statuses Existing statuses, in display order.
 * @return array
 */
function acme_order_statuses( $order_statuses ) {
	$new_statuses = array();

	foreach ( $order_statuses as $key => $status ) {
		$new_statuses[ $key ] = $status;

		if ( 'wc-processing' === $key ) {
			$new_statuses['wc-awaiting-pickup'] = _x( 'Awaiting pickup', 'Order status', 'acme' );
		}
	}

	return $new_statuses;
}
add_filter( 'wc_order_statuses', 'acme_order_statuses' );

/**
 * Give the status its own colour in the orders list.
 */
function acme_order_status_badge_css() {
	?>
	<style>
		mark.order-status.status-awaiting-pickup,
		.order-status.status-awaiting-pickup {
			background: #f0ad4e;
			color: #ffffff;
		}
	</style>
	<?php
}
add_action( 'admin_head', 'acme_order_status_badge_css' );

About this generator

Custom Order Status Generator Online

Register a WooCommerce custom order status — Awaiting pickup, Ready to dispatch, Sent to warehouse — as a real register_post_status() call plus the wc_order_statuses filter that puts it in the admin dropdown, the orders list filters and the bulk "Change status to" actions. The wc- prefix, the _n_noop() count label and the position in the list are all handled for you.

The slug field shows the resolved status string and its length against the 20-character database limit as you type, so you find out about a truncation problem here rather than after orders have been saved with it. Free to use, no account, nothing uploaded.

Reviewed Output tested on WordPress 6.8 · PHP 8.3

Why the Custom Order Status Generator beats a hand-written register_post_status() call

A WooCommerce order status is two registrations that have to agree with each other: a WordPress post status whose name starts with wc-, and an entry in the wc_order_statuses array whose key is that same prefixed string. Get the prefix wrong in one place and the status registers but never appears; make the slug too long and MySQL truncates it in the post_status column, so the value stored on the order stops matching the value you registered. The generator writes both halves from one slug and checks the failure cases before you paste.

The wc- prefix applied once, everywhere

You type awaiting-pickup; the generator emits wc-awaiting-pickup in register_post_status(), in the wc_order_statuses key, and in the CSS selector for the badge. There is no second place to keep in sync.

A live length check against the 20-character column

The post_status column is varchar(20). A resolved status longer than that is a hard error, with a one-click fix that shortens the slug — because a truncated status is silently written to the database and then never matches what you registered.

Core statuses are refused

Re-registering wc-pending, wc-processing, wc-on-hold, wc-completed, wc-cancelled, wc-refunded or wc-failed is an error with a rename fix, because it overwrites core’s own label and count text rather than adding anything.

A properly formed label_count

The count label is emitted as a real _n_noop() with singular and plural forms and the count span markup WordPress expects, not a plain string — which is what makes the "Awaiting pickup (3)" filter link render correctly.

Exact placement in the status list

Choose which core status yours sits after and the generator writes the rebuild loop that inserts it at that point, preserving order. Choose the end and it appends with a single assignment instead.

An optional coloured admin badge

Turn it on and you get an admin_head style block targeting mark.order-status.status-your-slug, with the hex values validated. Leave it off and no CSS is emitted at all.

How does the Custom Order Status Generator work?

Four steps. Name the status, decide where it appears, colour it if you want to, then export.

  1. 01

    Name it

    Enter the label shoppers and staff see and the slug it is stored under. Pick which existing status it should follow in the list, and set the function prefix and text domain.

  2. 02

    Set its visibility

    Three toggles map straight onto register_post_status() arguments: exclude from search, show in the "All" orders list, and show in the status filter list above the orders table.

  3. 03

    Give it a badge colour

    Optional. Set a background and foreground hex pair and the generator adds the admin stylesheet block so the status reads as its own thing in the orders table rather than inheriting the default grey.

  4. 04

    Clear the checks, then export

    Fix anything flagged — a slug over 20 characters, a core status name, an invalid hex — then copy the snippet or download it as a functions.php block or a plugin file.

Worked example — an "Awaiting pickup" status after Processing

The registration and the list entry, exactly as the tool emits them. The badge CSS block is omitted here for length.

function acme_register_order_status() {
	register_post_status(
		'wc-awaiting-pickup',
		array(
			'label'                     => _x( 'Awaiting pickup', 'Order status', 'acme' ),
			'public'                    => false,
			'exclude_from_search'       => false,
			'show_in_admin_all_list'    => true,
			'show_in_admin_status_list' => true,
			'label_count'               => _n_noop(
				'Awaiting pickup <span class="count">(%s)</span>',
				'Awaiting pickup <span class="count">(%s)</span>',
				'acme'
			),
		)
	);
}
add_action( 'init', 'acme_register_order_status' );

function acme_order_statuses( $order_statuses ) {
	$new_statuses = array();

	foreach ( $order_statuses as $key => $status ) {
		$new_statuses[ $key ] = $status;

		if ( 'wc-processing' === $key ) {
			$new_statuses['wc-awaiting-pickup'] = _x( 'Awaiting pickup', 'Order status', 'acme' );
		}
	}

	return $new_statuses;
}
add_filter( 'wc_order_statuses', 'acme_order_statuses' );

This code is identical whether or not High-Performance Order Storage is enabled. An order’s status is a plain string in either backend, stored in the wc- prefixed form, so nothing here needs an HPOS-specific branch.

WooCommerce custom order statuses — frequently asked questions

What people run into when adding a status to the order workflow.

Why does my custom order status need the wc- prefix?

WooCommerce identifies its own order statuses by that prefix. Internally it strips wc- when comparing, so $order->get_status() returns awaiting-pickup while the database column holds wc-awaiting-pickup. Register the post status without the prefix and WooCommerce will not treat it as an order status at all — it will not appear in the dropdown, the filters or the bulk actions.

Why is my custom order status missing from the bulk actions dropdown?

WooCommerce builds the "Change status to" bulk actions from the array returned by the wc_order_statuses filter, and only includes statuses with show_in_admin_status_list enabled. If you registered the post status but never added it to that filter — or turned that argument off — the status exists but has nowhere to be selected from.

How long can an order status slug be?

The resolved status, prefix included, must fit in 20 characters, because post_status is a varchar(20) column. MySQL truncates anything longer without raising an error, so the value written to the order no longer matches the one you registered and the order effectively falls out of every status filter. This generator counts the characters for you and blocks the export.

Do custom order statuses work with High-Performance Order Storage?

Yes, and the code is the same. A status is a plain string column value in both the legacy wp_posts table and the HPOS wc_orders table, so register_post_status() plus the wc_order_statuses filter is all that is needed either way. What does change under HPOS is how you *query* orders by that status — use wc_get_orders() rather than a WP_Query on shop_order.

Can a custom order status trigger an email?

Yes. WooCommerce fires woocommerce_order_status_{status} on every transition, including yours, so an email class can hook woocommerce_order_status_awaiting-pickup and send from there. It also fires woocommerce_order_status_changed with the old status, the new status and the order.

Will orders in a custom status count towards reports and stock?

Not automatically. WooCommerce treats only a fixed set of statuses as paid, and reduces stock on transitions into those. A custom status is inert unless you opt it in — for example by filtering woocommerce_order_is_paid_statuses or woocommerce_valid_order_statuses_for_payment_complete. Decide that deliberately rather than assuming a new status behaves like Processing.

Where do I paste the code from the Custom Order Status generator?

You have three good options and one bad one. Best is a small site-specific plugin — the generator can output one for you, and it keeps working when you change theme. A code-snippets plugin is fine too. A child theme's functions.php works but ties the code to that theme. Never edit the parent theme: the next update overwrites it and your code is gone.

Why isn't my new custom order status showing up, or why do I get a 404?

Almost always stale rewrite rules. WordPress caches the URL routing table, so anything that adds URLs is invisible until that cache is rebuilt. Go to Settings → Permalinks and press Save Changes — you do not need to alter anything, just saving the page flushes the rules. Never call flush_rewrite_rules() on every page load; it is an expensive write and belongs in an activation hook.

Can I use the generated code in client work or a commercial plugin?

Yes. The output is ordinary WordPress API calls that you configured — there is no licence attached to it and no attribution required. Use it in client sites, commercial plugins and themes, or products you sell. The generated file carries a short credit comment you are free to delete.

The other WooCommerce generators in the library, plus the WordPress tools most often used alongside this one.