WooCommerce Subscriptions: hook map
Use this when building or reviewing an integration that needs to react to WooCommerce Subscriptions events. This is not an exhaustive dump of the 800+ hook calls in the plugin. It is a decision map for the hooks that are usually correct and the older/noisy hooks that AI agents tend to choose incorrectly.
Misconception this skill corrects
"Subscriptions are just orders, so hook woocommerce_order_status_changed or update _schedule_next_payment meta directly."
Subscriptions are WC_Subscription objects with their own lifecycle hooks, status transition hooks, date hooks, relation store, renewal order hooks, and scheduler bridge. Prefer WCS hooks and CRUD methods unless the task explicitly needs ordinary WC orders.
When to use this skill
Trigger when ANY of the following is true:
- The user asks for WooCommerce Subscriptions actions/filters, lifecycle hooks, renewal hooks, status hooks, payment retry hooks, switching hooks, or gifting hooks.
- You see
WC_Subscription, wcs_get_subscription(), wcs_create_subscription(), wcs_create_renewal_order(), woocommerce_scheduled_subscription_payment, wcs_renewal_order_created, payment_retry, wcsg_, or subscription_switch.
- You need to decide whether to hook at subscription creation, renewal order creation, gateway payment attempt, successful payment, failed payment, status change, or scheduled action time.
Workflow
- Identify the lifecycle point first: creation, status, date schedule, renewal order, gateway charge, retry, switch/gift, or UI/API.
- Prefer hooks that pass
WC_Subscription or WC_Order objects over legacy hooks that pass subscription keys.
- For dynamic hooks, expand the real hook name from the runtime value: status, date type, payment method ID, or order relation type.
- Before implementing, inspect the exact source line in the installed plugin with:
rg -n "hook_name|function_name" wp-content/plugins/woocommerce-subscriptions/includes wp-content/plugins/woocommerce-subscriptions/src
Storage facts agents must not guess
Subscriptions registers shop_subscription as a WooCommerce order type. In CPT mode it appears as a post type; in HPOS it is an order type. Subscription product type slugs are subscription, variable-subscription, and subscription_variation.
Subscription prop meta keys include _billing_period, _billing_interval, _suspension_count, _cancelled_email_sent, _requires_manual_renewal, _trial_period, _last_order_date_created, _schedule_start, _schedule_trial_end, _schedule_next_payment, _schedule_cancelled, _schedule_end, _schedule_payment_retry, and _subscription_switch_data.
Related order meta keys are _subscription_renewal, _subscription_switch, and _subscription_resubscribe.
Switch cart items store subscription_switch cart item data. Gift cart items store wcsg_gift_recipients_email; gifted subscriptions use _recipient_user_email_address and _recipient_user, with parent order item meta wcsg_recipient.
WCS 9.0 bundles All Products for Subscriptions / Subscription Plans. APFS-selected plans store cart state under wcsatt_data.active_subscription_scheme and order item state under _wcsatt_scheme. Ordinary simple/variable products can be reported as subscriptions through the woocommerce_is_subscription filter when a plan is active.
Core hook map
| Need |
Hook |
Type |
Args |
Use |
| Detect a loaded subscription object |
wcs_get_subscription |
filter |
`WC_Subscription |
false $subscription` |
| Create subscription programmatically |
wcs_created_subscription |
filter |
WC_Subscription $subscription |
Modify the newly saved object before the post-create action. |
| Run after subscription creation |
wcs_create_subscription |
action |
WC_Subscription $subscription |
Attach metadata, external IDs, logs, or provisioning. |
| Change default new status |
woocommerce_default_subscription_status |
filter |
string $status |
Default is pending; return status without wc-. |
| Add/rename statuses |
wcs_subscription_statuses |
filter |
array $statuses |
Keys must use wc- prefix, e.g. wc-paused. |
| Allow status transition |
woocommerce_can_subscription_be_updated_to_{status} |
filter |
bool $can, WC_Subscription $subscription |
Permit a custom or normally blocked transition. |
| Before status update |
woocommerce_subscription_pre_update_status |
action |
$old_status, $new_status, WC_Subscription $subscription |
Validate/log before WCS mutates dates and saves. |
| Status reached |
woocommerce_subscription_status_{to} |
action |
WC_Subscription $subscription |
React to a specific target status, e.g. woocommerce_subscription_status_active. |
| Specific transition |
woocommerce_subscription_status_{from}_to_{to} |
action |
WC_Subscription $subscription |
Use for exact transitions such as on-hold_to_active. |
| Generic status update |
woocommerce_subscription_status_updated |
action |
WC_Subscription $subscription, string $to, string $from |
Best general hook for lifecycle integration. |
| WC-like status changed |
woocommerce_subscription_status_changed |
action |
int $subscription_id, string $from, string $to, WC_Subscription $subscription |
Useful when porting code shaped like woocommerce_order_status_changed. |
| Read a stored date |
woocommerce_subscription_get_{date_type}_date |
filter |
$date, WC_Subscription $subscription, string $timezone |
Display/read override; do not use to reschedule. |
| Calculate a future date |
woocommerce_subscription_calculated_{date_type}_date |
filter |
$date, WC_Subscription $subscription |
Change calculated next_payment, trial_end, end, or end_of_prepaid_term. |
| Date changed |
woocommerce_subscription_date_updated |
action |
WC_Subscription $subscription, string $date_type, string $datetime |
Scheduler listens here; good place for external sync. |
| Date deleted |
woocommerce_subscription_date_deleted |
action |
WC_Subscription $subscription, string $date_type |
Clean up external schedule/state. |
| Can date be changed |
woocommerce_subscription_can_date_be_updated |
filter |
bool $can, string $date_type, WC_Subscription $subscription |
Open/close date editing rules. |
| Query subscriptions |
woocommerce_get_subscriptions_query_args |
filter |
$query_args, $working_args |
Modify wcs_get_subscriptions() query before execution. |
| After query |
woocommerce_got_subscriptions |
filter |
$subscriptions, $working_args |
Post-filter subscription results. |
| Related orders |
woocommerce_subscription_related_orders |
filter |
$orders, WC_Subscription $subscription, $return_fields, $order_type |
Add/adjust parent, renewal, switch, resubscribe relations. |
Renewal and scheduled payment hooks
| Need |
Hook |
Type |
Args |
Use |
| Scheduled renewal is due |
woocommerce_scheduled_subscription_payment |
action |
int $subscription_id |
Fired by Action Scheduler/admin action. WCS prepares renewal at priority 1 and gateway processing runs at priority 10. |
| Renewal order creation failed |
wcs_failed_to_create_renewal_order |
action |
WP_Error $error, WC_Subscription $subscription |
Alert/log/retry externally. |
| Renewal order created |
wcs_renewal_order_created |
filter |
WC_Order $renewal_order, WC_Subscription $subscription |
Add order meta, line item data, external IDs. Return a WC_Order. |
| Gateway charge hook |
woocommerce_scheduled_subscription_payment_{gateway_id} |
action |
float $amount, WC_Order $renewal_order |
Payment gateways implement recurring charge here, e.g. the stored ID stripe produces woocommerce_scheduled_subscription_payment_stripe. |
| Manual renewal order generated |
woocommerce_generated_manual_renewal_order |
action |
int $renewal_order_id, WC_Subscription $subscription |
Notify, adjust pending manual renewal order. |
| Renewal payment complete |
woocommerce_subscription_renewal_payment_complete |
action |
WC_Subscription $subscription, WC_Order $last_order |
Provision after a successful renewal, not before gateway payment. |
| Renewal payment failed |
woocommerce_subscription_renewal_payment_failed |
action |
WC_Subscription $subscription, WC_Order $related_order |
Handle failed renewal consequences. |
| Any subscription payment complete |
woocommerce_subscription_payment_complete |
action |
WC_Subscription $subscription |
Fires for parent or renewal payment completion. |
| Any subscription payment failed |
woocommerce_subscription_payment_failed |
action |
WC_Subscription $subscription, string $new_status |
React to failure and resulting status. |
| Paid failed renewal |
woocommerce_subscriptions_paid_for_failed_renewal_order |
action |
WC_Order $renewal_order, WC_Subscription $subscription |
Update failing payment method or clear retry state after customer pays a failed renewal. |
Scheduler hooks
WCS uses Action Scheduler with group wc_subscription_scheduled_event, but the public integration point is the subscription date/status API. The scheduler listens to woocommerce_subscription_date_updated, woocommerce_subscription_date_deleted, and woocommerce_subscription_status_updated.
| Need |
Hook |
Type |
Args |
Use |
| Add/remove date types to schedule |
woocommerce_subscriptions_date_types_to_schedule |
filter |
string[] $date_types |
Include custom subscription date types. |
| Change scheduled action hook |
woocommerce_subscriptions_scheduled_action_hook |
filter |
string $hook, string $date_type |
Route a date type to a custom action. |
| Change scheduled args |
woocommerce_subscriptions_scheduled_action_args |
filter |
array $args, string $date_type, WC_Subscription $subscription |
Add deterministic args for custom scheduled actions. |
| Change Action Scheduler priority |
woocommerce_subscriptions_scheduled_action_priority |
filter |
int $priority, string $action_hook |
Default is priority 1. |
| Trial ended |
woocommerce_subscription_trial_ended |
action |
int $subscription_id |
Fired from scheduled trial end handler. |
| Expiration/end hooks |
woocommerce_scheduled_subscription_expiration, woocommerce_scheduled_subscription_end_of_prepaid_term |
action |
int $subscription_id |
Internal status handlers run here; attach after them if you need post-status side effects. |
Payment retry hooks
| Need |
Hook |
Type |
Args |
Use |
| Enable/disable retries |
wcs_is_retry_enabled |
filter |
bool $enabled |
Feature-level gate. |
| Replace default retry rules |
wcs_default_retry_rules |
filter |
array $rules |
Configure retry cadence/statuses. |
| Alter one retry rule |
wcs_get_retry_rule_raw, wcs_get_retry_rule |
filter |
$rule, $retry_number, $order_id |
Fine-grained retry rule customization. |
| Before/after applying rule |
woocommerce_subscriptions_before_apply_retry_rule, woocommerce_subscriptions_after_apply_retry_rule |
action |
WCS_Retry_Rule $rule, WC_Order $last_order, WC_Subscription $subscription |
Observe scheduled retry creation. |
| Retry action is about to charge |
woocommerce_subscriptions_before_payment_retry |
action |
WCS_Retry $retry, WC_Order $last_order |
Prepare/log before retry payment. |
| Retry charge finished |
woocommerce_subscriptions_after_payment_retry |
action |
WCS_Retry $retry, WC_Order $last_order |
Record retry result. |
| Retry status/date changed |
woocommerce_subscriptions_retry_status_updated, woocommerce_subscriptions_retry_date_updated |
action |
WCS_Retry ... |
External sync for retry objects. |
Switching, early renewal, gifting
| Area |
Hooks |
Use |
| Switch eligibility |
wcs_is_product_switchable, woocommerce_subscriptions_can_item_be_switched, woocommerce_subscriptions_can_item_be_switched_by_user |
Allow/block switching by product, item, or user. |
| Switch pricing |
wcs_switch_should_prorate_recurring_price, wcs_switch_should_prorate_sign_up_fee, wcs_switch_sign_up_fee, wcs_switch_proration_extra_to_pay |
Adjust proration math. In WCS 8.8+, wcs_switch_proration_extra_to_pay receives a 5th $switch_item argument. |
| Switch completion |
woocommerce_subscriptions_switch_completed |
React after switch order flow completes. |
| Early renewal |
wcs_is_early_renewal_enabled, woocommerce_subscriptions_can_user_renew_early, woocommerce_subscriptions_get_early_renewal_url |
Enable/disable and route early renewal. |
| Gifting product/checkout |
wcsg_enable_gifting, wcsg_is_enabled_for_all_products, wcsg_is_giftable_product, wcsg_cart_item_data |
Control whether gifting is available and persisted in cart. |
| Gifting recipient |
wcsg_recipient_details_updated, woocommerce_subscriptions_gifting_recipient_changed |
Sync recipient changes. |
Subscription Plans / APFS hooks in WCS 9.0+
| Need |
Hook/filter |
Use |
| Make ordinary products subscription-like |
woocommerce_is_subscription |
APFS hooks this so simple/variable/variation products with active plans participate in WCS logic. Do not override without preserving APFS result. |
| Add product type support |
wcsatt_supported_product_types |
Add only product types whose pricing/cart behavior you have tested with WCS recurring logic. |
| Change default product APFS mode |
woocommerce_subscriptions_default_product_subscription_scheme_mode |
Default is disable; alternatives are override or inherit. |
| Filter product plans |
wcsatt_product_subscription_schemes |
Adjust resolved local/storewide plans for a product. |
| Filter cart item plans |
wcsatt_cart_item_subscription_schemes |
Adjust plans available in a specific cart context. |
| Observe active scheme set |
wcsatt_set_product_subscription_scheme |
Runtime product object hook; not a database save hook. |
| Storewide plan REST save |
wcsatt_processed_cart_scheme_data |
Add custom plan fields before wcsatt_subscribe_to_cart_schemes is persisted. |
| Product plan REST save |
wcsatt_processed_scheme_data |
Add custom plan fields before _wcsatt_schemes is persisted. |
| Store API cart validation |
woocommerce_store_api_validate_cart_item |
APFS throws woocommerce_store_api_subscription_plan_invalid when a selected plan is invalid. |
| Store API checkout validation |
woocommerce_store_api_checkout_update_order_meta |
APFS validates selected plans when the real Store API checkout order exists. |
Use wcs-subscription-plans-apfs for storage, REST endpoints, cart data, and headless request details.
Gateway hooks
| Need |
Hook |
Type |
Args |
Use |
| Gateway support check |
woocommerce_subscription_payment_gateway_supports |
filter |
bool $supports, string $feature, WC_Subscription $subscription |
Add support for features like subscription_date_changes. |
| Status changed for gateway |
woocommerce_subscription_activated_{gateway_id}, woocommerce_subscription_on-hold_{gateway_id}, woocommerce_subscription_pending-cancel_{gateway_id}, woocommerce_subscription_cancelled_{gateway_id}, woocommerce_subscription_expired_{gateway_id} |
action |
WC_Subscription $subscription |
Gateway-specific remote profile updates. |
| Payment method updated |
woocommerce_subscription_payment_method_updated |
action |
WC_Subscription $subscription, string $new, string $old |
Sync token/payment method changes. |
| Payment method updated to/from gateway |
woocommerce_subscription_payment_method_updated_to_{gateway_id}, woocommerce_subscription_payment_method_updated_from_{gateway_id} |
action |
WC_Subscription $subscription, string $other_gateway_id |
Gateway-specific migration logic. |
| Failing method updated |
woocommerce_subscription_failing_payment_method_updated and ..._{gateway_id} |
action |
WC_Subscription $subscription, WC_Order $renewal_order |
After failed-renewal payment method handling. Use this for same-gateway failed-renewal retries in WCS 8.8+, because update_payment_method() hooks are skipped when the gateway did not actually change. |
| Payment meta fields |
woocommerce_subscription_payment_meta |
filter |
array $payment_meta, WC_Subscription $subscription |
Add fields to payment-method change UI. |
| Validate all payment meta |
woocommerce_subscription_validate_payment_meta |
action |
string $payment_method_id, array $payment_meta, WC_Subscription $subscription |
Generic validator receives 3 arguments. Register with accepted args 3. |
| Validate one gateway's meta |
woocommerce_subscription_validate_payment_meta_{gateway_id} |
action |
array $payment_meta, WC_Subscription $subscription |
Gateway-specific validator receives 2 arguments. Do not give the generic callback this signature. |
WCS 9.1 REST v2/v3 accepts request payment values only for slots declared by woocommerce_subscription_payment_meta; undeclared keys are ignored. Use wcs-rest-api for the allowlist shape and permission boundary.
Health Check and Processing reliability
WCS 8.8 adds operational surfaces that are not ordinary renewal hooks:
| Area |
Surface |
Use |
| Health Check tab |
Automattic\WooCommerce_Subscriptions\Internal\HealthCheck\StatusTab |
WooCommerce > Status > Subscriptions scan UI. It stores run/candidate rows and uses nonce-protected actions/AJAX for scan, cancel, suggestion, and remediation. |
| Resolve actions |
RemediationAdvisor, ToolRunner |
Built-in remediation can switch a flagged subscription to automatic renewal or process a missed renewal now. Do not call these internal classes from plugin business logic. |
| Dedicated processing |
Automattic\WooCommerce_Subscriptions\Internal\Queue_Management\Manager |
Merchant setting for subscription Action Scheduler isolation/focused runs. Tune via filters instead of creating competing runners. |
| External web cron |
/wp-json/wc/v3/subscriptions/job-queue?wcs_token=... |
Tokenized, rate-limited queue trigger created by the Web cron support setting. |
Use wcs-health-check-processing for implementation details and debugging patterns.
Abilities API caveat
WCS includes read-only Abilities API classes under src/Internal/Abilities, but registration is gated by woocommerce_subscriptions_abilities_enabled and defaults to false. The registrar also requires WooCommerce Core's 10.9 AbilitiesLoader. Do not assume these abilities exist on ordinary WCS installs, and do not build a production integration that depends on them unless your plugin explicitly controls that feature gate and Core version.
Customer action guardrails
For custom customer account actions, do not expose WCS admin REST writes directly. Load the subscription object, verify the current user owns it, then use WCS capabilities and object methods.
Status actions:
- Check
$subscription->get_user_id() === get_current_user_id() unless this is trusted admin/server code.
- Check
$subscription->can_be_updated_to( $target_status ) before calling $subscription->update_status( $target_status, $note, true ).
- Prefer domain statuses: cancel to
pending-cancel when the prepaid term should continue; cancel to cancelled only when immediate cancellation is intended and allowed.
- Let WCS status hooks run; do not update
post_status or order status meta directly.
Payment-method actions:
- Verify the selected payment token belongs to the same WP user and gateway customer.
- Do not update only payment meta/source IDs. Use
WC_Subscriptions_Change_Payment_Gateway::update_payment_method() or the gateway's change-payment flow so hooks and remote gateway side effects run.
- Preserve
woocommerce_subscriptions_pre_update_payment_method and woocommerce_subscription_payment_method_updated when the gateway actually changes; gateways use them for remote profile cleanup and migration. For same-gateway failed-renewal retries in WCS 8.8+, use woocommerce_subscription_failing_payment_method_updated for retry side effects.
Switch actions:
- Use
WC_Subscriptions_Switcher::can_item_be_switched_by_user() for eligibility.
- Wrap the switch cart/checkout flow or reproduce
_subscription_switch_data deliberately. Direct line-item replacement is not a subscription switch.
Common mistakes
// WRONG: catches many normal orders and misses WCS-specific semantics.
add_action( 'woocommerce_order_status_changed', 'my_sync' );
// RIGHT: subscription transition with object.
add_action( 'woocommerce_subscription_status_updated', function ( WC_Subscription $subscription, string $to, string $from ): void {
my_sync_subscription_status( $subscription->get_id(), $from, $to );
}, 10, 3 );
// WRONG: changing the schedule by writing meta bypasses validation and can desync Action Scheduler.
update_post_meta( $subscription_id, '_schedule_next_payment', '2026-05-01 00:00:00' );
// RIGHT: CRUD date update; WCS validates and reschedules via date hooks.
$subscription = wcs_get_subscription( $subscription_id );
if ( $subscription ) {
$subscription->update_dates( array( 'next_payment' => '2026-05-01 00:00:00' ), 'gmt' );
}
// WRONG: use the scheduled-payment hook for fulfillment.
add_action( 'woocommerce_scheduled_subscription_payment', 'ship_box' );
// RIGHT: fulfill only after renewal payment is complete.
add_action( 'woocommerce_subscription_renewal_payment_complete', function ( WC_Subscription $subscription, WC_Order $order ): void {
ship_box_for_renewal( $subscription, $order );
}, 10, 2 );
What this skill does NOT cover
- Building a payment gateway from scratch.
- HPOS order CRUD beyond the WCS-specific hooks here. Use
wc-hpos-compatibility for general order storage issues.
- Exhaustive hook cataloging. For full local discovery, run
rg -n "do_action\\(|apply_filters\\(" wp-content/plugins/woocommerce-subscriptions.
Cross-references
- Run
wcs-data-model-switching-gifting when exact Subscriptions meta names, product type slugs, switch payloads, switched item meta/types, or WCS Gifting recipient storage matters.
- Run
wcs-subscription-plans-apfs when ordinary Woo products can be sold as subscriptions via WCS 9.0 Subscription Plans / APFS.
- Run
wcs-cart-checkout-coupons for initial/recurring cart contexts, WCS coupon types, recurring fees, pseudo renewal coupons, and block checkout totals.
- Run
wcs-renewal-scheduler for changes to next payment dates, renewal order creation, scheduled actions, or payment retry timing.
- Run
wcs-health-check-processing for WCS 8.8 Health Check, Resolve actions, dedicated processing, and web-cron queue support.
- Run
wc-hpos-compatibility if the integration queries orders/subscriptions directly.
References
- Official documentation: https://woocommerce.com/document/subscriptions/develop/
- Verified source paths:
wp-content/plugins/woocommerce-subscriptions/includes/core/wcs-functions.php
wp-content/plugins/woocommerce-subscriptions/includes/core/class-wc-subscription.php
wp-content/plugins/woocommerce-subscriptions/includes/core/class-wc-subscriptions-change-payment-gateway.php
wp-content/plugins/woocommerce-subscriptions/includes/core/class-wc-subscriptions-core-plugin.php
wp-content/plugins/woocommerce-subscriptions/includes/core/class-wc-subscriptions-product.php
wp-content/plugins/woocommerce-subscriptions/includes/core/wcs-renewal-functions.php
wp-content/plugins/woocommerce-subscriptions/includes/core/class-wcs-action-scheduler.php
wp-content/plugins/woocommerce-subscriptions/includes/payment-retry/class-wcs-retry-manager.php
wp-content/plugins/woocommerce-subscriptions/includes/switching/class-wc-subscriptions-switcher.php
wp-content/plugins/woocommerce-subscriptions/includes/switching/class-wcs-cart-switch.php
wp-content/plugins/woocommerce-subscriptions/includes/gifting/class-wcs-gifting.php
wp-content/plugins/woocommerce-subscriptions/includes/gifting/class-wcsg-checkout.php
wp-content/plugins/woocommerce-subscriptions/includes/apfs/class-wcs-att-product.php
wp-content/plugins/woocommerce-subscriptions/includes/apfs/class-wcs-att-cart.php
wp-content/plugins/woocommerce-subscriptions/includes/apfs/class-wcs-att-order.php
wp-content/plugins/woocommerce-subscriptions/includes/apfs/api/class-wcs-att-store-api.php
wp-content/plugins/woocommerce-subscriptions/src/Internal/HealthCheck/
wp-content/plugins/woocommerce-subscriptions/src/Internal/Queue_Management/
wp-content/plugins/woocommerce-subscriptions/src/Internal/Abilities/
1---2name: wcs-subscription-hooks3description: Curated WooCommerce Subscriptions hook map for subscription creation, status/date transitions, renewal orders, scheduled payments, retries, gateway events, switching, gifting, related orders, APFS plans, REST, and account/admin UI. Use when choosing where to hook around WC_Subscription, wcs_create_subscription, wcs_create_renewal_order, woocommerce_scheduled_subscription_payment, payment_retry, wcsg_, subscription_switch, WCS_ATT, wcsatt_, or _wcsatt_scheme.4---56# WooCommerce Subscriptions: hook map78Use this when building or reviewing an integration that needs to react to WooCommerce Subscriptions events. This is not an exhaustive dump of the 800+ hook calls in the plugin. It is a decision map for the hooks that are usually correct and the older/noisy hooks that AI agents tend to choose incorrectly.910## Misconception this skill corrects1112> "Subscriptions are just orders, so hook `woocommerce_order_status_changed` or update `_schedule_next_payment` meta directly."1314Subscriptions are `WC_Subscription` objects with their own lifecycle hooks, status transition hooks, date hooks, relation store, renewal order hooks, and scheduler bridge. Prefer WCS hooks and CRUD methods unless the task explicitly needs ordinary WC orders.1516## When to use this skill1718Trigger when ANY of the following is true:1920- The user asks for WooCommerce Subscriptions actions/filters, lifecycle hooks, renewal hooks, status hooks, payment retry hooks, switching hooks, or gifting hooks.21- You see `WC_Subscription`, `wcs_get_subscription()`, `wcs_create_subscription()`, `wcs_create_renewal_order()`, `woocommerce_scheduled_subscription_payment`, `wcs_renewal_order_created`, `payment_retry`, `wcsg_`, or `subscription_switch`.22- You need to decide whether to hook at subscription creation, renewal order creation, gateway payment attempt, successful payment, failed payment, status change, or scheduled action time.2324## Workflow25261. Identify the lifecycle point first: creation, status, date schedule, renewal order, gateway charge, retry, switch/gift, or UI/API.272. Prefer hooks that pass `WC_Subscription` or `WC_Order` objects over legacy hooks that pass subscription keys.283. For dynamic hooks, expand the real hook name from the runtime value: status, date type, payment method ID, or order relation type.294. Before implementing, inspect the exact source line in the installed plugin with:3031```bash32rg -n "hook_name|function_name" wp-content/plugins/woocommerce-subscriptions/includes wp-content/plugins/woocommerce-subscriptions/src33```3435## Storage facts agents must not guess3637Subscriptions registers `shop_subscription` as a WooCommerce order type. In CPT mode it appears as a post type; in HPOS it is an order type. Subscription product type slugs are `subscription`, `variable-subscription`, and `subscription_variation`.3839Subscription prop meta keys include `_billing_period`, `_billing_interval`, `_suspension_count`, `_cancelled_email_sent`, `_requires_manual_renewal`, `_trial_period`, `_last_order_date_created`, `_schedule_start`, `_schedule_trial_end`, `_schedule_next_payment`, `_schedule_cancelled`, `_schedule_end`, `_schedule_payment_retry`, and `_subscription_switch_data`.4041Related order meta keys are `_subscription_renewal`, `_subscription_switch`, and `_subscription_resubscribe`.4243Switch cart items store `subscription_switch` cart item data. Gift cart items store `wcsg_gift_recipients_email`; gifted subscriptions use `_recipient_user_email_address` and `_recipient_user`, with parent order item meta `wcsg_recipient`.4445WCS 9.0 bundles All Products for Subscriptions / Subscription Plans. APFS-selected plans store cart state under `wcsatt_data.active_subscription_scheme` and order item state under `_wcsatt_scheme`. Ordinary simple/variable products can be reported as subscriptions through the `woocommerce_is_subscription` filter when a plan is active.4647## Core hook map4849| Need | Hook | Type | Args | Use |50|---|---|---|---|---|51| Detect a loaded subscription object | `wcs_get_subscription` | filter | `WC_Subscription|false $subscription` | Last-resort object substitution/validation. Do not return arbitrary types; WCS 8.1+ rejects non-`WC_Subscription` values. |52| Create subscription programmatically | `wcs_created_subscription` | filter | `WC_Subscription $subscription` | Modify the newly saved object before the post-create action. |53| Run after subscription creation | `wcs_create_subscription` | action | `WC_Subscription $subscription` | Attach metadata, external IDs, logs, or provisioning. |54| Change default new status | `woocommerce_default_subscription_status` | filter | `string $status` | Default is `pending`; return status without `wc-`. |55| Add/rename statuses | `wcs_subscription_statuses` | filter | `array $statuses` | Keys must use `wc-` prefix, e.g. `wc-paused`. |56| Allow status transition | `woocommerce_can_subscription_be_updated_to_{status}` | filter | `bool $can, WC_Subscription $subscription` | Permit a custom or normally blocked transition. |57| Before status update | `woocommerce_subscription_pre_update_status` | action | `$old_status, $new_status, WC_Subscription $subscription` | Validate/log before WCS mutates dates and saves. |58| Status reached | `woocommerce_subscription_status_{to}` | action | `WC_Subscription $subscription` | React to a specific target status, e.g. `woocommerce_subscription_status_active`. |59| Specific transition | `woocommerce_subscription_status_{from}_to_{to}` | action | `WC_Subscription $subscription` | Use for exact transitions such as `on-hold_to_active`. |60| Generic status update | `woocommerce_subscription_status_updated` | action | `WC_Subscription $subscription, string $to, string $from` | Best general hook for lifecycle integration. |61| WC-like status changed | `woocommerce_subscription_status_changed` | action | `int $subscription_id, string $from, string $to, WC_Subscription $subscription` | Useful when porting code shaped like `woocommerce_order_status_changed`. |62| Read a stored date | `woocommerce_subscription_get_{date_type}_date` | filter | `$date, WC_Subscription $subscription, string $timezone` | Display/read override; do not use to reschedule. |63| Calculate a future date | `woocommerce_subscription_calculated_{date_type}_date` | filter | `$date, WC_Subscription $subscription` | Change calculated `next_payment`, `trial_end`, `end`, or `end_of_prepaid_term`. |64| Date changed | `woocommerce_subscription_date_updated` | action | `WC_Subscription $subscription, string $date_type, string $datetime` | Scheduler listens here; good place for external sync. |65| Date deleted | `woocommerce_subscription_date_deleted` | action | `WC_Subscription $subscription, string $date_type` | Clean up external schedule/state. |66| Can date be changed | `woocommerce_subscription_can_date_be_updated` | filter | `bool $can, string $date_type, WC_Subscription $subscription` | Open/close date editing rules. |67| Query subscriptions | `woocommerce_get_subscriptions_query_args` | filter | `$query_args, $working_args` | Modify `wcs_get_subscriptions()` query before execution. |68| After query | `woocommerce_got_subscriptions` | filter | `$subscriptions, $working_args` | Post-filter subscription results. |69| Related orders | `woocommerce_subscription_related_orders` | filter | `$orders, WC_Subscription $subscription, $return_fields, $order_type` | Add/adjust parent, renewal, switch, resubscribe relations. |7071## Renewal and scheduled payment hooks7273| Need | Hook | Type | Args | Use |74|---|---|---|---|---|75| Scheduled renewal is due | `woocommerce_scheduled_subscription_payment` | action | `int $subscription_id` | Fired by Action Scheduler/admin action. WCS prepares renewal at priority 1 and gateway processing runs at priority 10. |76| Renewal order creation failed | `wcs_failed_to_create_renewal_order` | action | `WP_Error $error, WC_Subscription $subscription` | Alert/log/retry externally. |77| Renewal order created | `wcs_renewal_order_created` | filter | `WC_Order $renewal_order, WC_Subscription $subscription` | Add order meta, line item data, external IDs. Return a `WC_Order`. |78| Gateway charge hook | `woocommerce_scheduled_subscription_payment_{gateway_id}` | action | `float $amount, WC_Order $renewal_order` | Payment gateways implement recurring charge here, e.g. the stored ID `stripe` produces `woocommerce_scheduled_subscription_payment_stripe`. |79| Manual renewal order generated | `woocommerce_generated_manual_renewal_order` | action | `int $renewal_order_id, WC_Subscription $subscription` | Notify, adjust pending manual renewal order. |80| Renewal payment complete | `woocommerce_subscription_renewal_payment_complete` | action | `WC_Subscription $subscription, WC_Order $last_order` | Provision after a successful renewal, not before gateway payment. |81| Renewal payment failed | `woocommerce_subscription_renewal_payment_failed` | action | `WC_Subscription $subscription, WC_Order $related_order` | Handle failed renewal consequences. |82| Any subscription payment complete | `woocommerce_subscription_payment_complete` | action | `WC_Subscription $subscription` | Fires for parent or renewal payment completion. |83| Any subscription payment failed | `woocommerce_subscription_payment_failed` | action | `WC_Subscription $subscription, string $new_status` | React to failure and resulting status. |84| Paid failed renewal | `woocommerce_subscriptions_paid_for_failed_renewal_order` | action | `WC_Order $renewal_order, WC_Subscription $subscription` | Update failing payment method or clear retry state after customer pays a failed renewal. |8586## Scheduler hooks8788WCS uses Action Scheduler with group `wc_subscription_scheduled_event`, but the public integration point is the subscription date/status API. The scheduler listens to `woocommerce_subscription_date_updated`, `woocommerce_subscription_date_deleted`, and `woocommerce_subscription_status_updated`.8990| Need | Hook | Type | Args | Use |91|---|---|---|---|---|92| Add/remove date types to schedule | `woocommerce_subscriptions_date_types_to_schedule` | filter | `string[] $date_types` | Include custom subscription date types. |93| Change scheduled action hook | `woocommerce_subscriptions_scheduled_action_hook` | filter | `string $hook, string $date_type` | Route a date type to a custom action. |94| Change scheduled args | `woocommerce_subscriptions_scheduled_action_args` | filter | `array $args, string $date_type, WC_Subscription $subscription` | Add deterministic args for custom scheduled actions. |95| Change Action Scheduler priority | `woocommerce_subscriptions_scheduled_action_priority` | filter | `int $priority, string $action_hook` | Default is priority `1`. |96| Trial ended | `woocommerce_subscription_trial_ended` | action | `int $subscription_id` | Fired from scheduled trial end handler. |97| Expiration/end hooks | `woocommerce_scheduled_subscription_expiration`, `woocommerce_scheduled_subscription_end_of_prepaid_term` | action | `int $subscription_id` | Internal status handlers run here; attach after them if you need post-status side effects. |9899## Payment retry hooks100101| Need | Hook | Type | Args | Use |102|---|---|---|---|---|103| Enable/disable retries | `wcs_is_retry_enabled` | filter | `bool $enabled` | Feature-level gate. |104| Replace default retry rules | `wcs_default_retry_rules` | filter | `array $rules` | Configure retry cadence/statuses. |105| Alter one retry rule | `wcs_get_retry_rule_raw`, `wcs_get_retry_rule` | filter | `$rule, $retry_number, $order_id` | Fine-grained retry rule customization. |106| Before/after applying rule | `woocommerce_subscriptions_before_apply_retry_rule`, `woocommerce_subscriptions_after_apply_retry_rule` | action | `WCS_Retry_Rule $rule, WC_Order $last_order, WC_Subscription $subscription` | Observe scheduled retry creation. |107| Retry action is about to charge | `woocommerce_subscriptions_before_payment_retry` | action | `WCS_Retry $retry, WC_Order $last_order` | Prepare/log before retry payment. |108| Retry charge finished | `woocommerce_subscriptions_after_payment_retry` | action | `WCS_Retry $retry, WC_Order $last_order` | Record retry result. |109| Retry status/date changed | `woocommerce_subscriptions_retry_status_updated`, `woocommerce_subscriptions_retry_date_updated` | action | `WCS_Retry ...` | External sync for retry objects. |110111## Switching, early renewal, gifting112113| Area | Hooks | Use |114|---|---|---|115| Switch eligibility | `wcs_is_product_switchable`, `woocommerce_subscriptions_can_item_be_switched`, `woocommerce_subscriptions_can_item_be_switched_by_user` | Allow/block switching by product, item, or user. |116| Switch pricing | `wcs_switch_should_prorate_recurring_price`, `wcs_switch_should_prorate_sign_up_fee`, `wcs_switch_sign_up_fee`, `wcs_switch_proration_extra_to_pay` | Adjust proration math. In WCS 8.8+, `wcs_switch_proration_extra_to_pay` receives a 5th `$switch_item` argument. |117| Switch completion | `woocommerce_subscriptions_switch_completed` | React after switch order flow completes. |118| Early renewal | `wcs_is_early_renewal_enabled`, `woocommerce_subscriptions_can_user_renew_early`, `woocommerce_subscriptions_get_early_renewal_url` | Enable/disable and route early renewal. |119| Gifting product/checkout | `wcsg_enable_gifting`, `wcsg_is_enabled_for_all_products`, `wcsg_is_giftable_product`, `wcsg_cart_item_data` | Control whether gifting is available and persisted in cart. |120| Gifting recipient | `wcsg_recipient_details_updated`, `woocommerce_subscriptions_gifting_recipient_changed` | Sync recipient changes. |121122## Subscription Plans / APFS hooks in WCS 9.0+123124| Need | Hook/filter | Use |125|---|---|---|126| Make ordinary products subscription-like | `woocommerce_is_subscription` | APFS hooks this so simple/variable/variation products with active plans participate in WCS logic. Do not override without preserving APFS result. |127| Add product type support | `wcsatt_supported_product_types` | Add only product types whose pricing/cart behavior you have tested with WCS recurring logic. |128| Change default product APFS mode | `woocommerce_subscriptions_default_product_subscription_scheme_mode` | Default is `disable`; alternatives are `override` or `inherit`. |129| Filter product plans | `wcsatt_product_subscription_schemes` | Adjust resolved local/storewide plans for a product. |130| Filter cart item plans | `wcsatt_cart_item_subscription_schemes` | Adjust plans available in a specific cart context. |131| Observe active scheme set | `wcsatt_set_product_subscription_scheme` | Runtime product object hook; not a database save hook. |132| Storewide plan REST save | `wcsatt_processed_cart_scheme_data` | Add custom plan fields before `wcsatt_subscribe_to_cart_schemes` is persisted. |133| Product plan REST save | `wcsatt_processed_scheme_data` | Add custom plan fields before `_wcsatt_schemes` is persisted. |134| Store API cart validation | `woocommerce_store_api_validate_cart_item` | APFS throws `woocommerce_store_api_subscription_plan_invalid` when a selected plan is invalid. |135| Store API checkout validation | `woocommerce_store_api_checkout_update_order_meta` | APFS validates selected plans when the real Store API checkout order exists. |136137Use `wcs-subscription-plans-apfs` for storage, REST endpoints, cart data, and headless request details.138139## Gateway hooks140141| Need | Hook | Type | Args | Use |142|---|---|---|---|---|143| Gateway support check | `woocommerce_subscription_payment_gateway_supports` | filter | `bool $supports, string $feature, WC_Subscription $subscription` | Add support for features like `subscription_date_changes`. |144| Status changed for gateway | `woocommerce_subscription_activated_{gateway_id}`, `woocommerce_subscription_on-hold_{gateway_id}`, `woocommerce_subscription_pending-cancel_{gateway_id}`, `woocommerce_subscription_cancelled_{gateway_id}`, `woocommerce_subscription_expired_{gateway_id}` | action | `WC_Subscription $subscription` | Gateway-specific remote profile updates. |145| Payment method updated | `woocommerce_subscription_payment_method_updated` | action | `WC_Subscription $subscription, string $new, string $old` | Sync token/payment method changes. |146| Payment method updated to/from gateway | `woocommerce_subscription_payment_method_updated_to_{gateway_id}`, `woocommerce_subscription_payment_method_updated_from_{gateway_id}` | action | `WC_Subscription $subscription, string $other_gateway_id` | Gateway-specific migration logic. |147| Failing method updated | `woocommerce_subscription_failing_payment_method_updated` and `..._{gateway_id}` | action | `WC_Subscription $subscription, WC_Order $renewal_order` | After failed-renewal payment method handling. Use this for same-gateway failed-renewal retries in WCS 8.8+, because `update_payment_method()` hooks are skipped when the gateway did not actually change. |148| Payment meta fields | `woocommerce_subscription_payment_meta` | filter | `array $payment_meta, WC_Subscription $subscription` | Add fields to payment-method change UI. |149| Validate all payment meta | `woocommerce_subscription_validate_payment_meta` | action | `string $payment_method_id, array $payment_meta, WC_Subscription $subscription` | Generic validator receives 3 arguments. Register with accepted args `3`. |150| Validate one gateway's meta | `woocommerce_subscription_validate_payment_meta_{gateway_id}` | action | `array $payment_meta, WC_Subscription $subscription` | Gateway-specific validator receives 2 arguments. Do not give the generic callback this signature. |151152WCS 9.1 REST v2/v3 accepts request payment values only for slots declared by `woocommerce_subscription_payment_meta`; undeclared keys are ignored. Use `wcs-rest-api` for the allowlist shape and permission boundary.153154## Health Check and Processing reliability155156WCS 8.8 adds operational surfaces that are not ordinary renewal hooks:157158| Area | Surface | Use |159|---|---|---|160| Health Check tab | `Automattic\WooCommerce_Subscriptions\Internal\HealthCheck\StatusTab` | WooCommerce > Status > Subscriptions scan UI. It stores run/candidate rows and uses nonce-protected actions/AJAX for scan, cancel, suggestion, and remediation. |161| Resolve actions | `RemediationAdvisor`, `ToolRunner` | Built-in remediation can switch a flagged subscription to automatic renewal or process a missed renewal now. Do not call these internal classes from plugin business logic. |162| Dedicated processing | `Automattic\WooCommerce_Subscriptions\Internal\Queue_Management\Manager` | Merchant setting for subscription Action Scheduler isolation/focused runs. Tune via filters instead of creating competing runners. |163| External web cron | `/wp-json/wc/v3/subscriptions/job-queue?wcs_token=...` | Tokenized, rate-limited queue trigger created by the Web cron support setting. |164165Use `wcs-health-check-processing` for implementation details and debugging patterns.166167## Abilities API caveat168169WCS includes read-only Abilities API classes under `src/Internal/Abilities`, but registration is gated by `woocommerce_subscriptions_abilities_enabled` and defaults to `false`. The registrar also requires WooCommerce Core's 10.9 `AbilitiesLoader`. Do not assume these abilities exist on ordinary WCS installs, and do not build a production integration that depends on them unless your plugin explicitly controls that feature gate and Core version.170171## Customer action guardrails172173For custom customer account actions, do not expose WCS admin REST writes directly. Load the subscription object, verify the current user owns it, then use WCS capabilities and object methods.174175Status actions:176177- Check `$subscription->get_user_id() === get_current_user_id()` unless this is trusted admin/server code.178- Check `$subscription->can_be_updated_to( $target_status )` before calling `$subscription->update_status( $target_status, $note, true )`.179- Prefer domain statuses: cancel to `pending-cancel` when the prepaid term should continue; cancel to `cancelled` only when immediate cancellation is intended and allowed.180- Let WCS status hooks run; do not update `post_status` or order status meta directly.181182Payment-method actions:183184- Verify the selected payment token belongs to the same WP user and gateway customer.185- Do not update only payment meta/source IDs. Use `WC_Subscriptions_Change_Payment_Gateway::update_payment_method()` or the gateway's change-payment flow so hooks and remote gateway side effects run.186- Preserve `woocommerce_subscriptions_pre_update_payment_method` and `woocommerce_subscription_payment_method_updated` when the gateway actually changes; gateways use them for remote profile cleanup and migration. For same-gateway failed-renewal retries in WCS 8.8+, use `woocommerce_subscription_failing_payment_method_updated` for retry side effects.187188Switch actions:189190- Use `WC_Subscriptions_Switcher::can_item_be_switched_by_user()` for eligibility.191- Wrap the switch cart/checkout flow or reproduce `_subscription_switch_data` deliberately. Direct line-item replacement is not a subscription switch.192193## Common mistakes194195```php196// WRONG: catches many normal orders and misses WCS-specific semantics.197add_action( 'woocommerce_order_status_changed', 'my_sync' );198199// RIGHT: subscription transition with object.200add_action( 'woocommerce_subscription_status_updated', function ( WC_Subscription $subscription, string $to, string $from ): void {201 my_sync_subscription_status( $subscription->get_id(), $from, $to );202}, 10, 3 );203204// WRONG: changing the schedule by writing meta bypasses validation and can desync Action Scheduler.205update_post_meta( $subscription_id, '_schedule_next_payment', '2026-05-01 00:00:00' );206207// RIGHT: CRUD date update; WCS validates and reschedules via date hooks.208$subscription = wcs_get_subscription( $subscription_id );209if ( $subscription ) {210 $subscription->update_dates( array( 'next_payment' => '2026-05-01 00:00:00' ), 'gmt' );211}212213// WRONG: use the scheduled-payment hook for fulfillment.214add_action( 'woocommerce_scheduled_subscription_payment', 'ship_box' );215216// RIGHT: fulfill only after renewal payment is complete.217add_action( 'woocommerce_subscription_renewal_payment_complete', function ( WC_Subscription $subscription, WC_Order $order ): void {218 ship_box_for_renewal( $subscription, $order );219}, 10, 2 );220```221222## What this skill does NOT cover223224- Building a payment gateway from scratch.225- HPOS order CRUD beyond the WCS-specific hooks here. Use `wc-hpos-compatibility` for general order storage issues.226- Exhaustive hook cataloging. For full local discovery, run `rg -n "do_action\\(|apply_filters\\(" wp-content/plugins/woocommerce-subscriptions`.227228## Cross-references229230- Run `wcs-data-model-switching-gifting` when exact Subscriptions meta names, product type slugs, switch payloads, switched item meta/types, or WCS Gifting recipient storage matters.231- Run `wcs-subscription-plans-apfs` when ordinary Woo products can be sold as subscriptions via WCS 9.0 Subscription Plans / APFS.232- Run `wcs-cart-checkout-coupons` for initial/recurring cart contexts, WCS coupon types, recurring fees, pseudo renewal coupons, and block checkout totals.233- Run `wcs-renewal-scheduler` for changes to next payment dates, renewal order creation, scheduled actions, or payment retry timing.234- Run `wcs-health-check-processing` for WCS 8.8 Health Check, Resolve actions, dedicated processing, and web-cron queue support.235- Run `wc-hpos-compatibility` if the integration queries orders/subscriptions directly.236237## References238239- Official documentation: <https://woocommerce.com/document/subscriptions/develop/>240- Verified source paths:241 - `wp-content/plugins/woocommerce-subscriptions/includes/core/wcs-functions.php`242 - `wp-content/plugins/woocommerce-subscriptions/includes/core/class-wc-subscription.php`243 - `wp-content/plugins/woocommerce-subscriptions/includes/core/class-wc-subscriptions-change-payment-gateway.php`244 - `wp-content/plugins/woocommerce-subscriptions/includes/core/class-wc-subscriptions-core-plugin.php`245 - `wp-content/plugins/woocommerce-subscriptions/includes/core/class-wc-subscriptions-product.php`246 - `wp-content/plugins/woocommerce-subscriptions/includes/core/wcs-renewal-functions.php`247 - `wp-content/plugins/woocommerce-subscriptions/includes/core/class-wcs-action-scheduler.php`248 - `wp-content/plugins/woocommerce-subscriptions/includes/payment-retry/class-wcs-retry-manager.php`249 - `wp-content/plugins/woocommerce-subscriptions/includes/switching/class-wc-subscriptions-switcher.php`250 - `wp-content/plugins/woocommerce-subscriptions/includes/switching/class-wcs-cart-switch.php`251 - `wp-content/plugins/woocommerce-subscriptions/includes/gifting/class-wcs-gifting.php`252 - `wp-content/plugins/woocommerce-subscriptions/includes/gifting/class-wcsg-checkout.php`253 - `wp-content/plugins/woocommerce-subscriptions/includes/apfs/class-wcs-att-product.php`254 - `wp-content/plugins/woocommerce-subscriptions/includes/apfs/class-wcs-att-cart.php`255 - `wp-content/plugins/woocommerce-subscriptions/includes/apfs/class-wcs-att-order.php`256 - `wp-content/plugins/woocommerce-subscriptions/includes/apfs/api/class-wcs-att-store-api.php`257 - `wp-content/plugins/woocommerce-subscriptions/src/Internal/HealthCheck/`258 - `wp-content/plugins/woocommerce-subscriptions/src/Internal/Queue_Management/`259 - `wp-content/plugins/woocommerce-subscriptions/src/Internal/Abilities/`