agentsclimarketplace

Wcm membership hooks

Skill Lonsdale201/wp-agent-skills/woocommerce/wcm-membership-hooks

Curated WooCommerce Memberships hook and extension-point map for plugins that build on user memberships, membership plans, status transitions, purchase/free-signup grants, profile fields, REST API, webhooks, members-area templates, CSV import/export, and Woo Subscriptions-linked memberships. Use when the user asks for a Memberships hook list, "where should I hook", membership created/saved, status changed, grant access from order, user membership API, profile fields, member directory, or code contains wc_memberships_, WC_Memberships_User_Membership, WC_Memberships_Membership_Plan, wc_user_membership, wc_membership_plan, wcm-, _subscription_id, wc_memberships_rules, or woocommerce-memberships.From its SKILL.md

Install
npx -y skills add Lonsdale201/wp-agent-skills --skill wcm-membership-hooks

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 21 stars21 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

24.7 KB, ~5.4k tokens by cl100k_base, as published. Nobody here has run it

WooCommerce Memberships: hook map

Use this when building or reviewing an integration that needs to react to WooCommerce Memberships events. This is a curated decision map, not an exhaustive dump of every hook call in the plugin.

Misconception this skill corrects

"Memberships are just posts/meta, so query wc_user_membership posts and update _member_plan_id or post_status directly."

User memberships are stored as custom posts, but the plugin has object wrappers, status hooks, access rules, order-grant metadata, REST/webhook behavior, profile-field storage, and Subscriptions integration. Prefer public functions and WC_Memberships_User_Membership methods unless the task explicitly requires low-level inspection.

When to use this skill

Trigger when ANY of the following is true:

  • The user asks for WooCommerce Memberships actions/filters, lifecycle hooks, status hooks, purchase grant hooks, profile fields, REST API hooks, webhooks, member directory, members area, or Subscriptions-linked memberships.
  • Code contains wc_memberships_, WC_Memberships_User_Membership, WC_Memberships_Membership_Plan, wc_user_membership, wcm-active, _member_plan_id, _wc_memberships_access_granted, or woocommerce-memberships.
  • You need to decide whether to hook at membership creation, post save, status transition, access grant, profile field save, API response shaping, or frontend members-area rendering.

Workflow

  1. Identify the layer first: user membership lifecycle, purchase/free-signup grant, access/restriction, discount, profile fields, REST/webhook, Abilities API, members area, CSV/admin, or Subscriptions integration.
  2. Prefer public functions and objects over raw WP_Query, wp_update_post(), or direct post meta writes.
  3. For status strings, know the boundary: WP post statuses use wcm- prefix, but object methods and lifecycle hooks commonly use unprefixed statuses such as active, paused, cancelled, expired, free_trial, delayed.
  4. Before implementing, inspect the installed plugin line with:
rg -n "hook_name|function_name" wp-content/plugins/woocommerce-memberships/src wp-content/plugins/woocommerce-memberships/templates

Storage facts agents must not guess

Memberships registers two CPTs: wc_membership_plan for plans and wc_user_membership for user memberships. A user membership's plan ID is post_parent, and its member/user ID is post_author; the installed plugin does not use _member_plan_id as the plan relation.

Core plan meta keys are _access_method, _access_length, _access_start_date, _access_end_date, _product_ids, _members_area_sections, and _email_content.

Core user membership meta keys are _start_date, _end_date, _cancelled_date, _paused_date, _paused_intervals, _product_id, _order_id, _previous_owners, _renewal_login_token, and _locked.

Membership rules are stored in the wc_memberships_rules option. Rule type values are content_restriction, product_restriction, and purchasing_discount.

When WooCommerce Subscriptions links a membership, the wc_user_membership post gets _subscription_id; subscription-linked memberships may also use _has_installment_plan and _free_trial_end_date.

Active-detection canon

When gating code that registers something at FluentCRM / WP boot time (FluentCRM trigger / action, WC custom block, JFB action, etc.), use a symbol that exists at FILE LOAD — not one declared inside Memberships's own plugins_loaded:10 callback. The symptom of getting this wrong is non-deterministic: works on some installs, your code disappears on others, depending on plugin activation order.

// RIGHT — declared at file scope in woocommerce-memberships.php:83.
// Available the moment WP includes the plugin file, before any hook fires.
class_exists('WC_Memberships_Loader');

// WRONG — load-order race.
// wc_memberships() is declared inside class-wc-memberships.php which
// Memberships's plugins_loaded:10 callback require_onces from init_plugin().
// If your registration callback runs BEFORE Memberships's, function_exists
// returns false and your code skips registration silently.
function_exists('wc_memberships');

Same rule for WC_Memberships itself (the main class): it's loaded INSIDE init_plugin() so class_exists('WC_Memberships') is also racy. The loader class is the safe top-level checkpoint. At call time (REST endpoints, funnel handlers, AJAX) all classes/functions are loaded — the racy checks only matter at registration time.

Public API first

NeedAPINotes
Get one membershipwc_memberships_get_user_membership( $id, $plan = null )Returns WC_Memberships_User_Membership or false. Subscriptions integration may return the subscription-aware subclass.
Get memberships for user/querywc_memberships_get_user_memberships( $user_id, $args )Prefer over hand-written membership WP_Query unless doing admin/reporting internals.
Get active membershipswc_memberships_get_user_active_memberships( $user_id, $args )Uses Memberships status semantics.
Check plan membershipwc_memberships_is_user_member( $user_id, $plan )General membership check.
Check active membershipwc_memberships_is_user_active_member( $user, $plan )Use for "currently active member" business rules.
Create membershipwc_memberships_create_user_membership( $args, $action = 'create' )Lets Memberships run grant/renew/update logic. Throws SV_WC_Plugin_Exception on missing plan — wrap in try/catch. Sets post_status = 'wcm-active' by default; override with $membership->update_status('paused', $note) after.
Get plan(s)wc_memberships_get_membership_plan(), wc_memberships_get_membership_plans()Avoid reading plan post meta directly.
Status registrywc_memberships_get_user_membership_statuses( $with_labels = true, $prefixed = true )Canonical picker / dropdown source. Pass (true, false) for {key, label} pairs with UNPREFIXED keys (active not wcm-active) — that's the form update_status() accepts. Honours the wc_memberships_user_membership_statuses filter so 3rd-party additions appear automatically.
Set / clear start date$membership->set_start_date( $date_string_or_empty )Empty string defaults to current_time('timestamp', true) — useful for "member-since = now" semantics.
Set / clear end date$membership->set_end_date( $timestamp_or_string_or_empty )Empty string clears the end date — the canonical "unlimited / never expires" pattern. Numeric timestamp or strtotime-able string accepted.
Check access`wc_memberships_user_can( $user_id, 'view''purchase', $target, $when = '' )`

User membership lifecycle hooks

NeedHookTypeArgsUse
Alter new membership post datawc_memberships_new_membership_datafilterarray $data, array $argsSet initial status/date/title before insert/update. Keep status prefixed if assigning post_status.
Membership created by product/API/CLI create flowwc_memberships_user_membership_createdactionWC_Memberships_Membership_Plan $plan, array $argsAttach external IDs or provision once. Args include user_id, user_membership_id, is_update.
Membership saved through save_postwc_memberships_user_membership_savedactionWC_Memberships_Membership_Plan $plan, array $argsCatch admin/manual/import saves. May fire in broader situations than creation.
Status catalogwc_memberships_user_membership_statusesfilterarray $statusesAdd labels/statuses. Keys normally use wcm- prefix.
Statuses granting accesswc_memberships_active_access_membership_statusesfilterstring[] $statusesControls access semantics; do not assume only active grants access.
Renewal-eligible statuseswc_memberships_valid_membership_statuses_for_renewalfilterstring[] $statusesChange when a membership can be renewed.
Cancel-eligible statuseswc_memberships_valid_membership_statuses_for_cancelfilterstring[] $statusesChange when members can cancel.
Any status transitionwc_memberships_user_membership_status_changedactionWC_Memberships_User_Membership $membership, string $old, string $newBest generic lifecycle sync hook. $old and $new are unprefixed.
Membership activatedwc_memberships_user_membership_activatedaction$membership, bool $was_paused, string $previous_statusResume/provision after activation.
Membership pausedwc_memberships_user_membership_pausedaction$membershipSuspend access in external systems.
Membership cancelledwc_memberships_user_membership_cancelledaction$membershipCancel external entitlements.
Gate automatic expirywc_memberships_expire_user_membershipfilterbool $expire, $membershipBlock/allow scheduled expiration.
Membership expiredwc_memberships_user_membership_expiredactionint $user_membership_idRuns after expiry; load object if needed.
Can member cancelwc_memberships_user_membership_can_be_cancelledfilterbool $can, $membershipUI/business rule for cancellation.
Cancel URLwc_memberships_get_cancel_membership_urlfilterstring $url, $membershipRoute cancellation to custom flow.
Can renewwc_memberships_user_membership_can_be_renewedfilterbool $can, $membershipUI/business rule for renewal.
Renewal productwc_memberships_user_membership_get_product_for_renewalfilter$product, array $products, $membershipChoose renewal product when several grant access.
Renew URLwc_memberships_get_renew_membership_urlfilterstring $url, $membershipRoute renewal to custom flow.
Can transferwc_memberships_user_membership_can_be_transferredfilterbool $can, $membership, int $from_user_id, int $to_user_idGate ownership transfer.
Transfer errorwc_memberships_user_membership_can_be_transferred_errorfilterWP_Error $error, $membership, $from_user_id, $to_user_idCustomize transfer failure.
Transferredwc_memberships_user_membership_transferredaction$membership, WP_User $new_owner, WP_User $previous_ownerSync owner change.
Deletedwc_memberships_user_membership_deletedactionWC_Memberships_User_Membership $membershipClean external state before Memberships removes related data.

Purchase and free-signup grants

NeedHookTypeArgsUse
Choose order item product that grants accesswc_memberships_access_granting_purchased_product_idfilter`intint[] $product_ids, int[] $candidate_ids, WC_Memberships_Membership_Plan $plan`
Access granted from purchasewc_memberships_grant_membership_access_from_purchaseactionWC_Memberships_Membership_Plan $plan, array $argsRuns after plan grants access from an order. Args include user, order, product, membership.
Free membership granted on signupwc_memberships_grant_free_membership_access_from_sign_upactionWC_Memberships_Membership_Plan $plan, array $argsAdd onboarding/profile data for free signup plans.
Force checkout registrationwc_memberships_force_checkout_registrationfilterbool $force, array $plansForce account creation when cart grants membership access. Avoid disabling if granting products are in cart.
Thank-you membership linkswoocommerce_memberships_thank_you_messagefilterstring $message, int $order_id, array $membershipsCustomize order thank-you/email membership links.

Profile fields

NeedHookTypeUse
Field type compatibilitywc_memberships_profile_fields_compatible_typesfilterExtend/limit field type support.
Visibility optionswc_memberships_profile_fields_visibility_optionsfilterAdd visibility choices.
Plan-specific visibilitywc_memberships_profile_fields_membership_plan_visibility_optionsfilterChange visibility choices for a plan/access method.
Read profile fieldswc_memberships_get_profile_fieldsfilterAdjust loaded Profile_Field objects for a user.
Validate definitionwc_memberships_profile_field_definition_validationfilterValidate admin field definition data.
Validate valuewc_memberships_profile_field_validationfilterValidate submitted member value.
Before/after signup fieldswc_memberships_before_signup_profile_fields, wc_memberships_after_signup_profile_fieldsactionSurround registration profile fields.
Before/after product fieldswc_memberships_before_product_profile_fields, wc_memberships_after_product_profile_fieldsactionSurround product-page profile fields.
Process product field submissionwc_memberships_should_process_product_profile_fields_submissionfilterDisable product-page profile field collection for a product.
My Account profile endpointwc_memberships_profile_fields_area_endpointfilterChange endpoint slug/query var.
Profile field CRUDwc_memberships_create_profile_field, wc_memberships_update_profile_field, wc_memberships_delete_profile_fieldactionObserve user field storage changes.
Definition CRUDwc_memberships_create_profile_field_definition, wc_memberships_update_profile_field_definition, wc_memberships_delete_profile_field_definitionactionObserve field definition changes.

REST API and webhooks

AreaHooksUse
User membership write requestwc_memberships_rest_api_create_user_membership_request, wc_memberships_rest_api_update_user_membership_request, wc_memberships_rest_api_user_membership_set_dataValidate/shape REST create/update before save.
User membership responsewc_memberships_rest_api_user_membership_data, wc_memberships_rest_api_user_membership_links, wc_memberships_rest_api_user_membership_schema, wc_memberships_rest_api_user_membership_endpoint_args, wc_memberships_rest_api_user_memberships_collection_paramsAdd fields, links, schema, endpoint args, or collection params.
Membership plan responsewc_memberships_rest_api_membership_plan_data, wc_memberships_rest_api_membership_plan_schema, wc_memberships_rest_api_membership_plans_collection_paramsExtend plan REST output.
Excluded metawc_memberships_rest_api_{$object_name}_excluded_meta_keysHide internal meta keys in REST.
Webhook topicswc_memberships_user_membership_webhook_topic_hooksMap webhook topics to actions.
User membership webhook eventswc_memberships_webhook_user_membership_created, updated, transferred, deletedWebhook topic source events; usually observe, not replace.
Plan webhook eventswc_memberships_webhook_membership_plan_created, updated, deleted, restoredWebhook topic source events for plans.

The public member directory endpoint is not a generic user-memberships list shortcut. /wc/v4/memberships/members/directory requires a page_id that contains a Memberships Directory block and validates the referenced block context. Directory responses are intentionally narrowed to the fields the block renders: id, customer_data, plan_name, profile_fields, and meta_data with meta_data blanked and profile_fields filtered to the block's allowed slugs. Cross-resource REST links are stripped. Use the admin-gated user memberships endpoint for full membership records.

Abilities API

Memberships 1.28.0+ registers WordPress Abilities API abilities when wp_register_ability() and wp_register_ability_category() exist:

  • woocommerce-memberships/plans-create
  • woocommerce-memberships/plans-delete
  • woocommerce-memberships/plans-get
  • woocommerce-memberships/plans-list
  • woocommerce-memberships/user-memberships-create
  • woocommerce-memberships/user-memberships-delete
  • woocommerce-memberships/user-memberships-get
  • woocommerce-memberships/user-memberships-list
  • woocommerce-memberships/post-restriction-rules-get
  • woocommerce-memberships/post-restriction-rules-update

Plan and user-membership abilities use manage_woocommerce; post restriction rule abilities use manage_woocommerce_membership_plans. The GET rule ability also gets an edit_post check from its integer input, while UPDATE wrappers should add their own edit_post guard. Treat them as privileged admin/editor automation, not front-end/headless customer endpoints. Use wcm-abilities-api when implementing or reviewing ability-based automation.

Members area, directory, CSV, admin

AreaHooksUse
Members area wrapperwc_memberships_before_members_area, wc_memberships_after_members_areaAdd UI around a members-area section.
My memberships tablewc_memberships_my_memberships_column_names, wc_memberships_my_memberships_column_{$column_id}, wc_memberships_my_memberships_no_memberships_textAdd or render columns in My Account.
Members area navigation/detailswc_memberships_members_area_navigation_items, wc_memberships_members_area_my_membership_details, wc_memberships_members_area_{$section}_actions, wc_memberships_members_area_{$section}_titleCustomize member dashboard structure.
Section tableswc_memberships_members_area_my_membership_content_column_names, products_column_names, discounts_column_names, notes_column_names plus matching ..._column_{$column_id} actionsAdd columns to content/product/discount/note sections.
Active discount displaywc_memberships_members_area_show_only_active_discountsInclude inactive/future discounts in member area.
Member directorywc_memberships_member_directory_settings, wc_memberships_member_directory_listing_plans, wc_memberships_member_directory_included_members, wc_memberships_member_directory_before_member_card, after_member_cardShape directory query and cards.
CSV import/exportwc_memberships_csv_import_user_memberships_data, wc_memberships_csv_import_user_membership, wc_memberships_csv_export_user_memberships_headers, wc_memberships_csv_export_user_memberships_row, wc_memberships_csv_export_user_memberships_{$column_name}_column, wc_memberships_csv_export_user_memberships_query_argsAdd columns/data to Memberships import/export.
Admin capabilitieswoocommerce_memberships_can_import_export, woocommerce_memberships_can_manage_profile_fields, wc_memberships_admin_screen_ids, wc_memberships_admin_tabsIntegrate admin pages/caps carefully.

Woo Subscriptions integration

If WooCommerce Subscriptions is active, Memberships may use subscription-aware membership objects and lifecycle rules.

NeedHookTypeUse
Membership linked to subscriptionwc_memberships_user_membership_linked_to_subscriptionactionSync external relation. Args include membership object, new subscription ID, old subscription ID.
Membership unlinked from subscriptionwc_memberships_user_membership_unlinked_from_subscriptionactionRemove external relation.
Cancel membership when linked subscription cancelswc_memberships_cancel_subscription_linked_membershipfilterReturn false only when your integration deliberately keeps access after subscription cancellation.
Subscription affects access startwc_memberships_access_from_timefilterSubscriptions integration uses this for trial-aware drip access.
REST subscription datawc_memberships_rest_api_user_membership_data, wc_memberships_rest_api_user_membership_schema, wc_memberships_rest_api_user_membership_set_dataSubscriptions integration extends these with subscription fields.

Common mistakes

// WRONG: skips Memberships creation/update hooks and object semantics.
wp_insert_post( array(
    'post_type'   => 'wc_user_membership',
    'post_status' => 'wcm-active',
) );

// RIGHT: let Memberships create/renew/update the user membership.
$membership = wc_memberships_create_user_membership( array(
    'user_id' => $user_id,
    'plan_id' => $plan_id,
) );

// WRONG: status meta/post writes bypass object methods and can miss side effects.
wp_update_post( array( 'ID' => $membership_id, 'post_status' => 'wcm-cancelled' ) );

// RIGHT: use the object API; pass unprefixed status.
$membership = wc_memberships_get_user_membership( $membership_id );
if ( $membership instanceof WC_Memberships_User_Membership ) {
    $membership->update_status( 'cancelled', 'Cancelled by external CRM.' );
}

// WRONG: raw "active" check misses Memberships access semantics and delayed/free-trial cases.
$is_member = 'wcm-active' === get_post_status( $membership_id );

// RIGHT: use public checks for business rules.
if ( wc_memberships_is_user_active_member( $user_id, $plan_id ) ) {
    myplugin_enable_feature( $user_id );
}

// WRONG: function_exists() is a load-order race at registration time.
// wc_memberships() is declared inside Memberships's plugins_loaded:10
// callback. If your registrar runs first (also plugins_loaded:10), this
// returns false and your integration silently doesn't register.
add_action( 'plugins_loaded', function () {
    if ( ! function_exists( 'wc_memberships' ) ) {
        return; // racy — sometimes false even when Memberships is active
    }
    register_my_membership_integration();
}, 11 );

// RIGHT: WC_Memberships_Loader is at file scope — race-free.
add_action( 'plugins_loaded', function () {
    if ( ! class_exists( 'WC_Memberships_Loader' ) ) {
        return;
    }
    register_my_membership_integration();
}, 11 );

// WRONG: hardcoding the status list — Memberships allows 3rd parties to add
// their own statuses via the wc_memberships_user_membership_statuses filter.
$statuses = array( 'active', 'paused', 'cancelled', 'expired' );

// RIGHT: read from the registry (UNPREFIXED keys for object methods).
$statuses = wc_memberships_get_user_membership_statuses( true, false );
foreach ( $statuses as $key => $data ) {
    // $key = 'active', 'paused', ...; $data['label'] = 'Active', 'Paused', ...
}

Cross-references

  • Use wcm-data-model-subscriptions-link for exact CPT names, meta keys, rule storage, profile-field storage, and Memberships-Subscriptions relation details.
  • Use wcm-access-discounts for access checks, restriction/drip hooks, member discounts, and price-adjustment recursion safety.
  • Use wcm-abilities-api for Memberships 1.29+ WP Abilities API names, permissions, schemas, REST route exposure, and guardrails.
  • Use wcs-subscription-hooks or wcs-renewal-scheduler when the membership is tied to WooCommerce Subscriptions renewal/payment flow.

References

  • Verified source paths:
    • wp-content/plugins/woocommerce-memberships/src/class-wc-memberships-post-types.php
    • wp-content/plugins/woocommerce-memberships/src/class-wc-memberships-user-memberships.php
    • wp-content/plugins/woocommerce-memberships/src/class-wc-memberships-user-membership.php
    • wp-content/plugins/woocommerce-memberships/src/class-wc-memberships-membership-plan.php
    • wp-content/plugins/woocommerce-memberships/src/class-wc-memberships-rules.php
    • wp-content/plugins/woocommerce-memberships/src/API/Controller/User_Memberships.php
    • wp-content/plugins/woocommerce-memberships/src/API/Webhooks.php
    • wp-content/plugins/woocommerce-memberships/src/Abilities/Provider.php
    • wp-content/plugins/woocommerce-memberships/src/Plans/Abilities/
    • wp-content/plugins/woocommerce-memberships/src/UserMemberships/Abilities/
    • wp-content/plugins/woocommerce-memberships/src/Posts/Abilities/
    • wp-content/plugins/woocommerce-memberships/src/Helpers/Directory_Block_Validator.php
    • wp-content/plugins/woocommerce-memberships/src/Profile_Fields.php
    • wp-content/plugins/woocommerce-memberships/src/integrations/subscriptions/

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.