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_name is 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 when milo_subscriptions_allows_any_gateway is true. $mapped is the Milo Payments gateway id (or null); $source_method is the original gateway (e.g. stripe). Return $source_method to keep the existing gateway, $mapped to 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

Did this page miss something?

Tell us and we rewrite it, usually the same day.

Send a note