Ettic Docs
MagicAuthDevelopers

Programmatic API

Issue and send magic links from your own code.

Two public static methods cover most third-party integration use cases:

  • MagicAuth\Auth\TokenManager::issue(). Mint a token row, return the verify URL and code.
  • MagicAuth\Email\Mailer::send_magic_link(). Render and send the standard email for an existing token.

When to use the API

  • WooCommerce post-purchase: send a sign-in link in the order confirmation email so first-time guests can claim their account.
  • Membership plugins onboarding: generate and email a magic link as part of welcome flow.
  • Custom CLI scripts: hand-deliver access without going through the form.
  • Internal tools: issue a one-time link to onboard a colleague without sharing a password.

Basic call

use MagicAuth\Auth\TokenManager;

$result = TokenManager::issue( $user_id, $user_email );

if ( is_wp_error( $result ) ) {
    // User is disabled, the row insert failed, or the user is not allowed to use magic-link sign-in.
    error_log( $result->get_error_message() );
    return;
}

// $result is an array:
// [
//   'link_url'       => 'https://example.com/?magicauth=verify&s=...&v=...',
//   'code_plaintext' => 'X3YK7Q',
//   'selector'       => '0123456789abcdef',
//   'expires_at'     => '2026-05-05 14:30:00', // GMT, MySQL datetime.
// ]

What issue() does

  1. Pre-empts any prior outstanding tokens for the same email (no double-stacking).
  2. Generates a 16-char selector, a 256-bit link verifier, and a 40-bit Crockford-base32 code.
  3. Stores HMAC-SHA256 hashes of both verifiers. Plaintext is returned to you and never stored.
  4. Records email_hmac, ip_hmac (truncated), and timestamps.
  5. Fires the magicauth_token_issued action.
  6. Returns the array above on success, WP_Error on failure.

The expiry follows your Link & code lifetime setting (default 10 minutes).

Standard email pipeline

TokenManager::issue() does not send an email. To use MagicAuth's standard email pipeline (templates, branding, deliverability filters):

use MagicAuth\Auth\TokenManager;
use MagicAuth\Email\Mailer;

$result = TokenManager::issue( $user_id, $user_email );

if ( is_wp_error( $result ) ) {
    return;
}

Mailer::send_magic_link(
    $user_id,
    $result['link_url'],
    $result['code_plaintext'],
    $result['expires_at']
);

This goes through the full filter pipeline: magicauth_email_template_args, magicauth_email_html, magicauth_email_subject, magicauth_email_send, etc. See Email customisation.

Hand-delivering outside email

When you want to message someone the link via Slack, 1Password, or any non-email channel, don't send the email at all. Just consume the URL from issue():

$result = TokenManager::issue( $user_id, $user_email );

if ( is_wp_error( $result ) ) {
    return;
}

slack_dm( $slack_user_id, sprintf(
    "Here's your sign-in link (expires in %d minutes): %s",
    10,
    $result['link_url']
) );

The link works the same way as one delivered by email. The user clicks, MagicAuth validates the verifier, and they're signed in.

Diagnostic test email

Mailer::send_test() renders the magic-link email with a placeholder selector and code, so the recipient sees what their template looks like without a real token being issued.

use MagicAuth\Email\Mailer;

Mailer::send_test( $user_id );

This is what the Send test email button in Diagnostics & Recovery calls.

Invalidation

Three helpers for invalidating outstanding tokens en masse:

use MagicAuth\Auth\TokenManager;

// Revoke every outstanding token for a single user.
$count = TokenManager::invalidate_outstanding_for_user( $user_id );

// Revoke every outstanding token for a hashed email (for privacy-eraser flows).
$count = TokenManager::invalidate_outstanding_for_email(
    magicauth_hash_email( $user_email )
);

// Revoke every outstanding token across the site.
$count = TokenManager::invalidate_all_outstanding();

All three return the number of rows affected. None of them sign live sessions out. They only invalidate unconsumed links and codes.

Error handling

TokenManager::issue() returns WP_Error for these cases:

Error codeCause
magicauth_user_disabledThe user has the magicauth_disabled user-meta flag set, or their role is in the Require password for list. The error message is the generic enumeration envelope. Do not surface it directly to end users.
internal db errorThe row insert failed. The error message is the generic envelope. Check WP_DEBUG_LOG for the underlying SQL failure.

Mailer::send_magic_link() returns bool: the wp_mail return value (or the filtered short-circuit's cast value). Check false and log appropriately.

Safety

The programmatic API does not enforce permission or rate-limit checks. The caller is responsible for both.

Validate the actor

Always validate the user before calling issue(). It does not check whether the requesting actor has permission to sign in as that user.

Treat outputs as secrets

link_url and code_plaintext authenticate. Don't log them, don't put them in URL parameters of unrelated pages, don't store them.

Don't bypass throttles

issue() does not consult the per-IP / per-email throttle. If you're processing user-supplied input, check MagicAuth\Auth\Throttle first or implement your own rate limiting.

On this page