Developer reference for customizing and integrating with ProfileSwitch. Add hook and filter code to your theme’s functions.php or a site-specific plugin. Functions can be called anywhere after the plugin has loaded.

Jump to a section:

Action Hooks

ProfileSwitch fires these actions at key points so you can run custom logic without modifying plugin files. Hook in with add_action().

Profile lifecycle

These hooks fire whenever profile relationships change, no matter where the change comes from: the frontend switcher, the Manage Account panel, the admin Edit User screen, or a direct call to the linking functions below.

profileswitch_profile_linked

Fires after a profile is linked to a primary account. Receives the profile user ID and the primary profile user ID.

add_action( 'profileswitch_profile_linked', function( $profile_id, $primary_id ) {
    // A profile was linked to a primary account
}, 10, 2 );

profileswitch_profile_unlinked

Fires after a profile is unlinked from a primary account and becomes a standalone user. Receives the profile user ID and the primary profile ID it was unlinked from.

add_action( 'profileswitch_profile_unlinked', function( $profile_id, $old_primary_id ) {
    // A profile was unlinked and is now standalone
}, 10, 2 );

profileswitch_profile_deleted

Fires just before a profile is deleted through the Manage Account panel. The user still exists when this hook runs, so you can read its data for logging or cleanup.

add_action( 'profileswitch_profile_deleted', function( $profile_id, $primary_id ) {
    // A profile is about to be deleted
}, 10, 2 );

profileswitch_manager_added

Fires when a profile is designated as a manager.

add_action( 'profileswitch_manager_added', function( $profile_id, $primary_id ) {
    // Profile was made a manager
}, 10, 2 );

profileswitch_manager_removed

Fires when a profile’s manager status is removed.

add_action( 'profileswitch_manager_removed', function( $profile_id, $primary_id ) {
    // Profile is no longer a manager
}, 10, 2 );

Switcher form hooks

These hooks fire on the frontend profile switcher pages. Use them to render custom fields and save custom data when profiles are created or edited.

profileswitch_add_profile_form_fields

Fires after the name field on the Add Profile form. Use this to render additional input fields.

add_action( 'profileswitch_add_profile_form_fields', function() {
    echo '<p>Nickname:</p>';
    echo '<input type="text" name="my_nickname_field">';
} );

profileswitch_add_profile_save

Fires after a new profile is created (before it is linked to the primary). Use this to save data from custom fields added via profileswitch_add_profile_form_fields.

add_action( 'profileswitch_add_profile_save', function( $profile_id ) {
    if ( isset( $_POST['my_nickname_field'] ) ) {
        update_user_meta( $profile_id, 'my_nickname', sanitize_text_field( $_POST['my_nickname_field'] ) );
    }
} );

profileswitch_edit_profile_form_fields

Fires after the name field on the Edit Profile form. Receives the profile user ID.

add_action( 'profileswitch_edit_profile_form_fields', function( $profile_id ) {
    $value = get_user_meta( $profile_id, 'my_nickname', true );
    echo '<p>Nickname:</p>';
    echo '<input type="text" name="my_nickname_field" value="' . esc_attr( $value ) . '">';
} );

profileswitch_edit_profile_save

Fires after an existing profile is updated. Use this to save data from custom fields added via profileswitch_edit_profile_form_fields.

add_action( 'profileswitch_edit_profile_save', function( $profile_id ) {
    if ( isset( $_POST['my_nickname_field'] ) ) {
        update_user_meta( $profile_id, 'my_nickname', sanitize_text_field( $_POST['my_nickname_field'] ) );
    }
} );

profileswitch_edit_profile_after_form

Fires at the bottom of the Edit Profile page, after the form and the PIN section. Use it to render additional content for the profile being edited, such as custom settings that live outside the main form. Receives the profile user ID. Added in ProfileSwitch 1.4.6.

add_action( 'profileswitch_edit_profile_after_form', function( $profile_id ) {
    echo '<h3>Viewing Preferences</h3>';
    // Render your own form or content for this profile
} );

Manage Account section hooks

These hooks fire between sections of the frontend Manage Account panel. Each receives the primary profile user ID. Use them to add your own sections to the panel.

profileswitch_manage_account_after_managers

Fires after the Managers section.

add_action( 'profileswitch_manage_account_after_managers', function( $primary_id ) {
    // Add custom content to the Manage Account page
} );

profileswitch_manage_account_after_parental

Fires after the Parental Controls section. This hook fires even when parental controls are disabled site-wide, so you do not need to mirror that check. Added in ProfileSwitch 1.4.10.

add_action( 'profileswitch_manage_account_after_parental', function( $primary_id ) {
    // Add custom content to the Manage Account page
} );

profileswitch_manage_account_after_profiles

Fires after the Linked Profiles section.

add_action( 'profileswitch_manage_account_after_profiles', function( $primary_id ) {
    // Add custom content to the Manage Account page
} );

Filters

Filters let you change ProfileSwitch behavior and content. Hook in with add_filter() and return the (possibly modified) value.

Profiles & settings

profileswitch_max_profiles

Filter the maximum number of profiles allowed per account. The default value comes from the General settings tab (default 10). The Paid Memberships Pro integration also runs through this filter when per-level profile limits are enabled.

// Allow up to 20 profiles per account
add_filter( 'profileswitch_max_profiles', function( $max ) {
    return 20;
} );

// Different limits per user role
add_filter( 'profileswitch_max_profiles', function( $max ) {
    if ( current_user_can( 'manage_options' ) ) {
        return 50;
    }
    return $max;
} );

profileswitch_settings_tabs

Filter the tabs shown on the ProfileSwitch settings page. You can add, remove, or reorder tabs.

// Add a custom settings tab
add_filter( 'profileswitch_settings_tabs', function( $tabs ) {
    $tabs['my-custom-tab'] = 'My Custom Tab';
    return $tabs;
} );

Login & redirects

profileswitch_redirect_to_switcher_on_login

Filter whether the user is redirected to the profile switcher after logging in. Return false to skip the redirect and let WordPress send the user to its normal post-login destination. Receives the WP_User who logged in and the validated destination URL (empty string if none). Useful when another plugin’s flow, such as an email activation link, needs to finish at its own URL before the switcher takes over. Default: true. Added in ProfileSwitch 1.4.10.

// Skip the switcher when heading to a specific destination
add_filter( 'profileswitch_redirect_to_switcher_on_login', function( $do_redirect, $user, $redirect_to ) {
    if ( strpos( $redirect_to, '/account-activation/' ) !== false ) {
        return false;
    }
    return $do_redirect;
}, 10, 3 );

profileswitch_redirect_to_switcher_on_force_selection

When PINs or parental controls are active, ProfileSwitch requires each session to pick a profile and redirects logged-in users to the switcher until they do. Return false to let a specific request through without forcing profile selection. Receives the current user ID and the request URI. This is the non-login counterpart to profileswitch_redirect_to_switcher_on_login; integrations that need to exempt a URL should usually hook both. Default: true. Added in ProfileSwitch 1.4.11.

// Let a specific URL through without forcing profile selection
add_filter( 'profileswitch_redirect_to_switcher_on_force_selection', function( $do_redirect, $user_id, $current_url ) {
    if ( strpos( $current_url, '/account-activation/' ) !== false ) {
        return false;
    }
    return $do_redirect;
}, 10, 3 );

profileswitch_auto_switch_from_url

Profile deep links (?profileswitch_to=<id>) normally show a confirmation panel before switching. Return true to perform the switch silently instead. PIN-protected profiles still route through the PIN prompt regardless. Receives the target profile ID and the current user ID. Security note: enabling this allows an external page to trigger switches between profiles the visitor already owns. There is no privilege escalation and PIN-protected targets stay protected, but enable with awareness. Default: false. Added in ProfileSwitch 1.5. See also profileswitch_get_switch_url() below.

// Switch immediately when a deep link is followed
add_filter( 'profileswitch_auto_switch_from_url', '__return_true' );

PINs & parental controls

profileswitch_can_set_pin

Filter whether a specific profile is allowed to set a PIN. Return false to hide the “Set a PIN” option and block the form handler. Default: true.

// Prevent a specific profile from setting a PIN
add_filter( 'profileswitch_can_set_pin', function( $allowed, $profile_id ) {
    if ( $profile_id === 42 ) {
        return false;
    }
    return $allowed;
}, 10, 2 );

profileswitch_pin_override_allowed

Filter whether a primary profile or manager’s PIN can override a specific profile’s PIN. Receives the target profile ID and the primary profile ID. Return false to require the target’s own PIN. Default: true.

// Prevent PIN override for a specific profile
add_filter( 'profileswitch_pin_override_allowed', function( $allowed, $target_profile_id, $primary_id ) {
    if ( $target_profile_id === 42 ) {
        return false;
    }
    return $allowed;
}, 10, 3 );

profileswitch_max_pin_attempts

Filter the number of failed PIN attempts before lockout. Default: 5.

// Allow 10 attempts before lockout
add_filter( 'profileswitch_max_pin_attempts', function( $attempts ) {
    return 10;
} );

profileswitch_pin_lockout_duration

Filter the lockout duration (in seconds) after too many failed PIN attempts. Default: 60 (1 minute).

// Lock out for 5 minutes instead of 1
add_filter( 'profileswitch_pin_lockout_duration', function( $duration ) {
    return 5 * MINUTE_IN_SECONDS;
} );

profileswitch_pin_session_ttl

Filter how long a PIN verification session lasts before the user needs to re-enter their PIN for protected pages. Default: 3600 (1 hour).

// Require PIN re-entry every 15 minutes
add_filter( 'profileswitch_pin_session_ttl', function( $ttl ) {
    return 15 * MINUTE_IN_SECONDS;
} );

profileswitch_manage_account_parental_description

Filter the description HTML shown in the Parental Controls section of the Manage Account panel. The default text depends on whether parental controls are active for the account and whether the primary profile has a PIN set; both states are passed to the callback. Returned HTML runs through wp_kses_post, so you can return paragraphs, lists, or other block-level markup. Added in ProfileSwitch 1.4.9.

// List the protected pages under the default description
add_filter( 'profileswitch_manage_account_parental_description', function( $description, $primary_id, $active, $has_pin ) {
    if ( $active ) {
        $description .= '<p>Protected pages: Course Library, Store.</p>';
    }
    return $description;
}, 10, 4 );

Emails

ProfileSwitch sends two emails: the PIN reset email (sent from the “Forgot PIN?” link) and the account management verification email (a 6-digit code that unlocks the Manage Account panel). Each has subject, message, and headers filters, added in ProfileSwitch 1.4.7.

profileswitch_pin_reset_email_subject

Filter the subject of the PIN reset email. Receives the subject, the WP_User whose PIN is being reset, and the one-time reset URL.

add_filter( 'profileswitch_pin_reset_email_subject', function( $subject, $target_profile, $reset_url ) {
    return 'Reset your profile PIN';
}, 10, 3 );

profileswitch_pin_reset_email_message

Filter the body of the PIN reset email. Receives the message, the WP_User whose PIN is being reset, and the one-time reset URL. Any custom message should include $reset_url or the recipient will have no way to complete the reset.

add_filter( 'profileswitch_pin_reset_email_message', function( $message, $target_profile, $reset_url ) {
    return "A PIN reset was requested for your profile.\n\nReset it here: " . $reset_url;
}, 10, 3 );

profileswitch_pin_reset_email_headers

Filter the headers of the PIN reset email. Receives an array of headers (empty by default) and the WP_User whose PIN is being reset.

add_filter( 'profileswitch_pin_reset_email_headers', function( $headers, $target_profile ) {
    $headers[] = 'From: My Site <[email protected]>';
    return $headers;
}, 10, 2 );

profileswitch_account_verification_email_subject

Filter the subject of the account management verification email. Receives the subject, the primary profile WP_User receiving the email, and the 6-digit verification code.

add_filter( 'profileswitch_account_verification_email_subject', function( $subject, $user, $code ) {
    return 'Your account management code: ' . $code;
}, 10, 3 );

profileswitch_account_verification_email_message

Filter the body of the account management verification email. Receives the message, the primary profile WP_User, and the 6-digit code. Any custom message should include $code.

add_filter( 'profileswitch_account_verification_email_message', function( $message, $user, $code ) {
    return "Hi " . $user->display_name . ",\n\nYour verification code is: " . $code;
}, 10, 3 );

profileswitch_account_verification_email_headers

Filter the headers of the account management verification email. Receives an array of headers (empty by default) and the primary profile WP_User.

add_filter( 'profileswitch_account_verification_email_headers', function( $headers, $user ) {
    $headers[] = 'From: My Site <[email protected]>';
    return $headers;
}, 10, 2 );

Functions

These functions are available for use in themes, plugins, and custom integrations. All of them live in the global namespace and are defined once plugins are loaded.

Profile queries

profileswitch_get_profiles( $user_id )

Returns an array of WP_User objects for all profiles in the account, with the primary profile first. Pass any profile’s user ID and it resolves to the full set.

$profiles = profileswitch_get_profiles( get_current_user_id() );
foreach ( $profiles as $profile ) {
    echo esc_html( $profile->display_name );
}

profileswitch_get_primary_profile_id( $profile_id )

Returns the primary profile’s user ID for any sub-profile. If the user is already the primary (or has no linked profiles), returns their own ID.

$primary_id = profileswitch_get_primary_profile_id( $user_id ); // Returns: 1

profileswitch_is_user_primary_profile( $user_id )

Returns true if the user is a primary profile, meaning they are not a sub-profile of another account. Note that standalone users with no linked profiles also count as primary.

if ( profileswitch_is_user_primary_profile( get_current_user_id() ) ) {
    // Show primary-level controls
}

profileswitch_can_user_switch_to_profile( $user_id, $profile_id )

Returns true if the given user is allowed to switch to the specified profile (i.e. they belong to the same profile set).

if ( profileswitch_can_user_switch_to_profile( $current_user_id, $target_profile_id ) ) {
    profileswitch_switch_to_profile( $target_profile_id );
}

Switching & linking

profileswitch_switch_to_profile( $profile_id )

Programmatically switch the current user to the specified profile. Logs out of the current profile and logs into the target profile. Returns true on success or a WP_Error on failure (not logged in, profile not found, or the target is not in the current user’s profile set). Because it destroys the current session and sets new auth cookies, call it before any output is sent.

$result = profileswitch_switch_to_profile( $profile_id );
if ( is_wp_error( $result ) ) {
    error_log( $result->get_error_message() );
}

profileswitch_set_primary_profile( $profile_id, $primary_profile_id )

Link a user to a primary profile. This makes $profile_id a sub-profile of $primary_profile_id and fires profileswitch_profile_linked. Pass an empty value (or the profile’s own ID) as the second argument to unlink the profile and make it standalone, which fires profileswitch_profile_unlinked.

// Link user 23 as a sub-profile of user 1
profileswitch_set_primary_profile( 23, 1 );

// Unlink user 23 so it becomes a standalone account
profileswitch_set_primary_profile( 23, 0 );

profileswitch_get_switcher_url()

Returns the URL of the profile switcher page. Falls back to home_url() if no switcher page is configured.

$url = profileswitch_get_switcher_url(); // Returns: "https://example.com/select-profile/"

profileswitch_get_switch_url( $profile_id, $redirect_to = ” )

Build a deep-link URL that targets a specific profile. Intended for external integrations, such as notification emails, that want to send a user to a destination with a profile preselected. The recipient lands on a confirmation panel for that profile (or its PIN prompt) and then continues to $redirect_to. If $redirect_to is empty, the switcher page is used. Recipients can only be switched among profiles they already own. Sites can opt into silent switching via the profileswitch_auto_switch_from_url filter above. Added in ProfileSwitch 1.5.

// Email a link that opens the dashboard as a specific profile
$url = profileswitch_get_switch_url( $child_profile_id, home_url( '/dashboard/' ) );

PINs & parental controls

See the PINs & Parental Controls documentation for how these features work from the user’s perspective.

profileswitch_is_profile_pins_enabled()

Returns true if the Profile PINs feature is enabled globally in settings.

profileswitch_profile_has_pin( $profile_id )

Returns true if the specified profile has a PIN set.

profileswitch_requires_pin_to_switch( $target_profile_id )

Returns true if switching to the profile requires a PIN, meaning the PINs feature is enabled and the profile has one set.

if ( profileswitch_requires_pin_to_switch( $profile_id ) ) {
    // Prompt for a PIN before switching
}

profileswitch_set_profile_pin( $profile_id, $pin )

Set a profile’s PIN programmatically. The plaintext PIN is hashed before storage and is not kept. The stored PIN type (numeric or alphanumeric) follows the current PIN Type setting.

profileswitch_set_profile_pin( $profile_id, '1234' );

profileswitch_remove_profile_pin( $profile_id )

Remove the PIN from a specific profile.

profileswitch_verify_switch_pin( $target_profile_id, $pin )

Check a plaintext PIN against a target profile. Accepts the target’s own PIN, and (unless disabled via the profileswitch_pin_override_allowed filter) the primary’s or a manager’s PIN as an override. This is the same gate the switcher uses: failed calls count toward the attempt lockout, and a locked-out profile set always returns false, so do not call it speculatively.

if ( profileswitch_verify_switch_pin( $profile_id, $submitted_pin ) ) {
    profileswitch_switch_to_profile( $profile_id );
}

profileswitch_is_pin_locked_out( $user_id )

Returns true if the profile set is currently locked out due to too many failed PIN attempts. Accepts any user ID in the set.

profileswitch_is_pin_verified_for_session()

Returns true if the current session has already passed a PIN check for protected pages. The verification lasts for the duration set by the profileswitch_pin_session_ttl filter (1 hour by default). Useful when building custom PIN-protected content.

if ( ! profileswitch_is_pin_verified_for_session() ) {
    // This session has not entered the parental PIN yet
}

profileswitch_is_parental_controls_enabled()

Returns true if the Parental Controls feature is enabled globally in settings. This does not mean a specific account has activated it; use profileswitch_user_has_parental_controls() for that.

profileswitch_user_has_parental_controls( $user_id )

Returns true if the specified user’s account has parental controls actively in effect. That requires the site-wide feature toggle, the account-level toggle, and a PIN on the primary profile. Works with any profile in the set; it resolves to the primary automatically.

if ( profileswitch_user_has_parental_controls( get_current_user_id() ) ) {
    // This user's account has parental controls active
}

profileswitch_enable_parental_controls( $user_id )

Turn on parental controls for the account containing $user_id. Parental controls only take effect once the primary profile also has a PIN set.

profileswitch_disable_parental_controls( $user_id )

Turn off parental controls for the account containing $user_id.

profileswitch_get_protected_pages()

Returns an array of page IDs configured as protected in the PINs & Parental Controls settings tab.

profileswitch_is_protected_page( $page_id )

Returns true if the specified page is in the protected pages list.

if ( profileswitch_is_protected_page( get_the_ID() ) ) {
    // This page requires a PIN for sub-profiles
}

Profile managers

profileswitch_is_profile_managers_enabled()

Returns true if the Profile Managers feature is enabled in settings.

profileswitch_is_profile_manager( $user_id )

Returns true if the user is a primary profile or a designated manager. When the Profile Managers feature is disabled, this returns the same result as profileswitch_is_user_primary_profile().

if ( profileswitch_is_profile_manager( get_current_user_id() ) ) {
    // User is the primary profile or a designated manager
}

profileswitch_update_managers( $primary_id, $manager_ids )

Updates the list of manager profiles for the given primary account. Pass an array of user IDs. Fires profileswitch_manager_added and profileswitch_manager_removed hooks as appropriate.

// Make users 23 and 45 managers of account 1
profileswitch_update_managers( 1, array( 23, 45 ) );

Account management

profileswitch_is_account_verified( $primary_id )

Returns true if the current session has been verified for the given primary profile via the account management email verification flow. The verified session lasts 30 minutes.

profileswitch_generate_profile_email( $primary_email, $profile_name )

Generate a unique plus-addressed email for a new profile from the primary’s email and the profile name (e.g. [email protected]). Appends a number if the address is taken. Returns the email string, or false if an unused address could not be generated.

$email = profileswitch_generate_profile_email( '[email protected]', 'Kid' );
// Returns: "[email protected]"

profileswitch_is_plus_addressed_email( $profile_email, $primary_email )

Returns true if the profile email is a plus-addressed version of the primary email (same local part and domain, with a +suffix). Useful for determining whether a profile was auto-generated or has custom credentials.

$primary_id = profileswitch_get_primary_profile_id( $user_id );
$primary    = get_userdata( $primary_id );
$profile    = get_userdata( $user_id );

if ( profileswitch_is_plus_addressed_email( $profile->user_email, $primary->user_email ) ) {
    // This profile uses an auto-generated email
}

Avatars

profileswitch_get_preset_avatars()

Returns the array of attachment IDs configured as preset avatars on the Design settings tab.

profileswitch_get_profile_avatar_id( $user_id )

Returns the attachment ID of the profile’s selected avatar, or 0 if none is set.

profileswitch_set_profile_avatar( $user_id, $attachment_id )

Set a profile’s avatar. The attachment must be one of the configured preset avatars or the call is ignored. Pass 0 to clear the avatar and fall back to the default.

// Assign the first preset avatar to a profile
$presets = profileswitch_get_preset_avatars();
if ( ! empty( $presets ) ) {
    profileswitch_set_profile_avatar( $profile_id, $presets[0] );
}

Need a hook or function that doesn’t exist yet? Contact us and we’ll consider adding it in a future release.