$npx -y skills add Lonsdale201/wp-agent-skills --skill fluentcrm-funnel-actionBuild a custom FluentCRM funnel action — a per-contact sequence step run
| 1 | # FluentCRM: register a custom funnel action |
| 2 | |
| 3 | For developers building a companion plugin that needs to add a step inside FluentCRM's automation funnel — "Enroll user to LMS course", "Issue Woo coupon", "Send to webhook", "Update post status". Each action is a block the admin drags into the funnel sequence; when a contact reaches the block, the handler runs once per (contact, sequence) tuple. Extends `FluentCrm\App\Services\Funnel\BaseAction`. Verified end-to-end against FluentCRM 3.1.8 source. |
| 4 | |
| 5 | ## API stability note |
| 6 | |
| 7 | `BaseAction`, the `fluentcrm_funnel_blocks` / `fluentcrm_funnel_block_fields` filter pair, the per-action `fluentcrm_funnel_sequence_handle_{name}` action, and the `(subscriber, sequence, funnelSubscriberId, funnelMetric)` handler signature have been stable since 2.7. The implicit sequence-progress pattern (`FunnelProcessor` sets `last_sequence_status = 'complete'` before dispatching the handler) has been in place since 1.2 of the funnel processor. |
| 8 | |
| 9 | ## Misconception this skill corrects |
| 10 | |
| 11 | > "I'll mark the sequence as `'completed'` from my handler when the work succeeds." |
| 12 | |
| 13 | Two bugs in one sentence. |
| 14 | |
| 15 | **Bug A: the canonical string depends on the column.** `FunnelProcessor::processSequence()` writes `FunnelSubscriber.last_sequence_status = 'complete'` through `FunnelHelper::changeFunnelSubSequenceStatus()`. The full automation run status uses `FunnelSubscriber.status = 'completed'`, and `FunnelMetric.status` defaults to `'completed'` in the migration/model. Writing `'completed'` to `last_sequence_status` is wrong; writing `'complete'` to `FunnelMetric.status` is also wrong for the current reporting model. |
| 16 | |
| 17 | **Bug B: you don't need to mark anything as complete on success.** `FunnelProcessor::processSequence()` calls `FunnelHelper::changeFunnelSubSequenceStatus($funnelSubscriberId, $sequence->id, 'complete')` **before** firing `do_action('fluentcrm_funnel_sequence_handle_' . $sequence->action_name, ...)`. By the time your handler runs, the sequence is already marked `'complete'`. Your job is to override only when you want a different outcome: |
| 18 | |
| 19 | - Returning normally → `last_sequence_status` stays `'complete'`; the metric row stays at its default successful `'completed'` state |
| 20 | - Early-return because a precondition failed → call `changeFunnelSubSequenceStatus(..., 'skipped')` AND set `$funnelMetric->status = 'skipped'` |
| 21 | - API call failed → same pattern with `'skipped'` (or `'failed'` — see "Status vocabulary" below) |
| 22 | |
| 23 | Other AI-prone misconceptions: |
| 24 | |
| 25 | - **"I'll register my action on `fluent_crm/after_init`."** Same timing bug as triggers. `BaseAction::register()` adds itself to two filters (`fluentcrm_funnel_blocks`, `fluentcrm_funnel_block_fields`) plus one action listener (`fluentcrm_funnel_sequence_handle_{name}`). The block / field filters drive the editor UI, and the action listener powers runtime. **Instantiate on `fluentcrm_loaded` priority below 10** so the action is present before FluentCRM's `init` funnel item/listener passes. See `fluentcrm-funnel-trigger` for the lifecycle diagram. |
| 26 | - **"I need to record success metrics in `$funnelMetric` myself."** No — the metric row was already created by `FunnelProcessor::recordFunnelMetric()` immediately before dispatching the handler. Your handler receives the live model. Write `$funnelMetric->notes = '...'` (visible in the admin's automation log row) and `$funnelMetric->status = '...'` only on skip/failure overrides, then `$funnelMetric->save()`. Don't `new FunnelMetric()` yourself. |
| 27 | - **"`$subscriber->user_id` is the WP user ID."** Sometimes. The `Subscriber` model column `user_id` is the LINKED WP user, but |