Emails & Sending Filters
FluentCRM Core IntermediateThese filter hooks control email headers, body processing, design templates, tracking, the sending pipeline, and compliance settings.
Email Headers
fluent_crm/email_headers
Filter the full array of outgoing email headers. Use this to add custom MIME headers.
Parameters
$headersArray - Email headers$dataArray - Email data$subscriberSubscriber Model$emailModelCampaignEmail Model
Usage:
add_filter('fluent_crm/email_headers', function($headers, $data, $subscriber, $emailModel) {
$headers[] = 'X-Custom-Header: my-value';
return $headers;
}, 10, 4);Source: app/Services/Libs/Mailer/Mailer.php
fluent_crm/enable_unsub_header
Control whether the List-Unsubscribe header is included in outgoing marketing emails.
WARNING
This filter only runs when the email has a resolved subscriber. Mail sent without one never evaluates the filter and never gets the header.
Parameters
$enabledBoolean - Defaulttrue$dataArray - Email data$subscriberSubscriber Model - always present when this filter runs$emailModelCampaignEmail Model - may benullfor non-campaign mail
Usage:
add_filter('fluent_crm/enable_unsub_header', function($enabled, $data, $subscriber, $emailModel) {
return false; // Disable List-Unsubscribe header
}, 10, 4);Source: app/Services/Libs/Mailer/Mailer.php
fluent_crm/enable_mailer_to_name
Control whether the subscriber's display name is included in the To: header for better deliverability.
Parameters
$enabledBoolean - Defaulttrue
Usage:
add_filter('fluent_crm/enable_mailer_to_name', function($enabled) {
return false; // Send to email only, no display name
});Source: app/Services/Libs/Mailer/Mailer.php
fluent_crm/email_data_before_headers
Filter the full email data array before headers are built. Use this to modify the email subject, body, or other fields before sending.
Parameters
$dataArray - Email data (subject, body, from, etc.)$subscriberSubscriber Model$emailModelCampaignEmail Model
Usage:
add_filter('fluent_crm/email_data_before_headers', function($data, $subscriber, $emailModel) {
// Modify email data before headers are built
$data['subject'] .= ' - ' . $subscriber->first_name;
return $data;
}, 10, 3);Source: app/Services/Libs/Mailer/Mailer.php
Email Body & Smart Codes
fluent_crm/parse_campaign_email_text
Parse smart codes in email text. This filter is called in many places — campaign body, subject, footer, pre-header, double optin emails, and more. It is the primary filter for resolving smart code tokens.
Parameters
$textString - Text containing smart code tokens$subscriberSubscriber Model
Usage:
add_filter('fluent_crm/parse_campaign_email_text', function($text, $subscriber) {
// Replace a custom token
$text = str_replace('{{custom.membership_level}}', get_user_meta($subscriber->user_id, 'level', true), $text);
return $text;
}, 10, 2);Source: Multiple files — app/Services/Libs/Mailer/Handler.php, app/Models/CampaignEmail.php, app/Models/Campaign.php, app/Http/Controllers/CampaignController.php, and more.
fluent_crm/parse_extended_crm_text
Parse extended CRM smart codes after the email body has been wrapped in a design template. This fires later in the pipeline than parse_campaign_email_text.
Parameters
$textString - Email body with template wrapper$subscriberSubscriber Model
Usage:
add_filter('fluent_crm/parse_extended_crm_text', function($text, $subscriber) {
$text = str_replace('{{crm.custom_footer}}', 'My custom footer', $text);
return $text;
}, 10, 2);Source: app/Services/Libs/Mailer/Handler.php, app/Models/CampaignEmail.php, app/Hooks/Handlers/ExternalPages.php
fluentcrm_email_body_text
Filter the final email body text right before click-tracking URLs are injected. This is the last chance to modify the email body before sending.
Parameters
$emailBodyString - Final HTML email body$subscriberSubscriber Model$campaignEmailCampaignEmail Model
Usage:
add_filter('fluentcrm_email_body_text', function($emailBody, $subscriber, $campaignEmail) {
// Append a custom tracking pixel
$emailBody .= '<img src="https://example.com/track/' . $subscriber->id . '" />';
return $emailBody;
}, 10, 3);Source: app/Models/CampaignEmail.php
fluent_crm/web_email_footer_text
Filter the footer text displayed on the "view in browser" email page.
Parameters
$footerTextString - Footer HTML$emailCampaignEmail Model
Usage:
add_filter('fluent_crm/web_email_footer_text', function($footerText, $email) {
return $footerText . '<p>Powered by Our Company</p>';
}, 10, 2);Source: app/Hooks/Handlers/ExternalPages.php
fluent_crm/smartcode_group_callback_{$group}
Dynamic filter that resolves the value of a custom smart code group. When the parser meets a token like {{my_group.some_key}} whose group is not one of the built-ins (contact, wp, crm, user, other), it hands the token to this filter with {$group} set to the group name.
TIP
fluent_crm/smartcode_groups only registers the codes so they show up in the editor's smart code picker — without a matching fluent_crm/smartcode_group_callback_{$group} callback the registered codes never render and the literal token stays in the sent email. See the SmartCode parser reference for the full parsing pipeline.
Parameters
$codeString - the full unresolved token including braces, e.g.{{my_group.some_key|Fallback}}— returning it unchanged leaves the literal token in the email$valueKeyString - the part after the first dot, with any|modifiers stripped, e.g.some_key(nested dots are preserved:meta.city)$defaultValueString - the fallback text from the{{my_group.some_key|Fallback}}syntax,''when none is given$subscriberSubscriber Model - always set; the parser returns the default before this filter when no subscriber is available
TIP
Value transformers (|trim, |ucfirst, |strtolower, |strtoupper, |ucwords, |concat_first, |concat_last, |show_if) are applied by the parser after your callback returns a non-empty value, so return the raw value and let the token's modifiers do the formatting.
Usage:
add_filter('fluent_crm/smartcode_group_callback_my_group', function($code, $valueKey, $defaultValue, $subscriber) {
if ($valueKey === 'membership_level') {
return get_user_meta($subscriber->user_id, 'level', true) ?: $defaultValue;
}
return $defaultValue; // Unknown key for this group
}, 10, 4);Source: app/Services/Libs/Parser/ShortcodeParser.php
fluentcrm_smartcode_fallback
Last-chance resolver for smart code tokens the parser cannot attribute to any group: tokens with empty inner content, without a group.key dot structure (e.g. {{first_name}}), or with an empty key part. The unfiltered value is the raw token itself, so by default such tokens stay as-is in the sent email.
TIP
This filter does not fire for well-formed {{group.key}} tokens with an unknown group — those go to fluent_crm/smartcode_group_callback_{$group} instead. It runs in both parsing passes (the main pass and the later extended-CRM pass), so an unresolved token can hit your callback more than once.
Parameters
$codeString - the full unresolved token including braces, e.g.{{first_name}}$subscriberSubscriber Model - the contact the text is being parsed for
Usage:
add_filter('fluentcrm_smartcode_fallback', function($code, $subscriber) {
if ($code === '{{first_name}}') { // support a legacy un-grouped token
return $subscriber->first_name;
}
return $code;
}, 10, 2);Source: app/Services/Libs/Parser/ShortcodeParser.php
fluent_crm/allow_privileged_reset_link_smartcode
Security gate for the {{user.password_reset_direct_link}} smart code. FluentCRM refuses to mint a live password-reset link for a privileged WordPress account — any user with the manage_options capability (administrators) — because the rendered link can surface in records readable by lower-privileged CRM staff (the parsed subject stored on the sent-email row, SMTP logs). Return true to allow it for accounts you trust.
Silent by design
When the gate blocks a privileged account, the smart code silently renders its default value (usually an empty string) — no error is raised anywhere. If your site intentionally onboards administrators with a "set your password" email, this is why their reset link is blank; add this filter to allow it. Note the smart code also requires the contact's email to exactly match the linked WP user's email — that check runs first and is not filterable.
Parameters
$allowedBoolean - Defaultfalse$wpUserWP_User - the privileged WordPress user the link would be minted for$subscriberSubscriber Model - the contact being emailed
Usage:
add_filter('fluent_crm/allow_privileged_reset_link_smartcode', function($allowed, $wpUser, $subscriber) {
// Allow reset links for admins created by our own onboarding flow
return $subscriber->source === 'internal_onboarding';
}, 10, 3);Source: app/Services/Libs/Parser/ShortcodeParser.php
fluent_crm/rss_block_is_safe_feed_url
Override the SSRF guard that validates RSS feed URLs before the Gutenberg email RSS block fetches them server-side. The default guard only accepts http/https URLs whose host resolves to a public IP — feeds on private ranges (10.x, 192.168.x, 127.0.0.1) or reserved ranges are rejected, so the block renders nothing for intranet feeds. Return true to allow such a feed.
Parameters
$isSafeBoolean - whether the resolved IP passed the public-range check$urlString - the feed URL from the block attributes$hostString - the parsed URL host$ipString - the IP the host resolved to
Security trade-off
The feed URL is fetched by your server, so allowing private ranges re-opens the SSRF vector this guard exists for — an editor-supplied feed URL could then probe internal services. Allow specific hosts, never return a blanket true. Also note the filter only runs after the URL has parsed with an http/https scheme and the host has resolved to an IP; other schemes and unresolvable hosts are rejected before the filter fires and cannot be allowed here.
Usage:
add_filter('fluent_crm/rss_block_is_safe_feed_url', function($isSafe, $url, $host, $ip) {
if ($host === 'news.intranet.local') {
return true; // Trusted internal feed
}
return $isSafe;
}, 10, 4);Source: app/Services/GutenbergEmailParser.php
Design Templates
fluent_crm/email_design_templates
Filter the list of available email design templates (Simple Boxed, Plain Centered, Plain Left, Classic Editor, Raw HTML).
Parameters
$templatesArray - an array keyed by template slug. Each entry accepts:idString - must equal the array keylabelString - the name shown in the template pickerimageString - preview image URLconfigArray - default design configuration for the templateuse_gutenbergBoolean - whether the block editor is used to author this templatetemplate_typeString - optional;classic_editororraw_text_boxfor the non-Gutenberg editorstemplate_infoString - optional HTML shown alongside the editor
TIP
Register a matching fluent_crm/email-design-template-{$template} filter for your slug, otherwise the body is rendered without a wrapper.
Usage:
add_filter('fluent_crm/email_design_templates', function($templates) {
$templates['my_template'] = [
'id' => 'my_template',
'label' => 'My Custom Template',
'image' => 'https://example.com/template-preview.png',
'config' => [],
'use_gutenberg' => true
];
return $templates;
});Source: app/Services/Helper.php
fluent_crm/default_email_design_template
Filter the default email design template slug used when creating new emails.
Parameters
$slugString - Default'simple'
Usage:
add_filter('fluent_crm/default_email_design_template', function($slug) {
return 'classic';
});Source: app/Services/Helper.php
fluent_crm/email-design-template-{$template}
Dynamic filter to render the email body through a specific design template. The {$template} part is the template slug (e.g., simple, classic, visual_builder).
Parameters
$emailBodyString - Raw email body HTML$templateDataArray - Template data:preHeader,email_body,footer_text,footer_config,config$campaignCampaign Model - may benulloutside a campaign context$subscriberSubscriber Model - may benullwhen rendering a preview
WARNING
The third argument is the campaign, not the subscriber. FluentCRM's own built-in template callbacks are registered with accepted_args of 3, so they never receive the subscriber — request all four only if you need it.
Usage:
add_filter('fluent_crm/email-design-template-my_template', function($emailBody, $templateData, $campaign, $subscriber) {
// Wrap the email body in your custom template
return '<html><body>' . $emailBody . '</body></html>';
}, 10, 4);Source: app/Services/Libs/Mailer/Handler.php, app/Models/CampaignEmail.php, app/Hooks/Handlers/ExternalPages.php
fluent_crm/email_view_on_browser_data
Filter the data array passed to the "view on browser" page template.
Parameters
$dataArray - Template data (subject, body, campaign info, etc.)$emailCampaignEmail Model
Usage:
add_filter('fluent_crm/email_view_on_browser_data', function($data, $email) {
$data['custom_branding'] = true;
return $data;
}, 10, 2);Source: app/Hooks/Handlers/ExternalPages.php
fluent_crm/email_newsletter_data
Filter the data array passed to the public shared-newsletter page — the page served by a campaign's share URL (_campaign_share_id hash), as opposed to the per-subscriber "view on browser" link, which uses fluent_crm/email_view_on_browser_data. The email body has already been rendered through the design template when this filter runs.
Parameters
$dataArray - Page data:businessArray - the business settings (name, logo, address)email_headingString - the campaign subject used as the page headingemail- alwaysnullon this pageemail_bodyArray -['rendered' => $html], the fully rendered email HTMLcssAssetsArray - stylesheet URLs loaded by the page
Usage:
add_filter('fluent_crm/email_newsletter_data', function($data) {
$data['email_heading'] = 'Our Weekly Newsletter';
return $data;
});Source: app/Hooks/Handlers/ExternalPages.php
fluent_crm/editing_template_data
Filter the email template payload sent to the editor when an existing template is opened for editing. This does not run for the blank payload used when creating a new template.
Parameters
$templateDataArray - Template payload:post_title,post_content,post_excerptString - the stored template post fieldsemail_subjectString - the_email_subjectmetaedit_typeString - editor type,htmlwhen unsetdesign_templateString - the design template slugsettingsArray - normalizedtemplate_configandfooter_settings
$templateTemplate Model
Usage:
add_filter('fluent_crm/editing_template_data', function($templateData, $template) {
$templateData['email_subject'] = strtoupper($templateData['email_subject']);
return $templateData;
}, 10, 2);Source: app/Http/Controllers/TemplateController.php
fluent_crm/editing_recurring_campaign_data
ProFilter the recurring campaign payload written to the JSON file when a recurring campaign is exported for reuse on another site. The Pro sibling of fluent_crm/editing_template_data: whatever you add or strip here is what the import on the destination site receives.
Parameters
$campaignDataArray - Export payload:title,settings,template_id,email_subject,email_pre_header,email_body,utm_status,utm_source,utm_medium,utm_campaign,utm_term,utm_content,design_template, andstatus(always forced todraft)$campaignObject - theRecurringCampaignmodel being exported
Usage:
add_filter('fluent_crm/editing_recurring_campaign_data', function($campaignData, $campaign) {
$campaignData['template_id'] = 0; // Template IDs won't match on the destination site
return $campaignData;
}, 10, 2);Source: fluentcampaign-pro/app/Hooks/Handlers/DataExporter.php
Tracking
fluent_crm/is_simulated_mail
Simulate all email sending without actually dispatching. Useful for testing or staging environments.
Attention: If you return
true, no email will be sent from FluentCRM.
Parameters
$simulatedBoolean - Defaultfalse$dataArray - Email data$headersArray - Email headers
Usage:
add_filter('fluent_crm/is_simulated_mail', function($simulated, $data, $headers) {
return true; // Simulate all emails
}, 10, 3);Source: app/Services/Libs/Mailer/Mailer.php
fluentcrm_disable_email_open_tracking
Disable the email open tracking pixel globally.
Parameters
$disabledBoolean - Defaultfalse
Usage:
add_filter('fluentcrm_disable_email_open_tracking', function($disabled) {
return true; // Disable open tracking
});Source: app/Services/Helper.php, app/Functions/helpers.php
fluent_crm/track_click
Control whether click tracking (URL rewriting) is enabled globally.
Parameters
$enabledBoolean - Defaulttrue
Usage:
add_filter('fluent_crm/track_click', function($enabled) {
return false; // Disable click tracking
});Source: app/Services/Helper.php, app/Functions/helpers.php
fluent_crm/will_use_cookie
Control whether FluentCRM sets a tracking cookie when a contact clicks a link or confirms opt-in. This cookie enables revenue attribution and campaign tracking.
Attention: Disabling this will prevent revenue tracking.
Parameters
$enabledBoolean - Defaulttrue
Usage:
add_filter('fluent_crm/will_use_cookie', function($enabled) {
return false; // Disable tracking cookie
});Source: app/Hooks/Handlers/ExternalPages.php, app/Hooks/Handlers/RedirectionHandler.php
Sending Pipeline
fluent_crm/disable_email_processing
Halt all email sending immediately. Return true to stop the mailer from processing any emails.
Parameters
$disabledBoolean - Defaultfalse
Usage:
add_filter('fluent_crm/disable_email_processing', function($disabled) {
return true; // Stop all email processing
});Source: app/Services/Libs/Mailer/Handler.php, app/Services/Libs/Mailer/MultiThreadHandler.php, app/Services/Libs/Mailer/CliSendingHandler.php
fluent_crm/global_email_limit_per_second
Filter the global emails-per-second cap enforced by GlobalRateLimiter.
The unfiltered value comes from the Emails per second setting minus a safety buffer of 3, or 14 when that setting is empty; either way it is floored at 4 before the filter runs.
WARNING
The resolved limit is cached in a static for the rest of the request, so this filter runs at most once per PHP process. Do not return a value that varies per email — use fluent_crm/enable_global_rate_limit for per-email decisions instead.
Parameters
$limitINT - the resolved limit, already floored at4$emailSettingsArray - theemail_settingsglobal settings array
Usage:
add_filter('fluent_crm/global_email_limit_per_second', function($limit, $emailSettings) {
return 5; // Limit to 5 emails per second
}, 10, 2);Source: app/Services/Libs/Mailer/GlobalRateLimiter.php
fluent_crm/enable_global_rate_limit
Return false to skip the global per-second throttle for a given outgoing email. Unlike fluent_crm/global_email_limit_per_second, this runs per email, so it is the right place for per-email exemptions.
Parameters
$enabledBoolean - Defaulttrue$dataArray - the email payload; inspect keys such asscopeto exempt specific traffic
Usage:
add_filter('fluent_crm/enable_global_rate_limit', function($enabled, $data) {
if (($data['scope'] ?? '') === 'transactional') {
return false; // never throttle transactional mail
}
return $enabled;
}, 10, 2);Source: app/Services/Libs/Mailer/GlobalRateLimiter.php
fluent_crm/mailer_handler_chunk_size
Filter the number of emails pulled per batch in the standard single-thread mailer handler.
Parameters
$chunkSizeINT - Default20
TIP
Values of 0 or less are ignored — the handler keeps its default.
Usage:
add_filter('fluent_crm/mailer_handler_chunk_size', function($chunkSize) {
return 50;
});Source: app/Services/Libs/Mailer/Handler.php
fluent_crm/mailer_handler_max_processing_seconds
Filter the max processing time (seconds) for the single-thread mailer handler loop.
Parameters
$secondsINT - Default50
TIP
Values of 0 or less are ignored — the handler keeps its default.
Usage:
add_filter('fluent_crm/mailer_handler_max_processing_seconds', function($seconds) {
return 30;
});Source: app/Services/Libs/Mailer/Handler.php
fluent_crm/mailer_multi_thread_chunk_size
Filter the number of emails per batch in the multi-thread mailer handler.
Parameters
$chunkSizeINT - Default20
TIP
Values of 0 or less are ignored — the handler keeps its default.
Usage:
add_filter('fluent_crm/mailer_multi_thread_chunk_size', function($chunkSize) {
return 50;
});Source: app/Services/Libs/Mailer/MultiThreadHandler.php
fluent_crm/mailer_multi_thread_max_processing_seconds
Filter the max processing time (seconds) for the multi-thread mailer handler loop.
Parameters
$secondsINT - Default50
TIP
Values of 0 or less are ignored — the handler keeps its default.
Usage:
add_filter('fluent_crm/mailer_multi_thread_max_processing_seconds', function($seconds) {
return 30;
});Source: app/Services/Libs/Mailer/MultiThreadHandler.php
fluent_crm/process_subscribers_per_request
Filter the number of subscribers processed per batch request.
One hook, three different batch sizes
This filter name is reused by three unrelated batch loops, each passing a different default. A callback that returns a fixed number changes all three at once.
| Where | Default | What it sizes |
|---|---|---|
app/Services/CampaignProcessor.php | 30 | Subscribers enqueued per request while preparing a campaign for sending |
app/Http/Controllers/UsersController.php | 100 | WP users imported per request by the user-import screen |
fluentcampaign-pro/app/Http/Controllers/SequenceController.php | 200 | Contacts added to an email sequence per request |
Branch on the incoming default if you only mean to change one of them.
Parameters
$countINT - Default30,100, or200depending on the caller (see table)
Usage:
add_filter('fluent_crm/process_subscribers_per_request', function($count) {
// Only change the campaign-processing batch, leave imports and sequences alone
if ($count === 30) {
return 50;
}
return $count;
});Source: app/Services/CampaignProcessor.php, app/Http/Controllers/UsersController.php, fluentcampaign-pro/app/Http/Controllers/SequenceController.php
Compliance & Formatting
fluent_crm/disable_check_compliance_string
Skip the compliance string validation (unsubscribe link requirement) for outgoing emails. Return true to bypass the check.
Parameters
$disabledBoolean - Defaultfalse$textString - Email body text being checked
Usage:
add_filter('fluent_crm/disable_check_compliance_string', function($disabled, $text) {
return true; // Skip compliance check
}, 10, 2);Source: app/Services/Helper.php
fluent_crm/disable_emoji_to_image
Control whether WordPress's emoji-to-image filter is removed before sending emails. Default true (emojis stay as unicode text, not converted to images).
Parameters
$disabledBoolean - Defaulttrue
Usage:
add_filter('fluent_crm/disable_emoji_to_image', function($disabled) {
return false; // Allow WordPress to convert emojis to images
});Source: app/Services/Helper.php
fluent_crm/is_rtl
Control whether email templates should be rendered in RTL (right-to-left) direction.
Parameters
$isRtlBoolean - Default: WordPressis_rtl()value
Usage:
add_filter('fluent_crm/is_rtl', function($isRtl) {
return true; // Force RTL for all emails
});Source: app/Functions/helpers.php
AI Generation
These filters customize FluentCRM's AI features: email body generation in the campaign editor and contact summaries on the contact profile.
fluent_crm/wordpress_ai_generate
Route AI generation through your own provider. This filter fires for every AI call — email body generation, the rewrite/shorten/expand text actions, and contact summaries — whenever the AI provider in Settings is set to WordPress AI. Return a non-null value to short-circuit the built-in WordPress AI Client call entirely.
TIP
This is the single most useful AI hook: set the provider to wordpress in the AI settings and add this filter, and no other provider or API key configuration is needed — all generation goes through your callback. Returning null (the default) falls through to wp_ai_client_prompt(), which requires WordPress 7.0+.
Parameters
$response- Defaultnull. Return the generated text as a string to short-circuit, or aWP_Errorto report failure. For email body generation the string must be the JSON the system prompt asks for ({"subject_suggestions":[...],"preview_text":"...","email_body":"..."}); for text actions and contact summaries, plain text/markdown$userPromptString - the fully built user prompt$systemPromptString - the system prompt (may be an empty string)$modelString - alwayswordpressfor this provider$timeoutINT - request timeout in seconds (45for email body generation and contact summaries,30for the rewrite/shorten/expand text actions,15for the connection test)
Usage:
add_filter('fluent_crm/wordpress_ai_generate', function($response, $userPrompt, $systemPrompt, $model, $timeout) {
$result = my_llm_client()->complete($systemPrompt, $userPrompt, ['timeout' => $timeout]);
if (!$result) {
return new \WP_Error('generation_failed', 'My LLM provider did not return content');
}
return $result; // Raw text response
}, 10, 5);Source: app/Http/Controllers/AiController.php
fluent_crm/ai_email_body_system_prompt
Filter the system prompt sent to the configured AI provider for email body generation. The default prompt sets the copywriting persona, allows FluentCRM smartcodes, restricts HTML tags, and — most importantly — instructs the model to return only JSON in the shape {"subject_suggestions":[...],"preview_text":"...","email_body":"..."}. Any custom prompt saved in the AI settings is already appended when this filter runs.
WARNING
If you replace the prompt wholesale, keep the JSON output instruction. When the response is not parseable JSON, the whole raw response is used as the email body and no subject suggestions or preview text are produced.
Parameters
$promptString - the composed system prompt$settingsArray - the saved AI settings (includingcustom_prompt)
Usage:
add_filter('fluent_crm/ai_email_body_system_prompt', function($prompt, $settings) {
return $prompt . "\n\nAlways write in British English.";
}, 10, 2);Source: app/Http/Controllers/AiController.php
fluent_crm/ai_email_body_user_prompt
Filter the user prompt sent to the AI provider for email body generation, after it has been built from the brief the user typed in the editor.
Parameters
$promptString - the composed user prompt (goal, tone, length, audience, CTA, editor context, output rules)$promptDataArray - the sanitized inputs:prompt,tone,audience,length,cta, andcontext(design_template,editor_type,output_format,campaign_type,has_existing_body)
Usage:
add_filter('fluent_crm/ai_email_body_user_prompt', function($prompt, $promptData) {
return $prompt . "\nBrand voice: friendly but concise.";
}, 10, 2);Source: app/Http/Controllers/AiController.php
fluent_crm/ai_email_body_generated_content
Filter the parsed AI generation result before it is returned to the email editor. Runs after the provider response has been decoded, so you can post-process or replace the generated copy.
Parameters
$generatedArray -subject_suggestions(array, capped at 5),preview_text(string),email_body(string)$promptDataArray - the sanitized prompt inputs and editor context$resultString - the raw provider response
TIP
Return an array with the same keys — returning a non-array resets everything to empty values. The final response is still sanitized after this filter: email_body goes through HTML/blocks sanitization and the subject suggestions through sanitize_text_field().
Usage:
add_filter('fluent_crm/ai_email_body_generated_content', function($generated, $promptData, $result) {
$generated['preview_text'] = wp_trim_words($generated['preview_text'], 12);
return $generated;
}, 10, 3);Source: app/Http/Controllers/AiController.php
fluent_crm/ai_contact_summary_system_prompt
Filter the system prompt used when generating the AI contact summary on the contact profile. The default prompt scopes the model to the supplied contact data, forbids invented facts, and requests markdown in the site language. Any custom prompt saved in the AI settings is already appended.
Parameters
$promptString - the composed system prompt$settingsArray - the saved AI settings (includingcustom_prompt)$localeString - the WordPress site locale the summary should be written in
Usage:
add_filter('fluent_crm/ai_contact_summary_system_prompt', function($prompt, $settings, $locale) {
return $prompt . "\n\nOur team cares most about churn risk; lead with it.";
}, 10, 3);Source: app/Http/Controllers/AiController.php
fluent_crm/ai_contact_summary_user_prompt
Filter the user prompt for the AI contact summary. The structured contact context (profile, lists, tags, email engagement, purchase and support history when available) is JSON-encoded into the prompt; inspect $context to see exactly what the model receives.
Parameters
$promptString - the composed user prompt including the JSON-encoded contact context$contextArray - the structured contact context used to build the prompt$localeString - the WordPress site locale the summary should be written in
Usage:
add_filter('fluent_crm/ai_contact_summary_user_prompt', function($prompt, $context, $locale) {
return $prompt . "\nHighlight any open support tickets first.";
}, 10, 3);Source: app/Http/Controllers/AiController.php