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;
} );
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.
magic_link_pre_send_login_link
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.
Link creation and sending
kc_ml_pre_generate_magic_link
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.
kc_ml_validate_email_for_magic_link
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.
magic_link_send_magic_link (action)
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.