Skip to main content

In my development site, under Dynamic Segments, the Contacts column shows 10 WordPress Users. Could you kindly clarify what this number represents?

Currently, there are only 3 WordPress users on the development site, including the administrator account. As such, it is not clear how the total of 10 WordPress users is being calculated. Any clarification would be greatly appreciated ☺️

1.00

1.00

1.00

It would be great if we could attach documents to a contact and to a note, like contracts etc

I have a client request. He wants to send automated emails with links that expire after a set period (e.g., 7 days).

I'm not sure whether another service does this today, but it's something we could do in FluentCRM.

Hey everyone,

Wanted to see if anyone here has solved this before.

Right now, I am managing a large portfolio of 10+ websites, and each one has its own individual FluentCRM installation. The workflow is becoming incredibly tedious - if I want to check on an automation, verify a sequence, or just see what's happening with my lists, I have to log into every single site individually, navigate to the plugin, and dig through the settings.

It's a massive time sink.

Has anybody here managed to create (or found) a way to oversee all these FluentCRM installs from a central location? I am looking for a solution - even if it is a custom-built tool or a headless approach - that would allow me to monitor automations, check sequence statuses, or manage high-level data across all these sites from one single dashboard.

Love to hear if anyone has implemented a "master hub" approach or if you have any other clever workarounds for managing large-scale FluentCRM deployments without the constant site-hopping.

We shipped FluentCRM 3.2.5 today. This time we mostly worked on stability. We went through the edge cases you reported and fixed more than 30 of them. We also added a few small features along the way.

What's new

  • Lesson completion filter. If you sell courses, you can now filter learners by the lessons they've completed. This makes it easy to email people who stopped at a certain lesson.
  • Create companies from Fluent Forms entries. When someone submits a form, FluentCRM can link them to an existing company or create a new one. No more doing it by hand.
  • Safer CSV imports. There's a new "Update existing contacts only" option, plus a "Keep existing contact data" toggle. You can add data to your list without creating new contacts or overwriting existing ones.
  • Campaign archive layouts. You can now show archived campaigns as a grid or a list.

Improvements:

  • The campaigns list now shows how much revenue each campaign made
  • Renaming campaigns, automations and sequences now happens in a proper pop-up
  • Campaign reports and company contact counts load faster
  • WooCommerce variation filter now loads reliably on stores with a lot of variations
  • FluentCart purchase dates follow your FluentCRM date format

Fixes:

We fixed more than 30 reported bugs, including WhatsApp template variables, WooCommerce subscription variations, coupon expiry dates, automations with nested sequences, and emails with special characters in the From or Reply-To name.

Full details are here: https://fluentcrm.com/blog/fluentcrm-3-2-5/

Thanks to everyone who reported these edge cases. Most of the fixes in this release came from your reports. Please keep them coming.

More cooking πŸŽ‰

Hi FluentCRM / Fluent Forms team,

We found a consent-management gap while building a service lead funnel and wanted to suggest an improvement.

Environment:

  • FluentCRM 3.2.0
  • Fluent Forms 6.2.14
  • Fluent Forms Pro 6.2.14

Use case:

We have a service/contact form where submitting the form is operational and does NOT require consent for promotional marketing.

The form has a separate, optional marketing-consent checkbox.

Desired behavior:

  • New contact without marketing consent β†’ Transactional
  • New contact with explicit marketing consent β†’ Subscribed
  • Existing suppressed contacts should not be automatically reactivated
  • Pending should remain available for double opt-in workflows

FluentCRM already has a Transactional status, which is a good fit for service/operational contacts that should receive necessary communications but should not receive promotional campaigns.

Current limitation:

In the Fluent Forms β†’ FluentCRM β€œNew Form Submission” automation trigger, the available subscription-status choices do not provide a clean way to create a new contact as Transactional based on the form’s marketing-consent field.

The native Fluent Forms β†’ FluentCRM feed also does not provide conditional status mapping that cleanly handles this use case.

As a result, a service business has to either:

  1. mark a non-consented service lead as Subscribed,
  2. use Pending even when double opt-in is not the intended workflow, or
  3. add custom code to create the contact with the correct Transactional status.

Suggested improvement:

Could FluentCRM/Fluent Forms support:

  • Transactional as an available contact status in the Fluent Forms trigger/feed;

  • conditional status mapping based on a form field, for example:

    • consent_marketing = yes β†’ Subscribed
    • consent_marketing != yes β†’ Transactional;
  • an option such as β€œDo not change existing contact status” so submitting a service form cannot accidentally change an existing subscription/suppression state.

This would make it much easier to keep service communications separate from promotional consent while using the native Fluent ecosystem.

We reproduced the limitation on the versions listed above and currently need a small custom integration layer to model the consent correctly.

Would this be something you could consider adding to the native Fluent Forms ↔ FluentCRM integration?

Now that FluentCRM already supports SMS and WhatsApp messaging, I think it is becoming increasingly important to allow contacts or leads to be created without requiring an email address.

Currently, email is still mandatory when creating a FluentCRM contact. This creates a significant limitation, especially for B2B sales workflows.

In many real-world situations, the first piece of information we get from a lead is simply:

  • Name
  • Company
  • Phone number / WhatsApp

And sometimes we do not get the email until much later in the sales process.

If SMS and WhatsApp are becoming native communication channels inside FluentCRM, requiring an email before someone can even exist as a proper CRM contact feels increasingly inconsistent.

Ideally, FluentCRM could allow a contact to be created using either:

Email OR phone number as the primary identifier

and then enrich the contact later when additional information becomes available.

This would be especially useful for:

  • B2B prospecting
  • WhatsApp leads
  • SMS campaigns
  • Phone enquiries
  • Sales teams collecting leads manually
  • Events, networking and offline lead generation
  • Forms where asking for an email creates unnecessary friction

For us, this can become a real blocker when we have a perfectly valid lead and phone number, but simply do not have their email yet.

With Messaging already moving beyond email, I think the contact model should eventually do the same.

I'm trying to use WPVibe to build out my automation for me in FluentCRM and am getting an error. I think this is on the backed of FluentCRM, not with WPVibe.

Thoughts here Shahjahan JewelΒ 

β€œSecurity verification failed”
Cloudflare Ray ID: a42320c7ee8cfa06-ORD

I need to create multiple spaces for small discussion groups. I’d like to automate this, but I’ve noticed that this action isn’t available in FluentCRM’s automations.

Is there at least a way to create a β€œtemplate” for the space and copy it, so I don’t have to enter all the information each time I create one?

Does anyone know how to do this? Thanks to anyone who can help.

I send daily 80k emails with our newest daily post on marriageaftergod.com, and I wanted more control over what went out and how it looked than the built-in options gave me. So I created a small snippet that adds a Recent Posts group to FluentCRM's smart codes. Paste it into FluentSnippets (or your theme's functions.php) and it shows up in the smart code picker in the email editor.

What it adds

  • {{recent_posts.list}}: your 5 latest posts with thumbnail, title, excerpt and date. There's also list_3 and list_10.
  • {{recent_posts.latest_title}}, latest_excerpt, latest_image, latest_link, latest_link_html and latest_button: the parts of your newest post, so you can build your own layout.
  • {{recent_posts.latest_full}}: a ready-made card with image, title, date, author, excerpt and a "Read More" button.
  • {{recent_posts.latest_body}}: the full post content inside the email. This is the one I built it for. Our daily email is the post itself, not a teaser.

Filter by category. Add _category_yourcategory to any code, for example {{recent_posts.latest_body_category_prayers}}. Category IDs work too.

Style it your way. The HTML is table-based and uses inline styles, so it holds up in Gmail and Outlook. There are filters to change the query, the styles and the final HTML without touching the core code, for example fluentcrm_latest_post_body_styles and fluentcrm_recent_posts_html.

There's also a [fluentcrm_recent_posts] shortcode with count, category, show_image, show_excerpt and show_date options, if you prefer shortcodes.

Pair it with a daily recurring campaign or an automation and your newsletter builds itself.

Code below. Use it, tweak it, and let me know what you'd add.

/**
 * Recent Posts SmartCodes for FluentCRM
 *
 * Adds a "Recent Posts" smart code group so you can drop your latest
 * blog posts (or the full latest post) into any FluentCRM email.
 *
 * Smart codes:
 *   {{recent_posts.list}}             5 latest posts (thumbnail, title, excerpt, date)
 *   {{recent_posts.list_3}}           3 latest posts
 *   {{recent_posts.list_10}}          10 latest posts
 *   {{recent_posts.latest_title}}     Latest post title
 *   {{recent_posts.latest_excerpt}}   Latest post excerpt
 *   {{recent_posts.latest_link}}      Latest post URL (without https://)
 *   {{recent_posts.latest_link_html}} Latest post title as a link
 *   {{recent_posts.latest_button}}    "Read Latest Post" button
 *   {{recent_posts.latest_image}}     Latest post featured image
 *   {{recent_posts.latest_full}}      Latest post as a card
 *   {{recent_posts.latest_body}}      Full content of the latest post
 *
 * Filter by category: append _category_{slug-or-id} to any code.
 *   {{recent_posts.latest_body_category_daily-prayers}}
 *   {{recent_posts.list_3_category_12}}
 * Underscores in slugs become hyphens (_category_daily_prayers also works).
 *
 * Shortcode (also works inside FluentCRM emails):
 *   [upfluent_recent_posts count="5" category="news" show_image="yes" show_excerpt="yes" show_date="yes" post_type="post"]
 *
 * Install: paste into FluentSnippets (PHP snippet) or a code snippets plugin.
 */

defined( 'ABSPATH' ) || exit;

/* -------------------------------------------------------------------------
 * Register smart codes
 * ---------------------------------------------------------------------- */
add_action( 'fluent_crm/after_init', function () {
    if ( ! function_exists( 'FluentCrmApi' ) ) {
        return;
    }

    $codes = array(
        'list'             => 'Recent Posts List (5 posts)',
        'list_3'           => 'Recent Posts List (3 posts)',
        'list_10'          => 'Recent Posts List (10 posts)',
        'latest_title'     => 'Latest Post Title',
        'latest_excerpt'   => 'Latest Post Excerpt',
        'latest_link'      => 'Latest Post URL (raw)',
        'latest_link_html' => 'Latest Post Link (clickable)',
        'latest_button'    => 'Latest Post Button',
        'latest_image'     => 'Latest Post Featured Image',
        'latest_full'      => 'Latest Post (Full Card)',
        'latest_body'      => 'Latest Post Full Content',
    );

    FluentCrmApi( 'extender' )->addSmartCode( 'recent_posts', 'Recent Posts', $codes, function ( $code, $value_key, $default, $subscriber ) {
        $category = '';
        if ( preg_match( '/^(.+?)_category_(.+)$/', $value_key, $m ) ) {
            $value_key = $m[1];
            $category  = $m[2];
        }

        $list_counts = array( 'list' => 5, 'list_3' => 3, 'list_10' => 10 );
        if ( isset( $list_counts[ $value_key ] ) ) {
            return upfluent_list_html( $list_counts[ $value_key ], $category );
        }

        switch ( $value_key ) {
            case 'latest_title':
            case 'latest_excerpt':
            case 'latest_link':
            case 'latest_link_html':
            case 'latest_button':
            case 'latest_image':
                return upfluent_latest_field( substr( $value_key, strlen( 'latest_' ) ), $category );
            case 'latest_full':
                return upfluent_latest_card( $category );
            case 'latest_body':
                return upfluent_latest_body( $category );
        }

        return $default;
    } );
} );

/* -------------------------------------------------------------------------
 * Helpers
 * ---------------------------------------------------------------------- */

if ( ! function_exists( 'upfluent_get_posts' ) ) {
    /**
     * Fetch published posts. $context lets filters target one output
     * (list, latest, card, body).
     */
    function upfluent_get_posts( $count = 5, $category = '', $context = 'list', $post_type = 'post' ) {
        $args = array(
            'post_type'           => $post_type,
            'post_status'         => 'publish',
            'posts_per_page'      => max( 1, (int) $count ),
            'orderby'             => 'date',
            'order'               => 'DESC',
            'ignore_sticky_posts' => true,
            'no_found_rows'       => true,
        );

        $category = trim( (string) $category );
        if ( '' !== $category ) {
            if ( is_numeric( $category ) ) {
                $args['cat'] = (int) $category;
            } else {
                $args['category_name'] = sanitize_title( str_replace( '_', '-', $category ) );
            }
        }

        return get_posts( apply_filters( 'upfluent_query_args', $args, $context ) );
    }
}

if ( ! function_exists( 'upfluent_excerpt' ) ) {
    function upfluent_excerpt( $post, $words ) {
        $text = $post->post_excerpt ? $post->post_excerpt : $post->post_content;
        return esc_html( wp_trim_words( strip_shortcodes( $text ), $words, '…' ) );
    }
}

if ( ! function_exists( 'upfluent_empty' ) ) {
    function upfluent_empty() {
        return apply_filters( 'upfluent_empty_html', '<p style="color:#666666;font-style:italic;">No recent posts available.</p>' );
    }
}

if ( ! function_exists( 'upfluent_list_html' ) ) {
    /**
     * Email-safe list of posts (table layout, inline styles).
     */
    function upfluent_list_html( $count = 5, $category = '', $opts = array(), $post_type = 'post' ) {
        $opts  = wp_parse_args( $opts, array( 'image' => true, 'excerpt' => true, 'date' => true ) );
        $posts = upfluent_get_posts( $count, $category, 'list', $post_type );

        if ( empty( $posts ) ) {
            return upfluent_empty();
        }

        $html = '<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse:collapse;margin:20px 0;">';

        foreach ( $posts as $post ) {
            $url   = esc_url( get_permalink( $post ) );
            $title = get_the_title( $post );
            $thumb = $opts['image'] ? get_the_post_thumbnail_url( $post, 'thumbnail' ) : '';

            $html .= '<tr><td style="padding:15px 0;border-bottom:1px solid #e5e5e5;vertical-align:top;">';
            $html .= '<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%"><tr>';

            if ( $thumb ) {
                $html .= '<td width="80" style="padding-right:15px;vertical-align:top;">'
                    . '<a href="' . $url . '" style="text-decoration:none;">'
                    . '<img src="' . esc_url( $thumb ) . '" alt="' . esc_attr( $title ) . '" width="80" height="80" style="display:block;border-radius:4px;object-fit:cover;" />'
                    . '</a></td>';
            }

            $html .= '<td style="vertical-align:top;">';
            $html .= '<a href="' . $url . '" style="color:#333333;text-decoration:none;font-size:16px;font-weight:600;line-height:1.4;display:block;margin-bottom:5px;">' . esc_html( $title ) . '</a>';

            if ( $opts['excerpt'] ) {
                $html .= '<p style="color:#666666;font-size:14px;line-height:1.5;margin:0 0 8px 0;">' . upfluent_excerpt( $post, 20 ) . '</p>';
            }
            if ( $opts['date'] ) {
                $html .= '<span style="color:#999999;font-size:12px;">' . esc_html( get_the_date( 'M j, Y', $post ) ) . '</span>';
            }

            $html .= '</td></tr></table></td></tr>';
        }

        $html .= '</table>';

        return apply_filters( 'upfluent_list_html', $html, $posts, $count );
    }
}

if ( ! function_exists( 'upfluent_latest_field' ) ) {
    /**
     * A single field from the latest post.
     */
    function upfluent_latest_field( $field, $category = '' ) {
        $posts = upfluent_get_posts( 1, $category, 'latest' );
        if ( empty( $posts ) ) {
            return '';
        }

        $post  = $posts[0];
        $url   = get_permalink( $post );
        $title = get_the_title( $post );

        switch ( $field ) {
            case 'title':
                return esc_html( $title );

            case 'excerpt':
                return upfluent_excerpt( $post, 30 );

            case 'link':
                return preg_replace( '#^https?://#', '', $url );

            case 'link_html':
                return '<a href="' . esc_url( $url ) . '" style="color:#0073aa;text-decoration:underline;">' . esc_html( $title ) . '</a>';

            case 'button':
                $label = apply_filters( 'upfluent_button_label', 'Read Latest Post' );
                return '<a href="' . esc_url( $url ) . '" style="display:inline-block;background-color:#0073aa;color:#ffffff;text-decoration:none;padding:12px 24px;border-radius:4px;font-size:16px;font-weight:600;">' . esc_html( $label ) . '</a>';

            case 'image':
                $img = get_the_post_thumbnail_url( $post, 'medium' );
                return $img
                    ? '<img src="' . esc_url( $img ) . '" alt="' . esc_attr( $title ) . '" style="max-width:100%;height:auto;border-radius:4px;display:block;" />'
                    : '';
        }

        return '';
    }
}

if ( ! function_exists( 'upfluent_latest_card' ) ) {
    /**
     * Latest post as a card: image, title, date, author, excerpt, button.
     */
    function upfluent_latest_card( $category = '' ) {
        $posts = upfluent_get_posts( 1, $category, 'card' );
        if ( empty( $posts ) ) {
            return upfluent_empty();
        }

        $post   = $posts[0];
        $url    = esc_url( get_permalink( $post ) );
        $title  = get_the_title( $post );
        $author = get_the_author_meta( 'display_name', $post->post_author );
        $img    = get_the_post_thumbnail_url( $post, 'large' );

        $html = '<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse:collapse;margin:20px 0;background-color:#ffffff;border:1px solid #e5e5e5;border-radius:8px;overflow:hidden;">';

        if ( $img ) {
            $html .= '<tr><td style="padding:0;"><a href="' . $url . '" style="text-decoration:none;">'
                . '<img src="' . esc_url( $img ) . '" alt="' . esc_attr( $title ) . '" width="100%" style="display:block;max-width:100%;height:auto;" />'
                . '</a></td></tr>';
        }

        $meta = esc_html( get_the_date( 'F j, Y', $post ) );
        if ( $author ) {
            $meta .= ' &bull; By ' . esc_html( $author );
        }

        $html .= '<tr><td style="padding:20px;">';
        $html .= '<a href="' . $url . '" style="color:#333333;text-decoration:none;font-size:22px;font-weight:700;line-height:1.3;display:block;margin-bottom:10px;">' . esc_html( $title ) . '</a>';
        $html .= '<p style="color:#999999;font-size:13px;margin:0 0 12px 0;">' . $meta . '</p>';
        $html .= '<p style="color:#555555;font-size:15px;line-height:1.6;margin:0 0 15px 0;">' . upfluent_excerpt( $post, 40 ) . '</p>';
        $html .= '<a href="' . $url . '" style="display:inline-block;background-color:#0073aa;color:#ffffff;text-decoration:none;padding:10px 20px;border-radius:4px;font-size:14px;font-weight:600;">Read More &rarr;</a>';
        $html .= '</td></tr></table>';

        return apply_filters( 'upfluent_card_html', $html, $post );
    }
}

if ( ! function_exists( 'upfluent_latest_body' ) ) {
    /**
     * Full content of the latest post in a styled wrapper.
     */
    function upfluent_latest_body( $category = '' ) {
        $posts = upfluent_get_posts( 1, $category, 'body' );
        if ( empty( $posts ) ) {
            return '';
        }

        $post    = $posts[0];
        $content = apply_filters( 'the_content', $post->post_content );
        $styles  = apply_filters( 'upfluent_body_styles', 'font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:16px;line-height:1.6;color:#333333;' );

        return apply_filters( 'upfluent_body_html', '<div style="' . esc_attr( $styles ) . '">' . $content . '</div>', $post );
    }
}

/* -------------------------------------------------------------------------
 * Shortcode: [upfluent_recent_posts]
 * ---------------------------------------------------------------------- */
add_shortcode( 'upfluent_recent_posts', function ( $atts ) {
    $atts = shortcode_atts( array(
        'count'        => 5,
        'category'     => '',
        'show_image'   => 'yes',
        'show_excerpt' => 'yes',
        'show_date'    => 'yes',
        'post_type'    => 'post',
    ), $atts, 'upfluent_recent_posts' );

    return upfluent_list_html(
        (int) $atts['count'],
        $atts['category'],
        array(
            'image'   => 'yes' === $atts['show_image'],
            'excerpt' => 'yes' === $atts['show_excerpt'],
            'date'    => 'yes' === $atts['show_date'],
        ),
        sanitize_key( $atts['post_type'] )
    );
} );

// Run the shortcode inside FluentCRM emails.
add_filter( 'fluent_crm/parse_campaign_email_text', function ( $content ) {
    return has_shortcode( $content, 'upfluent_recent_posts' ) ? do_shortcode( $content ) : $content;
} );

/* -------------------------------------------------------------------------
 * Optional customizations (uncomment and edit)
 * ---------------------------------------------------------------------- */

// Always pull {{recent_posts.latest_body}} from one category (slug):
// add_filter( 'upfluent_query_args', function ( $args, $context ) {
//  if ( 'body' === $context ) {
//      $args['category_name'] = 'daily-prayers';
//  }
//  return $args;
// }, 10, 2 );

// Change the full-content email styling:
// add_filter( 'upfluent_body_styles', function () {
//  return 'font-family:Georgia,serif;font-size:18px;line-height:1.8;color:#222222;background:#ffffff;padding:30px;';
// } );

// Change the button text:
// add_filter( 'upfluent_button_label', function () {
//  return 'Read today\'s post';
// } );
Free snippet: put your latest blog posts into FluentCRM emails with customizable smart codes Free snippet: put your latest blog posts into FluentCRM emails with customizable smart codes