Skip to content

Developers

Magic Link Hooks and Filters Reference for Developers

Magic Link exposes WordPress actions and filters so developers can change how login links are requested, sent, checked, and recorded. This page lists the hooks that exist in version 2.3.0, with their arguments and a short example. Hooks marked PRO are only available when the PRO plugin is active.

Add your code to a small plugin, a file in wp-content/mu-plugins/, or a child theme's functions.php, never to a parent theme.

Shortcodes and constants

Name What it does
[magic_link_form], [magic-link], [magic_link] Three names for the same login form shortcode. Attributes: redirect, show_registration. See shortcode login form.
MAGIC_LINK_USERNAME_ONLY Define as true in wp-config.php (before the plugin loads) to make the login form ask for a Username only. Email addresses are then not accepted. Default false.

Login screen filters

magic_link_before_login_form_redirect

Skip the Force Magic Link Login redirect. Return any value other than null to show the normal WordPress login form.

add_filter( 'magic_link_before_login_form_redirect', function ( $skip ) {
	return ( isset( $_GET['classic'] ) && 'my-secret' === $_GET['classic'] ) ? true : $skip;
} );

See Force Magic Link login.

magic_link_get_wp_login_url

apply_filters( 'magic_link_get_wp_login_url', string $url ). The URL of the Magic Link form, by default site_url( 'wp-login.php?action=magic_link' ). Change it if your login page lives somewhere else.

magic_link_info_message

apply_filters( 'magic_link_info_message', string $html, array $args ). The blue message above the login form (Please enter your username or email address...). Return your own HTML paragraph, for example <p class="message">...</p>.

magic_link_missing_user_error_message

apply_filters( 'magic_link_missing_user_error_message', string $message, string $error_code, array $args ). The error shown when no account matches ($error_code is missing_user).

magic_link_pre_process_login_request

apply_filters( 'magic_link_pre_process_login_request', null ). Return a WP_Error to stop the request before a link is created, for example to add your own CAPTCHA check. The error is shown on the form.

apply_filters( 'magic_link_pre_send_login_link', null, WP_User|false $user ). Return a WP_Error to refuse sending a link to this user. Return null to continue.

magic_link_process_login_request_result

apply_filters( 'magic_link_process_login_request_result', array $response, array $args ). Final chance to change the result array (show_form, errors, info, is_processed, and so on) before the form is rendered.

magic_link_form (action)

Fires inside the Magic Link form, after the username/email field and before the submit button. Use it to add an extra field such as a CAPTCHA widget.

apply_filters( 'kc_ml_pre_generate_magic_link', null ). Return a WP_Error to block creation of any link. PRO uses this for brute-force and throttling checks.

apply_filters( 'kc_ml_validate_email_for_magic_link', null, string $email ). Return a WP_Error to reject an email address. PRO uses this for Domain Restriction.

kc_ml_allow_self_registration

apply_filters( 'kc_ml_allow_self_registration', true, string $email ). Return false to stop Magic Link creating a new account for an unknown email.

kc_ml_new_user_default_role

apply_filters( 'kc_ml_new_user_default_role', '' ). Return a role slug for accounts created by Magic Link, for example 'customer'. An empty string uses the WordPress default.

kc_ml_email_body

apply_filters( 'kc_ml_email_body', string $html, array $vars ). The finished email body after merge tags are replaced. $vars holds user (WP_User), magic_link, and subject. Branding uses this filter.

do_action( 'magic_link_send_magic_link', WP_User $user ). Fires just before the email is passed to wp_mail().

kc_ml_resend_cooldown

apply_filters( 'kc_ml_resend_cooldown', int $seconds ). Seconds a visitor must wait before requesting another link from the shortcode or block form (default 30).

kc_ml_trusted_ip_headers

apply_filters( 'kc_ml_trusted_ip_headers', array $headers ). Ordered list of $_SERVER keys to trust before REMOTE_ADDR when reading the visitor IP. Empty by default, which is the safe choice. Only add a header your infrastructure sets and clients cannot forge.

Logging in

kc_ml_pre_login_check

apply_filters( 'kc_ml_pre_login_check', true, array $context ). Runs when a link is clicked, before the user is logged in. $context has user_id, token_row, ip, ts. Return a WP_Error to refuse the login (its message is shown to the visitor) or true to allow it. PRO Access Control (IP, country, time window, usage limit) is built on this filter.

kc_ml_handle_login_request (action)

Fires after the Confirm sign-in step and before the link is validated.

magic_link_before_login and magic_link_login (actions)

do_action( 'magic_link_before_login', WP_User $user, array $extra ) fires just before the login cookie is set. do_action( 'magic_link_login', string $user_login, WP_User $user ) fires after it. Use these to run code whenever someone signs in with a magic link.

add_action( 'magic_link_login', function ( $user_login, $user ) {
	update_user_meta( $user->ID, 'last_passwordless_login', time() );
}, 10, 2 );

kc_ml_redirect_to_url

apply_filters( 'kc_ml_redirect_to_url', string $url, WP_User $user ). The page the user is sent to after login. PRO uses it for role-based redirects.

kc_ml_invalid_token and kc_ml_invalid_user (actions)

Fire when a link is invalid, expired, or already used, or the user cannot be found. They have no arguments. Brute-force protection counts them.

Events and the audit log

Hook Arguments Fires when
kc_ml_link_generated int $user_id, array $context A link is created by the shortcode/block, bulk claim, REST API, or MemberPress/PMPro integrations
kc_ml_link_opened int $user_id, array $context An admin-created link is opened
kc_ml_login_link_sent none The sign-in email was sent successfully
kc_ml_user_tokens_revoked int $user_id, int $removed A user's links are revoked
kc_ml_trusted_domain_user_created int $user_id, string $email, string $role PRO Trusted Domains created an account
kc_ml_audit_log_before_record string $event, array $context Before an audit-log row is written
kc_ml_audit_log_recorded int $id, string $event, array $context After an audit-log row is written. PRO webhooks listen here

Admin and extension filters

Hook Purpose
kc_ml_magic_links_bulk_actions apply_filters( ..., array $actions ). Add or remove bulk actions on Magic Link → Magic Links.
kc_ml_filter_general_settings, kc_ml_filter_branding_settings, kc_ml_filter_email_digest_settings Add or change fields on the General, Branding, and Reports settings tabs.
kc_ml_filter_settings_tab, kc_ml_filter_settings_sections Add settings tabs and sections.
kc_ml_integrations Change the list shown on the PRO Integrations tab.
kc_ml_load_integration apply_filters( ..., bool $enabled, string $slug ). Force a PRO integration on or off.

PRO-only hooks

Hook Purpose
kc_ml_geoip_resolver Supply a function fn( string $ip ): string that returns a two-letter country code. Required by the Country Allow List. See Restrict when links work.
kc_ml_rest_read_capability Capability needed to list or read links through the REST API (default list_users).
kc_ml_rest_write_capability Capability needed to create or revoke links (default edit_users). See REST API.
kc_ml_pro_claim_recent_login_window Seconds within which a user counts as "recently logged in" and is skipped by bulk claim emails (default 30 days).
kc_ml_outgoing_email_html_block, kc_ml_outgoing_email_plain_text_block Change the block that Outgoing Emails appends. Arguments: string $block, string $magic_link, string $link_text.

FAQs

Where should I put my hook code?

In a small plugin, an mu-plugins file, or a child theme. Code in a parent theme is erased by theme updates.

Are these hooks stable?

They are the hooks present in version 2.3.0. Check the changelog before updating if you rely on them.

Was this page helpful?

Previous
WP-CLI support