WooCommerce Store API
The Store API is WooCommerce's shopper-facing REST surface. It powers Cart and Checkout blocks and is the right API for product browsing, current-cart reads/writes, current-customer checkout data, and headless cart/checkout flows.
It is not the admin/integration REST API (/wp-json/wc/v3 or /wp-json/wc/v4). Store API routes are public by design and return data for the current shopper/session only. If you need store settings, arbitrary orders/customers by ID, private product data, or back-office CRUD, use authenticated WC REST (wc/v4) or a custom WP REST route with explicit permissions.
Misconception this skill corrects
"Checkout is WooCommerce REST, so I will call
/wp-json/wc/v4/ordersfrom the browser."
That leaks the wrong model. WC REST is capability/consumer-key based and exposes admin-style resources. Store API is cart/session based, uses Nonce or Cart-Token for write protection, and is shaped for blocks/headless storefronts. Do not ship WC consumer keys to a browser to make checkout work.
When to use this skill
Trigger when ANY of the following is true:
- Building or debugging Cart/Checkout Blocks behavior.
- Calling
/wp-json/wc/store/v1/...from JS, a mobile client, or a headless frontend. - Adding extension data under
extensions.<namespace>to cart items, cart, checkout, or products. - Mutating custom cart state from block checkout via
/cart/extensionsorextensionCartUpdate. - Filtering payment-method availability for block checkout with Store API payment requirements.
- The diff contains
woocommerce_store_api_register_endpoint_data,woocommerce_store_api_register_update_callback,woocommerce_store_api_register_payment_requirements,Cart-Token,wc_store_api, orextensionCartUpdate.
Mental model
| Need | Use |
|---|---|
| Public products/product filters for storefront UI | Store API /wc/store/v1/products |
| Current shopper cart read/write | Store API /wc/store/v1/cart... |
| Checkout the current cart | Store API /wc/store/v1/checkout |
| Add data to existing Store API responses | woocommerce_store_api_register_endpoint_data() |
| Let block UI update plugin cart state | /cart/extensions via woocommerce_store_api_register_update_callback() |
| Admin/server CRUD for orders, products, refunds, fulfillments, settings | WC REST wc/v4 |
| Plugin-specific private endpoint | Custom register_rest_route() with permission_callback |
All stable Store API resources are under /wp-json/wc/store/v1. Woo also registers /wc/store as an alias to the current v1 routes, but hardcode /wc/store/v1 in clients so versioning is explicit.
Route map
Source-verified in src/StoreApi/RoutesController.php and src/StoreApi/Routes/V1/:
| Area | Common routes |
|---|---|
| Cart | GET /cart, POST /cart/add-item, POST /cart/update-item, POST /cart/remove-item, POST /cart/apply-coupon, POST /cart/remove-coupon, POST /cart/update-customer, POST /cart/select-shipping-rate, POST /cart/extensions |
| Cart collections | /cart/items, /cart/items/<key>, /cart/coupons, /cart/coupons/<code> |
| Checkout | GET /checkout, POST /checkout, PUT /checkout, POST /checkout/<id> |
| Order | GET /order/<id> for the current shopper/order key flow, not arbitrary admin lookup |
| Products | /products, /products/<id>, /products/<slug>, /products/collection-data |
| Taxonomies | /products/categories, /products/tags, /products/attributes, /products/attributes/<id>/terms, /products/brands |
| Reviews/batch | /products/reviews, /batch |
Private routes under /wc/private and experimental/feature-gated agentic checkout routes are not plugin extension contracts. Do not build public plugin behavior on them.
Nonce, Cart-Token, and sessions
Store API writes do not use X-WP-Nonce / wp_rest. They use a header named Nonce whose value is created with:
wp_create_nonce( 'wc_store_api' );
Cart routes send fresh headers with each response:
NonceNonce-TimestampCart-TokenCart-HashUser-IDCache-Control: no-store
For update methods (POST, PUT, PATCH, DELETE), AbstractCartRoute requires Nonce unless a valid Cart-Token header is present. Missing nonce returns woocommerce_rest_missing_nonce (401); invalid nonce returns woocommerce_rest_invalid_nonce (403).
Headless flow:
curl -i https://store.example/wp-json/wc/store/v1/cart
# Save the Cart-Token response header.
curl -H "Cart-Token: $CART_TOKEN" \
-H "Content-Type: application/json" \
-X POST \
-d '{"id":123,"quantity":1}' \
https://store.example/wp-json/wc/store/v1/cart/add-item
Same-site browser flow can use cookies + the Nonce header returned by the API. A Cart-Token is usually easier for headless clients because it avoids cookie affinity and also bypasses the nonce requirement for cart/checkout updates.
Store API CORS is stricter than the default WP REST behavior because cart/checkout responses can include shopper data. Cart-Token and Nonce are allowed request headers; only Cart-Token is exposed in CORS responses. A valid cart token can allow access from origins that would otherwise fail origin checks.
Extending response data
Register extension data after woocommerce_blocks_loaded; the Store API container is not ready before then. Use the helper functions rather than instantiating ExtendSchema yourself.
Extensible endpoint identifiers:
| Identifier | Constant | data_callback args |
|---|---|---|
cart |
CartSchema::IDENTIFIER |
none |
cart-item |
CartItemSchema::IDENTIFIER |
$cart_item |
checkout |
CheckoutSchema::IDENTIFIER |
none |
product |
ProductSchema::IDENTIFIER |
WC_Product $product |
Example: add public product badge data under extensions.myplugin:
use Automattic\WooCommerce\StoreApi\Schemas\V1\ProductSchema;
add_action( 'woocommerce_blocks_loaded', static function (): void {
woocommerce_store_api_register_endpoint_data( array(
'endpoint' => ProductSchema::IDENTIFIER,
'namespace' => 'myplugin',
'data_callback' => static function ( WC_Product $product ): array {
return array(
'badge' => (string) $product->get_meta( '_myplugin_badge' ),
);
},
'schema_callback' => static function (): array {
return array(
'badge' => array(
'description' => __( 'Short public badge text.', 'myplugin' ),
'type' => array( 'string', 'null' ),
'readonly' => true,
),
);
},
'schema_type' => ARRAY_A,
) );
} );
Rules for extension data:
- Namespace is required and should be your plugin slug.
data_callbackandschema_callbackmust return arrays. Returning anything else is logged and becomes empty data for non-admins.- Data appears under the endpoint's
extensionsobject, not as a top-level field. - Do not mutate cart/order state from a response
data_callback; keep it read-only. - Do not expose secrets, private settings, arbitrary customer/order lookups, or admin-only product data. Store API is public.
Updating cart state from blocks
Use /cart/extensions for plugin state that the shopper can change in cart/checkout UI: gift wrap, delivery instruction, insurance toggle, pickup choice, custom fee option, etc.
PHP registration:
add_action( 'woocommerce_blocks_loaded', static function (): void {
woocommerce_store_api_register_update_callback( array(
'namespace' => 'myplugin',
'callback' => static function ( array $data ): void {
$gift_wrap = ! empty( $data['gift_wrap'] );
WC()->session->set( 'myplugin_gift_wrap', $gift_wrap );
},
) );
} );
JS call from a checkout/cart block extension:
import { extensionCartUpdate } from '@woocommerce/blocks-checkout';
await extensionCartUpdate( {
namespace: 'myplugin',
data: { gift_wrap: true },
overwriteDirtyCustomerData: {
shipping_address: false,
billing_address: false,
},
} );
/cart/extensions loads the cart, runs your callback with data, recalculates totals, and returns the updated cart response. In WC 10.8, overwriteDirtyCustomerData can be a boolean or an object with shipping_address / billing_address booleans, so extensions can avoid overwriting the shopper's unsaved address edits independently.
Payment requirements
Payment requirements are extra cart-wide support flags. Store API merges your returned strings with the default requirement products and compares them against each gateway's $supports array.
add_action( 'woocommerce_blocks_loaded', static function (): void {
woocommerce_store_api_register_payment_requirements( array(
'data_callback' => static function (): array {
if ( myplugin_cart_requires_saved_card() ) {
return array( 'tokenization' );
}
return array();
},
) );
} );
This filters which gateways are valid for the current Store API cart. It does not register gateway UI. Checkout Block payment UI is still a JS payment-method integration; the PHP gateway class still owns process_payment().
Validation and quantity hooks
For Store API add-to-cart validation, prefer:
add_action( 'woocommerce_store_api_validate_add_to_cart', static function ( WC_Product $product, array $request ): void {
if ( myplugin_product_is_locked( $product ) ) {
throw new Exception( __( 'This product cannot be added to the cart.', 'myplugin' ) );
}
}, 10, 2 );
Quantity constraints shown in Store API cart/item schemas come from QuantityLimits. Filter:
woocommerce_store_api_product_quantity_minimum
woocommerce_store_api_product_quantity_maximum
woocommerce_store_api_product_quantity_multiple_of
woocommerce_store_api_product_quantity_editable
Each receives the value, WC_Product $product, and optional $cart_item.
Product endpoint notes for 10.8
GET /wc/store/v1/products is public and cache-sensitive. WC 10.8 fixed transient bloat caused by arbitrary product IDs in the related query parameter. In 10.8 source, related is an integer product ID, sanitized with absint, and invalid/non-visible products throw woocommerce_rest_product_not_found.
Rules:
- Pass a single product ID to
related, not arrays or comma lists. - Do not use Store API products as a private catalog endpoint. Visibility/readability checks still matter.
- Use WC REST
wc/v4/productswith proper auth when you need admin/private product fields.
Critical rules
- Store API is
wc/store/v1, notwc/v4. Use Store API for shopper cart/checkout; use WC REST for admin/integration CRUD. - Never ship WC consumer keys or secret gateway keys to a public client.
- Use header
Nonce, actionwc_store_api; notX-WP-Nonce, actionwp_rest. - Use
Cart-Tokenfor headless cart continuity. Get it fromGET /cart, send it back as a request header. - Register Store API extension callbacks on
woocommerce_blocks_loaded. - Keep extension data public and read-only. Use
/cart/extensionsfor mutations. - Return arrays from Store API callbacks. Non-array return values are logged and dropped.
- Namespace extension data. Do not write top-level response fields or generic namespaces like
custom. - Do not subclass internal Store API route classes for plugin endpoints. Use
register_rest_route()for your own routes; use Store API helpers only where WC exposes extension points. - Do not disable nonce checks outside local/dev testing.
woocommerce_store_api_disable_nonce_checkis a development escape hatch, not production configuration. - Use Store API rate-limit hooks for public write-heavy Store API flows.
woocommerce_store_api_rate_limit_options,woocommerce_store_api_rate_limit_id, andwoocommerce_store_api_rate_limit_exceededaffect Store API requests; unrelated custom REST routes need their own limits.
Cross-references
- Run
wc-rest-api-v4when the task is admin/integration REST, fulfillments, settings, private product/order data, or server-to-server API clients. - Run
wc-customer-and-sessionswhen code usesWC()->session,WC()->customer, or needs to understand frontend/REST session bootstrapping. - Run
wc-payment-gatewaywhen Store API payment requirements intersect with a gateway's$supportsandprocess_payment(). - Run
wc-hpos-compatibilitywhen checkout/order code stores custom order meta. - Run
wp-rest-apiwhen creating custom plugin endpoints outside Store API.
What this skill does NOT cover
- Full React payment-method UI registration (
registerPaymentMethod, express methods, saved-token components). This skill covers Store API server-side availability and data extension points. - Admin REST
wc/v4route details. Usewc-rest-api-v4. - Store API internals/private route implementation. Internal classes are useful source references, not a public inheritance contract.
- GraphQL or the newer dual-code API experiments in WC 10.8.
References
- Official Store API overview: https://developer.woocommerce.com/docs/apis/store-api/.
- Store API nonce tokens: https://developer.woocommerce.com/docs/apis/store-api/nonce-tokens/.
- Store API cart tokens: https://developer.woocommerce.com/docs/apis/store-api/cart-tokens/.
- Extending Store API data: https://developer.woocommerce.com/docs/apis/store-api/extending-store-api/extend-store-api-add-data/.
- Route registration: wp-content/plugins/woocommerce/src/StoreApi/RoutesController.php.
- Cart route headers/session/nonce rules: wp-content/plugins/woocommerce/src/StoreApi/Routes/V1/AbstractCartRoute.php.
- Store API authentication/CORS/rate limit logic: wp-content/plugins/woocommerce/src/StoreApi/Authentication.php.
- Extension helpers: wp-content/plugins/woocommerce/src/StoreApi/functions.php and wp-content/plugins/woocommerce/src/StoreApi/Schemas/ExtendSchema.php.
/cart/extensions: wp-content/plugins/woocommerce/src/StoreApi/Routes/V1/CartExtensions.php and wp-content/plugins/woocommerce/src/StoreApi/Schemas/V1/CartExtensionsSchema.php.- Product
relatedquery handling: wp-content/plugins/woocommerce/src/StoreApi/Routes/V1/Products.php and wp-content/plugins/woocommerce/src/StoreApi/Utilities/ProductQuery.php.