fluentcrm-automation-sequence-models
Work with FluentCRM 3.x automation subscriber state and FluentCampaign Pro email sequences. Covers FunnelSubscriber, FunnelSequence, FunnelProcessor, FunnelHelper, FunnelMetric, Pro Sequence, SequenceMail, and SequenceTracker. Use when enrolling a contact into an automation funne
Install
npx skills add https://github.com/Lonsdale201/wp-agent-skills/tree/main/fluentcrm/fluentcrm-automation-sequence-models
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install lonsdale201-wp-agent-skills@llmmart
git clone https://github.com/Lonsdale201/wp-agent-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole lonsdale201/wp-agent-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
FluentCRM: automation and email sequence models
Use this skill when code needs to start or inspect FluentCRM automations, or enroll contacts into FluentCampaign Pro email sequences. Keep these two systems separate: FunnelSequence is an automation step; FluentCampaign\App\Models\Sequence is a Pro email sequence stored in fc_campaigns.
Verification note: local source was FluentCRM core 3.1.13 and FluentCampaign Pro 3.1.13. Core 3.1.13 declares FLUENTCRM_MIN_PRO_VERSION as 3.1.13.
When to use this skill
- Starting an automation funnel for a known contact or event.
- Resuming a funnel from a benchmark sequence point.
- Reading
fc_funnel_subscribersprogress, statuses, next sequence, or source metadata. - Enrolling or removing contacts from a Pro email sequence.
- Reviewing code that writes directly to
fc_funnel_subscribers,fc_funnel_sequences, orfc_sequence_tracker.
Automation data model
FluentCrm\App\Models\FunnelSubscriber maps fc_funnel_subscribers and tracks one contact inside one automation funnel.
Core fillable fields:
[
'funnel_id',
'subscriber_id',
'status',
'type',
'next_sequence',
'next_sequence_id',
'last_sequence_id',
'last_sequence_status',
'last_executed_time',
'next_execution_time',
'starting_sequence_id',
'source_trigger_name',
'source_ref_id',
'notes',
]
Common main statuses are draft, pending, active, waiting, completed, cancelled, and skipped. active() only scopes to status = active. Do not confuse the main status with last_sequence_status; FunnelHelper::changeFunnelSubSequenceStatus() writes last_sequence_status = complete when a normal step is processed. The benchmark direct-entry path can seed last_sequence_status = completed while creating a synthetic starting row; don't treat that as a value to write from action handlers.
Relations:
funnel()->FluentCrm\App\Models\Funnelsubscriber()->FluentCrm\App\Models\Subscribernext_sequence_item()->FunnelSequencelast_sequence()->FunnelSequencemetrics()->FunnelMetricrows for the same contact
FluentCrm\App\Models\FunnelSequence maps fc_funnel_sequences and represents a step in the automation builder. Important fields are funnel_id, parent_id, action_name, condition_type, type, status, conditions, settings, delay, c_delay, and sequence. The model serializes and unserializes settings and conditions.
Start a funnel safely
Do not directly insert FunnelSubscriber rows from a companion plugin. Use FunnelProcessor::startFunnelSequence(), which creates or finds the contact, handles pending/double-opt-in state, checks duplicates, creates the funnel subscriber, records the start hook, and processes immediate steps.
use FluentCrm\App\Models\Funnel;
use FluentCrm\App\Services\Funnel\FunnelProcessor;
if (!function_exists('FluentCrmApi')) {
return;
}
$contact = FluentCrmApi('contacts')->getContactByUserRef($userId);
$funnel = Funnel::where('id', (int) $funnelId)
->where('status', 'published')
->where('type', 'funnels')
->first();
if ($contact && $funnel) {
(new FunnelProcessor())->startFunnelSequence($funnel, [], [
'source_trigger_name' => 'my_plugin_event',
'source_ref_id' => (int) $eventId,
], $contact);
}
When you do not yet have a contact, pass subscriber data as the second argument:
(new FunnelProcessor())->startFunnelSequence($funnel, [
'email' => sanitize_email($email),
'first_name' => sanitize_text_field($firstName),
'status' => 'subscribed',
], [
'source_trigger_name' => 'my_plugin_event',
'source_ref_id' => (int) $eventId,
]);
Core manual attach uses the same pattern in SubscriberController: it filters contacts not already in the funnel and starts them with source_trigger_name => fcrm_manual_attach.
Duplicate and status guards
FunnelProcessor::startSequences() calls FunnelHelper::ifAlreadyInFunnel($funnelId, $subscriberId) and also relies on a unique DB constraint on (funnel_id, subscriber_id). Keep this path intact.
Processing later steps is status-gated. processFunnelAction() only processes contacts with status subscribed or transactional unless the funnel setting __force_run_actions is yes. Otherwise the funnel subscriber is marked cancelled.
The follow-up processor selects published funnels, due next_execution_time, and statuses from:
apply_filters('fluent_crm/funnel_subscriber_statuses', ['active']);
In 3.1.13 it also has batch controls:
fluent_crm/funnel_processor_batch_limit, default200fluent_crm/funnel_processor_max_processing_seconds, default55
Resume from a benchmark
For benchmarks or goal-style entry points, use startFunnelFromSequencePoint($startSequence, $subscriber, $args, $metricArgs). It records a FunnelMetric, starts from the benchmark point if allowed, or advances an existing funnel subscriber when the target point is ahead of the current progress.
Do not create a new funnel subscriber manually for benchmark resumes. The processor handles:
can_enter = no- already completed or cancelled funnels
starting_sequence_idlast_sequence_idnext_sequence_id- pending contacts
recordFunnelMetric() uses FunnelMetric::firstOrCreate() and catches a race where another process inserted the metric between select and insert.
Read automation progress
Use ORM reads for reports and conditional logic:
use FluentCrm\App\Models\FunnelSubscriber;
$runs = FunnelSubscriber::where('subscriber_id', (int) $contactId)
->with(['funnel', 'next_sequence_item', 'last_sequence'])
->orderBy('id', 'DESC')
->get();
Prefer read-only access unless you are implementing FluentCRM internals. If you must update a run, update only state fields you own and do not skip the processor's hook path for executing actions.
Pro email sequences
FluentCampaign\App\Models\Sequence is a Pro email sequence. It maps fc_campaigns with a global scope type = email_sequence. Its child emails are SequenceMail rows, and each enrolled contact is tracked by SequenceTracker in fc_sequence_tracker.
Enroll contacts through Sequence::subscribe():
use FluentCampaign\App\Models\Sequence;
use FluentCampaign\App\Models\SequenceTracker;
use FluentCrm\App\Models\Subscriber;
$sequence = Sequence::find((int) $sequenceId);
$contact = Subscriber::find((int) $contactId);
if ($sequence && $contact) {
$already = SequenceTracker::where('campaign_id', $sequence->id)
->where('subscriber_id', $contact->id)
->first();
if (!$already) {
$sequence->subscribe([$contact]);
}
}
Sequence::subscribe() loads SequenceMail rows ordered by delay, schedules first batch emails in fc_campaign_emails, parses subject/body smart codes with fluent_crm/parse_campaign_email_text, and creates or updates SequenceTracker.
Unsubscribe through the sequence model:
$sequence->unsubscribe([(int) $contactId], 'Removed by My Plugin');
This marks the tracker cancelled and cancels scheduled sequence emails. Do not delete SequenceTracker directly unless you intentionally want the admin-controller behavior, which removes tracker rows without cancelling scheduled emails.
SequenceTracker has a global scope type = sequence_tracker; status defaults to active. ofNextTrackers() selects active due trackers by next_execution_time <= current_time('mysql').
Common mistakes
- Do not confuse
FunnelSequencewithFluentCampaign\App\Models\Sequence. - Do not insert into
fc_funnel_subscribersfor normal integrations; callFunnelProcessor. - Do not write
completedthroughchangeFunnelSubSequenceStatus()for normal action progress; that helper's default/canon iscomplete. Reservecompletedfor full automation run status and Pro email sequence tracker status. - Do not enroll a contact into an email sequence twice. Check
SequenceTrackerfirst. - Do not process unsubscribed contacts unless the funnel explicitly uses
__force_run_actions.
Cross-references
- Use
fluentcrm-funnel-triggerwhen registering a new trigger in the automation builder. - Use
fluentcrm-funnel-actionfor custom action step classes. - Use
fluentcrm-funnel-benchmarkfor custom benchmark step classes. - Use
fluentcrm-contact-modelsfor contact/list/tag CRUD before starting automations.
References
- Local source:
FunnelProcessor.php,FunnelHelper.php,FunnelSubscriber.php,FunnelSequence.php, ProSequence.php, andSequenceTracker.php. - FluentCRM docs: FunnelSubscriber model, FunnelSequence model, and Fluent ORM.
- Official documentation: https://developers.fluentcrm.com/database/models/funnelSubscriber
- Official documentation: https://developers.fluentcrm.com/database/models/funnelSequence
- Official documentation: https://developers.fluentcrm.com/database/orm/
- Verified source paths:
fluent-crm/app/Models/Funnel.phpfluent-crm/app/Models/FunnelMetric.phpfluent-crm/app/Http/Controllers/SubscriberController.phpfluentcampaign-pro/app/Models/SequenceMail.phpfluentcampaign-pro/app/Http/Controllers/SequenceController.php
Files (wp-agent-skills)
-
SKILL.md 10.1 KB
--- name: fluentcrm-automation-sequence-models description: Work with FluentCRM 3.x automation subscriber state and FluentCampaign Pro email sequences. Covers FunnelSubscriber, FunnelSequence, FunnelProcessor, FunnelHelper, FunnelMetric, Pro Sequence, SequenceMail, and SequenceTracker. Use when enrolling a contact into an automation funnel, resuming from a benchmark, reading funnel progress, subscribing or unsubscribing contacts from Pro email sequences, or avoiding confusion between automation steps and email sequences. Triggers on FunnelSubscriber, FunnelSequence, startFunnelSequence, startFunnelFromSequencePoint, Sequence::subscribe, SequenceTracker, fc_funnel_subscribers, fc_sequence_tracker. metadata: wp-skills-author: "Soczó Kristóf" wp-skills-contact: "mailto:lonsdale201@hotmail.com" wp-skills-plugin: "fluent-crm" wp-skills-plugin-version-tested: "3.1.13" wp-skills-wp-version-tested: "7.1" wp-skills-php-min: "7.4" wp-skills-last-updated: "2026-08-25" --- # FluentCRM: automation and email sequence models Use this skill when code needs to start or inspect FluentCRM automations, or enroll contacts into FluentCampaign Pro email sequences. Keep these two systems separate: `FunnelSequence` is an automation step; `FluentCampaign\App\Models\Sequence` is a Pro email sequence stored in `fc_campaigns`. Verification note: local source was FluentCRM core 3.1.13 and FluentCampaign Pro 3.1.13. Core 3.1.13 declares `FLUENTCRM_MIN_PRO_VERSION` as 3.1.13. ## When to use this skill - Starting an automation funnel for a known contact or event. - Resuming a funnel from a benchmark sequence point. - Reading `fc_funnel_subscribers` progress, statuses, next sequence, or source metadata. - Enrolling or removing contacts from a Pro email sequence. - Reviewing code that writes directly to `fc_funnel_subscribers`, `fc_funnel_sequences`, or `fc_sequence_tracker`. ## Automation data model `FluentCrm\App\Models\FunnelSubscriber` maps `fc_funnel_subscribers` and tracks one contact inside one automation funnel. Core fillable fields: ```php [ 'funnel_id', 'subscriber_id', 'status', 'type', 'next_sequence', 'next_sequence_id', 'last_sequence_id', 'last_sequence_status', 'last_executed_time', 'next_execution_time', 'starting_sequence_id', 'source_trigger_name', 'source_ref_id', 'notes', ] ``` Common main statuses are `draft`, `pending`, `active`, `waiting`, `completed`, `cancelled`, and `skipped`. `active()` only scopes to `status = active`. Do not confuse the main `status` with `last_sequence_status`; `FunnelHelper::changeFunnelSubSequenceStatus()` writes `last_sequence_status = complete` when a normal step is processed. The benchmark direct-entry path can seed `last_sequence_status = completed` while creating a synthetic starting row; don't treat that as a value to write from action handlers. Relations: - `funnel()` -> `FluentCrm\App\Models\Funnel` - `subscriber()` -> `FluentCrm\App\Models\Subscriber` - `next_sequence_item()` -> `FunnelSequence` - `last_sequence()` -> `FunnelSequence` - `metrics()` -> `FunnelMetric` rows for the same contact `FluentCrm\App\Models\FunnelSequence` maps `fc_funnel_sequences` and represents a step in the automation builder. Important fields are `funnel_id`, `parent_id`, `action_name`, `condition_type`, `type`, `status`, `conditions`, `settings`, `delay`, `c_delay`, and `sequence`. The model serializes and unserializes `settings` and `conditions`. ## Start a funnel safely Do not directly insert `FunnelSubscriber` rows from a companion plugin. Use `FunnelProcessor::startFunnelSequence()`, which creates or finds the contact, handles pending/double-opt-in state, checks duplicates, creates the funnel subscriber, records the start hook, and processes immediate steps. ```php use FluentCrm\App\Models\Funnel; use FluentCrm\App\Services\Funnel\FunnelProcessor; if (!function_exists('FluentCrmApi')) { return; } $contact = FluentCrmApi('contacts')->getContactByUserRef($userId); $funnel = Funnel::where('id', (int) $funnelId) ->where('status', 'published') ->where('type', 'funnels') ->first(); if ($contact && $funnel) { (new FunnelProcessor())->startFunnelSequence($funnel, [], [ 'source_trigger_name' => 'my_plugin_event', 'source_ref_id' => (int) $eventId, ], $contact); } ``` When you do not yet have a contact, pass subscriber data as the second argument: ```php (new FunnelProcessor())->startFunnelSequence($funnel, [ 'email' => sanitize_email($email), 'first_name' => sanitize_text_field($firstName), 'status' => 'subscribed', ], [ 'source_trigger_name' => 'my_plugin_event', 'source_ref_id' => (int) $eventId, ]); ``` Core manual attach uses the same pattern in `SubscriberController`: it filters contacts not already in the funnel and starts them with `source_trigger_name => fcrm_manual_attach`. ## Duplicate and status guards `FunnelProcessor::startSequences()` calls `FunnelHelper::ifAlreadyInFunnel($funnelId, $subscriberId)` and also relies on a unique DB constraint on `(funnel_id, subscriber_id)`. Keep this path intact. Processing later steps is status-gated. `processFunnelAction()` only processes contacts with status `subscribed` or `transactional` unless the funnel setting `__force_run_actions` is `yes`. Otherwise the funnel subscriber is marked `cancelled`. The follow-up processor selects published funnels, due `next_execution_time`, and statuses from: ```php apply_filters('fluent_crm/funnel_subscriber_statuses', ['active']); ``` In 3.1.13 it also has batch controls: - `fluent_crm/funnel_processor_batch_limit`, default `200` - `fluent_crm/funnel_processor_max_processing_seconds`, default `55` ## Resume from a benchmark For benchmarks or goal-style entry points, use `startFunnelFromSequencePoint($startSequence, $subscriber, $args, $metricArgs)`. It records a `FunnelMetric`, starts from the benchmark point if allowed, or advances an existing funnel subscriber when the target point is ahead of the current progress. Do not create a new funnel subscriber manually for benchmark resumes. The processor handles: - `can_enter = no` - already completed or cancelled funnels - `starting_sequence_id` - `last_sequence_id` - `next_sequence_id` - pending contacts `recordFunnelMetric()` uses `FunnelMetric::firstOrCreate()` and catches a race where another process inserted the metric between select and insert. ## Read automation progress Use ORM reads for reports and conditional logic: ```php use FluentCrm\App\Models\FunnelSubscriber; $runs = FunnelSubscriber::where('subscriber_id', (int) $contactId) ->with(['funnel', 'next_sequence_item', 'last_sequence']) ->orderBy('id', 'DESC') ->get(); ``` Prefer read-only access unless you are implementing FluentCRM internals. If you must update a run, update only state fields you own and do not skip the processor's hook path for executing actions. ## Pro email sequences `FluentCampaign\App\Models\Sequence` is a Pro email sequence. It maps `fc_campaigns` with a global scope `type = email_sequence`. Its child emails are `SequenceMail` rows, and each enrolled contact is tracked by `SequenceTracker` in `fc_sequence_tracker`. Enroll contacts through `Sequence::subscribe()`: ```php use FluentCampaign\App\Models\Sequence; use FluentCampaign\App\Models\SequenceTracker; use FluentCrm\App\Models\Subscriber; $sequence = Sequence::find((int) $sequenceId); $contact = Subscriber::find((int) $contactId); if ($sequence && $contact) { $already = SequenceTracker::where('campaign_id', $sequence->id) ->where('subscriber_id', $contact->id) ->first(); if (!$already) { $sequence->subscribe([$contact]); } } ``` `Sequence::subscribe()` loads `SequenceMail` rows ordered by delay, schedules first batch emails in `fc_campaign_emails`, parses subject/body smart codes with `fluent_crm/parse_campaign_email_text`, and creates or updates `SequenceTracker`. Unsubscribe through the sequence model: ```php $sequence->unsubscribe([(int) $contactId], 'Removed by My Plugin'); ``` This marks the tracker `cancelled` and cancels scheduled sequence emails. Do not delete `SequenceTracker` directly unless you intentionally want the admin-controller behavior, which removes tracker rows without cancelling scheduled emails. `SequenceTracker` has a global scope `type = sequence_tracker`; status defaults to `active`. `ofNextTrackers()` selects active due trackers by `next_execution_time <= current_time('mysql')`. ## Common mistakes - Do not confuse `FunnelSequence` with `FluentCampaign\App\Models\Sequence`. - Do not insert into `fc_funnel_subscribers` for normal integrations; call `FunnelProcessor`. - Do not write `completed` through `changeFunnelSubSequenceStatus()` for normal action progress; that helper's default/canon is `complete`. Reserve `completed` for full automation run status and Pro email sequence tracker status. - Do not enroll a contact into an email sequence twice. Check `SequenceTracker` first. - Do not process unsubscribed contacts unless the funnel explicitly uses `__force_run_actions`. ## Cross-references - Use `fluentcrm-funnel-trigger` when registering a new trigger in the automation builder. - Use `fluentcrm-funnel-action` for custom action step classes. - Use `fluentcrm-funnel-benchmark` for custom benchmark step classes. - Use `fluentcrm-contact-models` for contact/list/tag CRUD before starting automations. ## References - Local source: `FunnelProcessor.php`, `FunnelHelper.php`, `FunnelSubscriber.php`, `FunnelSequence.php`, Pro `Sequence.php`, and `SequenceTracker.php`. - FluentCRM docs: FunnelSubscriber model, FunnelSequence model, and Fluent ORM. - Official documentation: <https://developers.fluentcrm.com/database/models/funnelSubscriber> - Official documentation: <https://developers.fluentcrm.com/database/models/funnelSequence> - Official documentation: <https://developers.fluentcrm.com/database/orm/> - Verified source paths: - `fluent-crm/app/Models/Funnel.php` - `fluent-crm/app/Models/FunnelMetric.php` - `fluent-crm/app/Http/Controllers/SubscriberController.php` - `fluentcampaign-pro/app/Models/SequenceMail.php` - `fluentcampaign-pro/app/Http/Controllers/SequenceController.php`
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.