WooCommerce’s Store API (the replacement for the legacy wc/v2 endpoints) is extensible through woocommerce_store_api_register_endpoint_data(), while the admin REST API (wc/v3) can be extended with custom routes using the standard WordPress register_rest_route() approach inside the woocommerce_rest_api namespace.
Problem: WooCommerce's built-in REST API endpoints cover products, orders, and customers, but custom business objects — delivery zones, B2B accounts, or product configurators — have no corresponding API, forcing developers to use admin AJAX or custom wp-ajax actions.
Solution: Register a custom WooCommerce REST controller by extending WC_REST_Controller or WP_REST_Controller and hooking into woocommerce_rest_api_get_rest_namespaces. Define routes in register_routes(), implement get_item_schema() for documentation, and use the WooCommerce authentication middleware automatically by registering under the wc/v3 namespace.
The examples below add custom product meta to the wc/v3/products response, register a custom wc/v3 route for bulk stock updates, and use Application Passwords for REST API authentication.
get_data();
$data['custom_fields'] = [
'supplier_sku' => get_post_meta( $product->get_id(), '_supplier_sku', true ),
'lead_time_days'=> (int) get_post_meta( $product->get_id(), '_lead_time', true ),
'is_featured' => (bool) get_post_meta( $product->get_id(), '_is_featured_product', true ),
];
$response->set_data( $data );
return $response;
}, 10, 3 );
// Also handle CREATE/UPDATE — save incoming custom fields
add_action( 'woocommerce_rest_insert_product_object', function(
WC_Product $product,
WP_REST_Request $request,
bool $creating
): void {
$custom = $request->get_param( 'custom_fields' );
if ( ! is_array( $custom ) ) return;
if ( isset( $custom['supplier_sku'] ) ) {
update_post_meta( $product->get_id(), '_supplier_sku', sanitize_text_field( $custom['supplier_sku'] ) );
}
if ( isset( $custom['lead_time_days'] ) ) {
update_post_meta( $product->get_id(), '_lead_time', absint( $custom['lead_time_days'] ) );
}
}, 10, 3 );
Register a custom bulk-update endpoint under the WooCommerce namespace:
'POST',
'callback' => 'myplugin_bulk_update_stock',
'permission_callback' => function(): bool {
// WooCommerce uses wc_rest_check_post_permissions() internally
return current_user_can( 'edit_products' );
},
'args' => [
'updates' => [
'type' => 'array',
'required' => true,
'items' => [
'type' => 'object',
'properties' => [
'id' => [ 'type' => 'integer', 'required' => true ],
'stock' => [ 'type' => 'integer', 'required' => true ],
],
],
],
],
] );
} );
function myplugin_bulk_update_stock( WP_REST_Request $request ): WP_REST_Response {
$updates = $request->get_param( 'updates' );
$results = [];
foreach ( $updates as $item ) {
$product = wc_get_product( absint( $item['id'] ) );
if ( ! $product ) {
$results[] = [ 'id' => $item['id'], 'error' => 'Product not found' ];
continue;
}
$product->set_stock_quantity( absint( $item['stock'] ) );
$product->set_stock_status( $item['stock'] > 0 ? 'instock' : 'outofstock' );
$product->save();
$results[] = [ 'id' => $item['id'], 'stock' => $product->get_stock_quantity() ];
}
return new WP_REST_Response( [ 'updated' => $results ], 200 );
}
// ── AUTHENTICATION ──
// Use WordPress Application Passwords (Settings → Application Passwords in admin)
// Authorization: Basic base64(username:application_password)
// curl -X GET https://example.com/wp-json/wc/v3/products \
// -u "admin:XXXX XXXX XXXX XXXX XXXX XXXX"
NOTE: WooCommerce uses its own permission system on top of WordPress capabilities. Always check permissions with current_user_can('edit_products') or wc_rest_check_post_permissions('product', 'create') in your permission callbacks rather than manage_options. Test your endpoints with wp rest wc-myplugin get --user=admin.