wp_mail goes out through PHP mail() until phpmailer_init says otherwise

wp_mail() is PHPMailer configured to use PHP’s mail(), which hands the message to whatever MTA is on the box. The message therefore leaves from the web server, as the web server’s user, from a host that appears in nobody’s SPF record — which is why the password reset arrives in spam or does not arrive at all. phpmailer_init is where the transport is changed.

add_action( 'phpmailer_init', function ( $phpmailer ) {
    $phpmailer->IsSMTP();
    $phpmailer->Host       = 'smtp.relay.internal';
    $phpmailer->Port       = 587;
    $phpmailer->SMTPAuth   = true;
    $phpmailer->SMTPSecure = 'tls';
    $phpmailer->Username   = CATALOGUE_SMTP_USER;
    $phpmailer->Password   = CATALOGUE_SMTP_PASS;
} );

add_filter( 'wp_mail_from', function () { return '[email protected]'; } );
add_filter( 'wp_mail_from_name', function () { return 'Catalogue'; } );

The hook fires with the fully configured PHPMailer instance immediately before send(), and it is dispatched by reference — there is nothing to return, you mutate the object. Two consequences. The callback runs for every message the site sends, including ones from plugins with their own headers, so anything conditional has to inspect the instance rather than assume. And the credentials belong in wp-config.php as constants, not in a theme that gets exported and shared. The header filters matter as much as the transport: relays reject or rewrite a From address they are not authorised for, so setting one that matches the sending domain is what actually fixes deliverability. Note that wp_mail() returns only whether PHPMailer accepted the message for delivery — everything after that is the relay’s log, not WordPress’s.