FlowMattic Magic Link & FluentCRM Tags

In the last two weeks, two of the plugins I use have had some updates added to them. I immediately saw a use case that I could build, document, and record a video on, and this article is the written part of this workflow.

Firstly, Conditional Block Pro introduced WPFusion and Fluentcrm tag support for conditionally displaying blocks based on whether a user has tags or not, and then later in the week, FlowMattic launched version 5.1.1, which introduced the Magic Link function

Immediate Idea

When I saw these two releases in such quick succession, I immediately had an idea of a workflow. Similar ones previously were more complex to set up as they used custom user meta and other fields to deliver this, but the use case comes up enough where you could find prolonged use for this sort of workflow.

Flowmattic magic link fluentcrm woo

The premise is simple: we want to offer users who have access to a backend area a single-use free upgrade based on whether they have a tag or not, and conditionally display the blocks.

The Blocks

In my example, I am using FluentCRM and Conditional Blocks Pro; you could also use WPFusion and Block Visibility with WPFusion

The check is simple here, display the shortcode to allow the free coupon generation IF the user doesn’t have the upgraded tag, there is a guide to do this here in Conditional Blocks website

In bricks, you could use conditional I thought BricksExtra already had this, but they don’t. I have since sent in a feature request to add FluentCRM and WPfusion

I added a small data class to the shortcode used for the magic link class=”hide-on-click”. The reason for this is so I could automatically hide and reload the page once it was clicked,d so they saw the already redeemed link. The code for that data class is below

// 1) Enqueue AJAX script
function hoc_enqueue_ajax_script() {
    if ( is_user_logged_in() ) {
        wp_enqueue_script( 'hoc-ajax', '', [ 'jquery' ], null, true );
        wp_localize_script( 'hoc-ajax', 'HOC_Ajax', [
            'ajax_url' => admin_url( 'admin-ajax.php' ),
            'nonce'    => wp_create_nonce( 'hoc_click' ),
        ] );
        wp_add_inline_script( 'hoc-ajax', <<<'JS'
jQuery(function($){
  $('.hide-on-click').on('click', function(){
    var linkId = $(this).data('link-id');
    $.post(HOC_Ajax.ajax_url, {
      action: 'hoc_mark_clicked',
      nonce: HOC_Ajax.nonce,
      link_id: linkId
    });
    $(this).hide();
  });
});
JS
        );
    }
}
add_action( 'wp_enqueue_scripts', 'hoc_enqueue_ajax_script' );

// 2) Ajax handler: save to user meta
function hoc_mark_clicked() {
    check_ajax_referer( 'hoc_click', 'nonce' );
    if ( ! is_user_logged_in() || empty( $_POST['link_id'] ) ) {
        wp_send_json_error();
    }
    $user_id = get_current_user_id();
    $clicked = get_user_meta( $user_id, 'hoc_clicked_links', true ) ?: [];
    if ( ! in_array( $_POST['link_id'], $clicked, true ) ) {
        $clicked[] = sanitize_text_field( wp_unslash( $_POST['link_id'] ) );
        update_user_meta( $user_id, 'hoc_clicked_links', $clicked );
    }
    wp_send_json_success();
}
add_action( 'wp_ajax_hoc_mark_clicked', 'hoc_mark_clicked' );

// 3) Filter content to hide links already clicked
function hoc_filter_content( $content ) {
    if ( is_user_logged_in() ) {
        $clicked = get_user_meta( get_current_user_id(), 'hoc_clicked_links', true ) ?: [];
        // remove any anchors whose data-link-id is in the clicked list
        foreach ( $clicked as $id ) {
            // simple regex to strip that link (assumes no nested <a>)
            $pattern = sprintf(
              '#<a\b([^>]*?)\sdata-link-id=["\']%s["\'].*?<\/a>#i',
              preg_quote( $id, '#' )
            );
            $content = preg_replace( $pattern, '', $content );
        }
    }
    return $content;
}
add_filter( 'the_content', 'hoc_filter_content', 20 );

PHP

The WorkFlow

Over in flowmattic, I set up the workflow, and start it off with our new magic link trigger

Magic link tutorial flowmattic workflow

The next step is a custom PHP function to take the email address of the logged-in user (with our magic link) and run a check to see if they have the tag we are looking for In this case, we are looking for a tag slug of “upgrade-offer”

Magic link tutorial flowmattic workflow php

The code for that snippet is in the code block below, and this will accept the same parameters and check if a user has a tag, returning true or false against the check.

/**
 * Name: FluentCRM User Tag Helpers
 * Description: Helper functions to retrieve and check FluentCRM tags for a user by email or WP user ID.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly
}

/**
 * Initialize our helpers after all plugins are loaded.
 */
add_action( 'plugins_loaded', 'fcrm_user_tag_helpers_init', 20 );
function fcrm_user_tag_helpers_init() {
    // Ensure FluentCRM is active
    if ( ! function_exists( 'FluentCrmApi' ) ) {
        add_action( 'admin_notices', function() {
            echo '<div class="notice notice-error"><p><strong>FluentCRM User Tag Helpers</strong>: FluentCRM plugin is not active. Please install and activate FluentCRM.</p></div>';
        } );
        return;
    }

    /**
     * Retrieve all tag slugs for a FluentCRM contact by email or WP user ID.
     *
     * @param int|string $user_identifier WP user ID or email address.
     * @return string[] Array of tag slugs (empty if none or contact not found).
     */
    function get_fluentcrm_user_tags( $user_identifier ) {
        $contactApi = FluentCrmApi( 'contacts' );
        $subscriber = is_numeric( $user_identifier )
            ? $contactApi->getContactByUserRef( absint( $user_identifier ) )
            : $contactApi->getContact( sanitize_email( $user_identifier ) );

        if ( ! $subscriber ) {
            return [];
        }

        return $subscriber->tags->pluck( 'slug' )->toArray();
    }

    /**
     * Check if a FluentCRM contact has a specific tag slug.
     *
     * @param int|string $user_identifier WP user ID or email address.
     * @param string     $tag_slug        The tag slug to check.
     * @return bool     True if the contact has the tag; false otherwise.
     */
    function user_has_fluentcrm_tag( $user_identifier, $tag_slug ) {
        // Get all tag slugs for this user
        $tags = get_fluentcrm_user_tags( $user_identifier );
        // Check for slug in array
        return in_array( sanitize_title( $tag_slug ), $tags, true );
    }
}
PHP

We then have a router checking if we get a true or false return from the PHP snippet, we then add a coupon that expires in a week, can only be used once and is limited to that user with a description of the user’s email and the fact it was for a free upgrade.

We then use the Fluentcrm email template to send out an email containing the coupon link, in case our link doesn’t work, and for our link, we generate our coupon and then have a site.com/?product=X&coupon=Y URL with X being the product number and Y being the generated code.

I use a custom WooCommerce improvement plugin for this, but there are several options available to you

You are ideally looking for functionality to clear the cart, add the product to the cart and auto-assign the coupon, to make the flow as frictionless and friendly as possible.

Once we’ve generated and sent our email, we have offered the upgrade, and we apply our tag to the user using create or update user (in case they are not already in the CRM) of “upgrade-offer”, and they are now stopped from running this flow again.

Note: if you were deploying this in a real-world solution, I would probably recommend a small delay between running them if a secondary tag is applied and not removed, such as (in progress upgrade). If this tag applies, wait 50 seconds before continuing and checking whether they have the tag. This would prevent double issues for double clicks of the button.

You can watch the video showing all the above and the workflow below:

Support the Author

buy me a coffee
Really Useful Plugin Logo
Wpvideobank temp
Appoligies for any spelling and grammer issue. As a dyslexic i need to rely on tools for this they like me are not perfect but I do try my best