Hooks & filters
Milo Subscriptions exposes a set of WordPress action hooks and filter hooks so you can react to subscription lifecycle events, modify output, or extend behavior without editing the plugin itself. This page lists every public hook, what it passes, and where it fires.
All hooks use the milo_subscriptions_ prefix (note the underscore between milo and subscriptions). Add-ons use their own prefix, for example milo_any_gateway_.
Actions
Subscription lifecycle
do_action( 'milo_subscriptions_subscription_created', $subscription, $order );
do_action( 'milo_subscriptions_subscription_status_updated', $subscription, $new_status, $old_status );
do_action( 'milo_subscriptions_trial_ended', $subscription );
do_action( 'milo_subscriptions_subscription_switched', $subscription, $order, $new_item, $old_product_name );
do_action( 'milo_subscriptions_subscription_manually_created', $subscription );
milo_subscriptions_subscription_created: fires when a new subscription has been created (typically just after checkout).milo_subscriptions_subscription_status_updated: fires every time a subscription status changes. Useful for CRM syncs, audit logs, and external notifications.milo_subscriptions_trial_ended: fires when a subscription’s trial period completes.milo_subscriptions_subscription_switched: fires after a customer (or admin) switches a subscription to a different variation.$old_product_nameis the name of the variation it switched from.milo_subscriptions_subscription_manually_created: fires when a subscription is created from the admin (not via checkout).
Renewals
do_action( 'milo_subscriptions_renewal_order_created', $renewal_order, $subscription );
do_action( 'milo_subscriptions_customer_renewal_invoice', $renewal_order, $subscription );
do_action( 'milo_subscriptions_process_token_payment', $renewal_order, $token, $subscription );
do_action( 'milo_subscriptions_renewal_payment_complete', $subscription, $order );
milo_subscriptions_renewal_order_created: fires right after Milo Subscriptions creates a renewal order.milo_subscriptions_customer_renewal_invoice: fires when a manual renewal invoice needs to be emailed to the customer.milo_subscriptions_process_token_payment: fires when a gateway should charge a stored token for a renewal. Payment gateway add-ons typically hook into this.milo_subscriptions_renewal_payment_complete: fires when a renewal payment has been successfully captured.
Settings
do_action( 'milo_subscriptions_settings_updated', $settings );
Fires after the settings REST endpoint successfully persists new values. Receives the full settings array.
Admin UI
do_action( 'milo_subscriptions_product_data_panel', $product );
Fires inside the product Data panel for subscription products. Use it to add custom fields to the subscription product edit screen.
Templates
do_action( 'milo_subscriptions_after_subscription_details', $subscription );
Fires at the end of the My Account subscription detail template. Use it to render custom information below the default subscription details block.
Standalone mode
do_action( 'milo_subscriptions_standalone_subscription_created', $user_id, $plan_id, $session );
do_action( 'milo_subscriptions_standalone_subscription_updated', $user_id, $plan_id, $old_status, $new_status );
do_action( 'milo_subscriptions_standalone_subscription_canceled', $user_id, $plan_id );
Fired by the standalone-mode Stripe webhook listener when Stripe notifies the site that a standalone subscription has changed state. The $session argument on *_created is the raw Stripe Checkout Session payload.
Filters
Subscription content and output
apply_filters( 'milo_subscriptions_product_price_html', $price, $product );
apply_filters( 'milo_subscriptions_product_add_to_cart_text', $text, $product );
apply_filters( 'milo_subscriptions_variable_product_price_html', $price_html, $product );
apply_filters( 'milo_subscriptions_variable_product_add_to_cart_text', $text, $product );
Control what is rendered for subscription product prices and add-to-cart buttons, for both simple and variable subscription products.
My Account
apply_filters( 'milo_subscriptions_customer_subscription_actions', $actions, $subscription );
Controls which action buttons appear on a customer’s subscription in My Account (Cancel, Pause, Resume, Change payment method, Switch, Renew now). Return $actions minus an entry to hide an action, or add your own.
Renewal control
apply_filters( 'milo_subscriptions_process_renewal', true );
Return false from this filter to skip renewal processing in the current request. Useful during migrations, bulk maintenance, or custom scheduling.
Default settings
apply_filters( 'milo_subscriptions_default_settings', $settings );
apply_filters( 'milo_subscriptions_settings_boolean_keys', $keys );
milo_subscriptions_default_settings: fires when Milo Subscriptions builds the default settings array (before reading stored values). Use to seed different defaults, or to register an add-on’s own setting so it is saved through the settings UI.milo_subscriptions_settings_boolean_keys: the setting keys sanitized as yes/no on save. Add-ons register their boolean settings here.
Email identification
apply_filters( 'milo_subscriptions_email_id_prefixes', array( 'milo_subscriptions_' ) );
Controls which email-ID prefixes the settings REST endpoint considers as Milo Subscriptions emails. Useful when your add-on registers emails under a different prefix and you still want them managed from the settings UI.
REST API
apply_filters( 'milo_subscriptions_rest_subscription_data', $data, $subscription );
Filter the data returned for each subscription in REST responses. Use to add or remove fields.
Admin menu
apply_filters( 'milo_subscriptions_admin_menu_items', array() );
apply_filters( 'milo_subscriptions_admin_menu_extra_children', array() );
apply_filters( 'milo_subscriptions_available_features', array() );
apply_filters( 'milo_subscriptions_settings_addon_sections', array() );
apply_filters( 'milo_subscriptions_detail_sidebar_sections', array() );
Used by add-ons to register extra menu items, feature toggles, and settings sections inside the Milo Subscriptions React admin.
milo_subscriptions_admin_menu_items: add a top-level entry to the Milo Subscriptions sidebar.milo_subscriptions_admin_menu_extra_children: add a child entry under an existing parent.milo_subscriptions_available_features: advertise additional features for the settings UI.milo_subscriptions_settings_addon_sections: inject custom sections into the settings page tabs.milo_subscriptions_detail_sidebar_sections: add custom panels to the subscription detail sidebar.
Standalone mode
apply_filters( 'milo_subscriptions_subscribe_button_args', $args, $plan );
apply_filters( 'milo_subscriptions_subscribe_button_html', $html, $args, $plan );
apply_filters( 'milo_subscriptions_my_subscription_html', $html, $args, $subscription );
apply_filters( 'milo_subscriptions_stripe_checkout_session_args', $args, $plan, $user_id );
apply_filters( 'milo_subscriptions_restricted_content_fallback', $fallback_html, $plan );
For standalone mode (Stripe Checkout without WooCommerce):
milo_subscriptions_subscribe_button_args: alter the arguments used to render a Subscribe button.milo_subscriptions_subscribe_button_html: alter the final HTML of a Subscribe button.milo_subscriptions_my_subscription_html: alter the HTML of a single subscription block inside the standalone My Account shortcode.milo_subscriptions_stripe_checkout_session_args: modify the arguments sent to Stripe when creating a Checkout session. Useful for adding metadata, coupons, or tax IDs.milo_subscriptions_restricted_content_fallback: customize the message shown when restricted content is visible only to subscribers on a specific plan.
Tracking and staging
apply_filters( 'milo_subscriptions_send_tracking', true );
apply_filters( 'milo_subscriptions_staging_url_patterns', $staging_patterns );
milo_subscriptions_send_tracking: return false to disable anonymous usage tracking for this request.milo_subscriptions_staging_url_patterns: add URL patterns that should be treated as staging (which suppresses live-environment behavior like tracking and webhook deliveries).
Payment gateway integration (Milo Payments)
When working with Milo Payments or custom gateways:
apply_filters( 'milo_subscriptions_gateway_meta_keys', array() );
Gateways register meta keys that must be preserved on renewals, switches, and migrations. Stripe adds _stripe_customer_id, PayPal adds _milo_paypal_vault_id, Square adds _milo_square_customer_id and _milo_square_card_id, and Mollie adds _milo_mollie_customer_id and _milo_mollie_mandate_id.
Migration, gateways, and change payment
These hooks were added in 1.5.0 for migrations and for integrating a gateway’s own renewal and change-payment flows.
Migration mapping
apply_filters( 'milo_subscriptions_allows_any_gateway', false );
apply_filters( 'milo_subscriptions_migrate_payment_method', $mapped, $source_method, $subscription );
milo_subscriptions_allows_any_gateway: return true to allow non-Milo Payments gateways for subscriptions. The Any Gateway add-on enables this with a valid license. It gates both subscription checkout and the migration mapping below, so the free plan always routes through Milo Payments.milo_subscriptions_migrate_payment_method: the gateway a migrated subscription is assigned. Only consulted whenmilo_subscriptions_allows_any_gatewayis true.$mappedis the Milo Payments gateway id (or null);$source_methodis the original gateway (e.g.stripe). Return$source_methodto keep the existing gateway,$mappedto remap to Milo Payments, or''to force manual renewal.
Renewals through a gateway
do_action( "milo_subscriptions_scheduled_payment_{$gateway_id}", $amount, $renewal_order, $subscription );
Fires to charge a renewal for the subscription’s gateway. A gateway hooks milo_subscriptions_scheduled_payment_{gateway_id} to charge its stored token and call $renewal_order->payment_complete().
Change payment method
For a gateway that needs to own the My Account change-payment flow (for example to establish an off-session mandate and write its own recurring source meta):
apply_filters( 'milo_subscriptions_gateway_handles_change_payment', $handles, $gateway_id, $subscription );
do_action( "milo_subscriptions_change_payment_form_{$gateway_id}", $subscription );
apply_filters( 'milo_subscriptions_change_payment_gateway', $gateway_id, $subscription );
apply_filters( 'milo_subscriptions_block_unvalidated_token_change', $block, $token, $subscription );
milo_subscriptions_gateway_handles_change_payment: return true so your gateway owns the change-payment flow instead of the generic token swap.milo_subscriptions_change_payment_form_{gateway_id}: render and process your gateway’s own change-payment form, then store the new source on the subscription and save.milo_subscriptions_change_payment_gateway: resolve the gateway id for a subscription that has no stored method yet (for example after a manual import).milo_subscriptions_block_unvalidated_token_change: return true (or an error string) to block a token swap that has no off-session mandate, so the next renewal cannot be declined for a missing SCA mandate.
See Integrating a Payment Gateway and Migration and Payment Gateways for full examples.
Add-on filters
apply_filters( 'milo_any_gateway_keep_source_gateway', $keep, $source_method );
milo_any_gateway_keep_source_gateway (Any Gateway add-on): whether migrated subscriptions keep their source gateway (the default) or are consolidated onto Milo Payments.
Example usage
Send a webhook when a subscription status changes:
add_action( 'milo_subscriptions_subscription_status_updated', function ( $subscription, $new_status, $old_status ) {
wp_remote_post( 'https://example.com/hooks/subscription-status', array(
'body' => wp_json_encode( array(
'id' => $subscription->get_id(),
'customer' => $subscription->get_customer_id(),
'old_status' => $old_status,
'new_status' => $new_status,
) ),
) );
}, 10, 3 );
Hide the Cancel action for a specific product:
add_filter( 'milo_subscriptions_customer_subscription_actions', function ( $actions, $subscription ) {
foreach ( $subscription->get_items() as $item ) {
if ( $item->get_product_id() === 123 ) {
unset( $actions['cancel'] );
}
}
return $actions;
}, 10, 2 );
Add metadata to every standalone Stripe Checkout session:
add_filter( 'milo_subscriptions_stripe_checkout_session_args', function ( $args, $plan, $user_id ) {
$args['metadata']['source'] = 'landing_page_v2';
$args['metadata']['user_id'] = $user_id;
return $args;
}, 10, 3 );
Skip renewal processing during a maintenance window:
add_filter( 'milo_subscriptions_process_renewal', function ( $process ) {
$in_maintenance = get_option( 'store_maintenance_mode' );
return $in_maintenance ? false : $process;
} );
Next Steps
- REST API: manage subscriptions programmatically.
- Developer REST API reference: endpoint-by-endpoint details.
Did this page miss something?
Tell us and we rewrite it, usually the same day.