Workflow Automation Engine Guide
Last Updated: 2026-01-22 Status: Enhanced with Unified Notification Actions, Templates Gallery, Tooltips, Dry-Run & Validation, Discovery Workflows, State Duration Triggers, Test Sandbox Plan Reference: 031-workflow-automation-engine.md, 063-workflow-automation-enhancement.md, 119-enhanced-workflow-triggers.md, 125-client-stage-email-automation.md, 129-discovery-flow-email-notifications.md, 196-workflow-action-ux-improvements.md, 197-workflow-test-sandbox.md
Overview
The Workflow Automation Engine enables administrators to create automated workflows that trigger actions based on events in the portal. It supports conditional logic, multiple actions, and integrations with notifications, status updates, and external services.
Table of Contents
- Accessing Workflows
- Workflow Concepts
- How to Use
- Triggers & Actions
- Workflow Analytics
- Workflow Debugging
- Workflow Test Sandbox
- Workflow Templates
- Webhook Triggers
- Technical Architecture
- Related Features
Accessing Workflows
Navigation
| Access Point | Location | URL | Role |
|---|---|---|---|
| Workflows | Admin sidebar | /admin/workflows | Admin |
| Create Workflow | Workflows page | /admin/workflows/create | Admin |
| Workflow Analytics | Admin More menu | /admin/workflows-analytics | Admin |
| Workflow Templates | Admin More menu | /admin/workflow-templates | Admin |
| Test Sandbox | Workflows page nav | /admin/workflow-tests | Admin |
| Execution Debug | Execution detail | /admin/workflow-executions/{id}/debug | Admin |
Permissions
| Action | Admin | Client User |
|---|---|---|
| View workflows | ✅ | ❌ |
| Create workflows | ✅ | ❌ |
| Edit workflows | ✅ | ❌ |
| Delete workflows | ✅ | ❌ |
| View execution logs | ✅ | ❌ |
Workflow Concepts
Components
| Component | Description |
|---|---|
| Trigger | Event that starts the workflow |
| Conditions | Rules that must be met |
| Actions | Tasks to execute |
| Variables | Dynamic data from trigger |
Workflow Flow
Trigger → Check Conditions → Execute Actions → Log Results
Example Workflow
"Send reminder when invoice is overdue":
- Trigger: Invoice due date passed
- Condition: Status is "sent" (not paid)
- Action: Send reminder email to client
- Action: Notify admin
How to Use
Creating a Workflow
- Navigate to Admin → Workflows
- Click "Create Workflow"
- Choose a Template (optional):
- Browse the templates gallery at the top
- Click a template to create a workflow from it
- Or continue with a blank workflow
- Enter workflow name and description
- Select Trigger:
- Choose trigger type (e.g., "Invoice status changed")
- Configure trigger options
- Hover over the help icon for guidance
- Add Conditions (optional):
- Click "Add Condition"
- Set field, operator, value
- Add Actions:
- Click "Add Action"
- Choose action type
- Configure action parameters
- Preview Workflow (New):
- Click "Preview Workflow" to see dry-run
- View the execution flow: Trigger → Conditions → Actions
- Verify the workflow logic before saving
- Enable workflow
- Click "Save Workflow" (validates before save)
Templates Gallery (New)
The create workflow page now displays a gallery of templates at the top:
Features:
- Shows up to 6 most popular templates
- Displays template name, category, and usage count
- One-click to create workflow from template
- Link to full templates library
Access: Available at /admin/workflows/create
Inline Help Tooltips (New)
The workflow builder includes contextual help tooltips:
| Field | Tooltip Content |
|---|---|
| Name | "Give your workflow a descriptive name..." |
| Trigger Type | "When should this workflow run?..." |
| Model | "Which type of record should trigger..." |
| Priority | "Higher priority workflows run first..." |
| Active | "Only active workflows will run..." |
| Conditions | "Conditions filter when the workflow runs..." |
| Actions | "Actions are what the workflow does..." |
| Action Type | Dynamic based on selected type |
| Delay | "Wait this many minutes before running..." |
Component: Uses <x-tooltip> Blade component
Dry-Run Preview (New)
Preview how your workflow will execute before saving:
- Configure your workflow (trigger, conditions, actions)
- Click "Preview Workflow" button
- View the dry-run preview panel showing:
- Step 1: Trigger description and model
- Conditions: Logic operators and comparisons
- Steps 2+: Each action with configuration
- Review the execution flow
- Close preview or make adjustments
Features:
- Shows trigger type and target model
- Displays all conditions with AND/OR logic
- Lists each action with its delay time
- Shows email recipients, webhook URLs, etc.
Client-Side Validation (New)
The workflow form validates before submission:
Validations:
- Workflow name is required
- Model must be selected for model-based triggers
- At least one action is required
- Email actions require recipient and subject
- Webhook actions require URL
- Conditions require field name
Error Display:
- Yellow warning box at top of form
- Lists all validation errors
- Scrolls to top when errors are found
- Prevents form submission until fixed
Editing a Workflow
- Navigate to Admin → Workflows → [Workflow]
- Click "Edit"
- Modify trigger, conditions, or actions
- Save changes
Testing a Workflow
- Navigate to workflow detail
- Click "Test"
- Provide sample data
- View simulated execution
- Check for errors
Viewing Execution Logs
- Navigate to Admin → Workflows → [Workflow]
- Click "Logs" tab
- View execution history:
- Trigger time
- Conditions evaluated
- Actions executed
- Status (success/failed)
Triggers & Actions
Available Triggers
| Trigger | Event |
|---|---|
model_created | Model instance created |
model_updated | Model instance updated |
model_deleted | Model instance deleted |
client_stage_changed | Client stage transition |
project.status_changed | Project status updated |
invoice.created | New invoice created |
invoice.sent | Invoice sent to client |
invoice.overdue | Invoice past due date |
invoice.paid | Invoice marked paid |
file.uploaded | File uploaded |
user.registered | New user registered |
scheduled | Time-based trigger |
client_state_duration | Client in state for X days |
webhook | External webhook trigger |
manual | Manual trigger |
Client Stage Changed Trigger
The client_stage_changed trigger fires when a client transitions between lifecycle stages. Use trigger_config to filter by specific stage:
// Workflow definition
[
'trigger_type' => 'client_stage_changed',
'trigger_config' => ['stage' => 'qualified'], // Only fires for this stage
]
Available Stages: prospect, questionnaire_pending, questionnaire_submitted, qualified, discovery_scheduled, discovery_completed, proposal_sent, proposal_accepted, active, completed, support, declined, churned
Client State Duration Trigger
The client_state_duration trigger fires for clients who have been in a specific stage for a certain number of days. This is ideal for follow-up reminders and time-based escalations.
// Workflow definition
[
'trigger_type' => 'client_state_duration',
'trigger_config' => [
'stage' => 'questionnaire_pending', // Which stage to monitor
'min_days' => 3, // Minimum days in stage
'max_days' => 7, // Maximum days (optional, null = no limit)
'deduplication_hours' => 72, // Prevent re-sending within X hours
],
]
Configuration Options:
| Option | Required | Default | Description |
|---|---|---|---|
stage | Yes | - | Client stage to monitor |
min_days | No | 1 | Minimum days client must be in stage |
max_days | No | null | Maximum days (stops after this) |
deduplication_hours | No | 24 | Hours before same workflow can re-trigger |
How It Works:
- The
workflows:check-state-durationcommand runs hourly - Finds all active workflows with
client_state_durationtrigger - For each workflow, queries clients matching stage + duration criteria
- Checks deduplication (no execution within
deduplication_hours) - Triggers workflow for each matching client
Example Use Cases:
// Questionnaire reminder after 3 days
'trigger_config' => [
'stage' => 'questionnaire_pending',
'min_days' => 3,
'max_days' => 6, // 7-day reminder takes over
'deduplication_hours' => 72,
],
// Proposal follow-up after 5 days
'trigger_config' => [
'stage' => 'proposal_sent',
'min_days' => 5,
'max_days' => 12,
'deduplication_hours' => 120,
],
// At-risk client alert after 30 days inactive
'trigger_config' => [
'stage' => 'active',
'min_days' => 30,
'deduplication_hours' => 168, // Weekly
],
Running Manually:
# Test what would be triggered (dry-run)
php artisan workflows:check-state-duration --dry-run
# Actually trigger workflows
php artisan workflows:check-state-duration
Scheduler:
The command is scheduled to run hourly via routes/console.php.
Model Updated Trigger with Conditions
The model_updated trigger can be combined with conditions to detect specific field changes:
// Workflow with changed_to condition
$workflow = Workflow::create([
'trigger_type' => 'model_updated',
'trigger_model' => DiscoveryQuestionnaire::class,
]);
WorkflowCondition::create([
'workflow_id' => $workflow->id,
'field' => 'status',
'operator' => 'changed_to', // Detects field transition
'value' => 'submitted',
]);
Trigger Variables
Each trigger provides variables for conditions and actions:
invoice.created:
{{invoice.id}}
{{invoice.number}}
{{invoice.total}}
{{invoice.due_date}}
{{client.name}}
{{client.email}}
Observed Models for Triggers
The workflow system observes the following models for model_created, model_updated, and model_deleted triggers:
| Category | Models | Purpose |
|---|---|---|
| Core Entities | Client, Project, Invoice, User | Primary business objects |
| Client Intake | DiscoveryQuestionnaire, DiscoveryCall, Payment | Discovery and onboarding flow |
| Project Management | ProjectFile, ProjectMilestone, ProjectPlan, InvoiceItem | Project tracking |
| Communication | Message, DocumentRequest, Conversation | Client communication |
This comprehensive model coverage enables workflows for the entire client lifecycle, from initial questionnaire submission through project completion.
Available Actions
| Action | Description |
|---|---|
send_notification | Unified notification - supports email, in-app, SMS, Slack channels |
update_field | Update model field values |
webhook | HTTP webhook call |
request_approval | Pause workflow until approved |
delay | Wait before next action |
Note: The legacy action types
send_emailandsend_template_emailare still supported for backward compatibility but will be automatically mapped tosend_notificationwith channel set to
Unified Send Notification Action
The send_notification action is the recommended way to send notifications through workflows. It supports multiple channels and offers a consistent configuration interface.
Configuration:
{
"type": "send_notification",
"config": {
"channel": "email",
"notification_template_id": 5,
"recipient_type": "client",
"subject": "Custom subject if no template",
"body": "Custom message body if no template"
}
}
Configuration Options:
| Field | Required | Values | Description |
|---|---|---|---|
channel | Yes | email, in_app, sms, slack | Notification delivery channel |
notification_template_id | No | Template ID or null | Use predefined template |
recipient_type | Yes | See below | How to determine recipient |
recipient_email | If custom | Email or {variable} | Custom email address |
recipient_phone | If SMS | Phone number | For SMS channel |
subject | If no template | String | Email subject line |
body | If no template | String | Message body |
Recipient Types:
| Type | Description |
|---|---|
client | Primary user associated with the client |
all_client_users | All users associated with the client |
specific_user | Specific user by ID (requires recipient_id) |
role | All users with a role (requires recipient_role) |
custom | Custom email/phone (requires recipient_email or recipient_phone) |
Channel Support:
| Channel | Template Support | Custom Message | Recipient Types |
|---|---|---|---|
email | ✅ | ✅ | All |
in_app | ✅ | ✅ | User-based only |
sms | ✅ | ✅ | User-based only |
slack | ✅ | ✅ | User-based only |
Example - Email with Template:
{
"type": "send_notification",
"config": {
"channel": "email",
"notification_template_id": 5,
"recipient_type": "client"
}
}
Example - Email without Template:
{
"type": "send_notification",
"config": {
"channel": "email",
"recipient_type": "custom",
"recipient_email": "{client_email}",
"subject": "Your invoice is ready",
"body": "Dear {client_name}, your invoice #{invoice_number} is ready for review."
}
}
Example - In-App Notification:
{
"type": "send_notification",
"config": {
"channel": "in_app",
"recipient_type": "role",
"recipient_role": "admin",
"subject": "New client registered",
"body": "{client_name} has completed their questionnaire."
}
}
Migrating Legacy Workflow Actions
A migration command is available to convert existing workflows using legacy action types to the unified send_notification format:
# Preview what would be migrated (dry-run)
php artisan workflows:migrate-notification-actions --dry-run
# Actually migrate the actions
php artisan workflows:migrate-notification-actions
The migration converts:
send_email→send_notificationwithchannel: emailsend_template_email→send_notificationwithchannel: email
Note: This migration is optional. Legacy action types continue to work due to backward compatibility mapping in the WorkflowEngine.
Send Template Email Action (Legacy)
The send_template_email action sends emails using pre-defined NotificationTemplates, enabling reusable email content managed through the admin UI.
Configuration:
{
"action": "send_template_email",
"notification_template_id": 5,
"to": "{client_email}"
}
Benefits:
- Reusable email templates across multiple workflows
- Templates editable in database without code changes
- Variable substitution in both
{var}and{{ var }}formats - Queue-based async execution
Usage:
- Create a NotificationTemplate in Admin → Notifications → Templates
- Create a workflow with your desired trigger
- Add action type: Send Template Email
- Select the template and set recipient to
{client_email} - Save and activate
Available Variables:
| Variable | Description |
|---|---|
{client}, {client_name}, {company_name} | Client name |
{client_email}, {client_phone} | Client contact info |
{stage}, {new_stage} | Current client stage |
{previous_stage}, {old_stage} | Previous stage |
{date}, {time}, {datetime} | Current date/time |
Action Configuration
Send Email:
{
"action": "send_email",
"to": "{{client.email}}",
"subject": "Invoice {{invoice.number}} Reminder",
"template": "invoice-reminder",
"variables": {
"invoice_number": "{{invoice.number}}",
"amount": "{{invoice.total}}"
}
}
Update Status:
{
"action": "update_status",
"entity": "project",
"id": "{{project.id}}",
"status": "completed"
}
Send Webhook:
{
"action": "send_webhook",
"url": "https://example.com/webhook",
"method": "POST",
"payload": {
"event": "invoice_paid",
"invoice_id": "{{invoice.id}}"
}
}
Condition Operators
| Operator | Description | Example |
|---|---|---|
equals | Exact match | status equals "active" |
not_equals | Not equal | status not_equals "draft" |
contains | String contains | name contains "Corp" |
greater_than | Numeric greater | total > 1000 |
less_than | Numeric less | days_overdue < 30 |
is_empty | Null or empty | notes is_empty |
is_not_empty | Has value | email is_not_empty |
Combining Conditions
{
"match": "all",
"conditions": [
{"field": "status", "operator": "equals", "value": "sent"},
{"field": "days_overdue", "operator": "greater_than", "value": 7}
]
}
Workflow Analytics
The Workflow Analytics dashboard provides comprehensive insights into workflow performance and execution patterns.
Accessing Analytics
Navigate to Admin → More → Workflow Analytics or /admin/workflows-analytics.
Dashboard Metrics
| Metric | Description |
|---|---|
| Total Workflows | Count of all workflows in the system |
| Active Workflows | Workflows currently enabled |
| Executions This Month | Number of executions in current month |
| Success Rate | Percentage of successful executions |
| Average Execution Time | Mean execution duration in milliseconds |
Top Workflows
The analytics dashboard shows:
- Most frequently executed workflows
- Success rates per workflow
- Execution counts
Workflows Needing Attention
Automatically identifies workflows that:
- Have high failure rates (>20%)
- Have slow execution times (>5 seconds average)
Recommendations
The system provides automated recommendations:
- Warning: Workflows with high failure rates
- Info: Optimization suggestions
Performance Distribution
View execution patterns:
- By trigger type
- By time of day (hourly distribution)
- Recent failures with error messages
Workflow Debugging
The debug view provides detailed execution analysis for troubleshooting.
Accessing Debug View
- Navigate to a workflow execution
- Click "Debug" or access
/admin/workflow-executions/{id}/debug
Execution Summary
| Field | Description |
|---|---|
| Status | Current execution status |
| Duration | Total execution time in ms |
| Triggered By | What triggered the execution |
| Started At | Execution start timestamp |
Performance Bottlenecks
Automatically identifies actions that:
- Take longer than 1 second to execute
- Consume disproportionate execution time
Error Analysis
When executions fail:
- Shows which action failed
- Displays error messages
- Provides stack trace context
Action Timeline
Step-by-step visualization showing:
- Action execution order
- Duration per action
- Status per action (completed/failed/skipped)
- Error details if failed
Execution History
Quick access to recent executions for comparison:
- Success/failure status
- Execution times
- Navigate between executions
Workflow Test Sandbox
The Workflow Test Sandbox provides a comprehensive testing environment for workflows, allowing you to simulate triggers, preview condition evaluations, and test workflow behavior without affecting production data.
Accessing the Test Sandbox
Navigate to Admin → Workflows → Test Sandbox or /admin/workflow-tests.
Features
| Feature | Description |
|---|---|
| Dry Run Mode | Preview what would happen without executing actions |
| Execute Mode | Run workflow actions with notifications sent to you instead of actual recipients |
| Trigger Simulation | Simulate different trigger scenarios (stage changes, model events) |
| Condition Evaluation | See detailed pass/fail status for each condition with actual vs expected values |
| Action Preview | Preview email content, webhooks, and other action outputs |
| Execution Timeline | Visual timeline of action execution order with delays |
| Batch Testing | Test workflow against multiple records to see pass rates |
| Value Overrides | Test "what if" scenarios by overriding field values |
| Run History | Track test runs and compare results over time |
Creating a Test Configuration
- Navigate to the Test Sandbox
- Click "New Test"
- Enter a test name
- Select the workflow to test
- Choose run mode:
- Dry Run: Safe preview mode, no actions executed
- Execute Mode: Actions run, but notifications go to you
- Configure trigger simulation (if applicable)
- Select a test subject (Client, Project, etc.)
- Optionally add value overrides
- Save and run
Run Modes
Dry Run Mode (Recommended for testing):
- Shows what would happen without executing
- Displays condition evaluation with actual values
- Previews action outputs (email content, webhook payloads)
- Safe to run repeatedly
Execute Mode:
- Actually runs workflow actions
- Notifications are redirected to you instead of actual recipients
- Useful for testing email formatting and delivery
- Warning banner displayed before running
Trigger Simulation
Simulate different trigger scenarios based on the workflow's trigger type:
| Trigger Type | Simulation Options |
|---|---|
client_stage_changed | Select from/to stages |
client_state_duration | Set days in stage |
model_created | Provide sample model data |
model_updated | Specify changed fields and values |
webhook | Provide webhook payload JSON |
Condition Evaluation Display
For each condition, the test results show:
| Field | Description |
|---|---|
| Field | The condition field being evaluated |
| Operator | Comparison operator (equals, contains, etc.) |
| Expected | The value configured in the condition |
| Actual | The real value from the test subject |
| Status | Pass or Fail indicator |
| Failure Reason | Human-readable explanation of why it failed |
Batch Testing
Test a workflow against multiple records to understand its reach:
- Navigate to a test configuration
- Click "Batch Test"
- Configure filters (optional):
- For Clients: stage, status
- For Projects: status
- For Invoices: status
- Select number of records (up to 100)
- Run batch test
Batch Results:
- Total tested count
- Would trigger count
- Would not trigger count
- Pass rate percentage
- Per-record breakdown with failing reasons
- Export to CSV option
Value Overrides
Test "what if" scenarios without modifying actual data:
- In the test configuration, expand "Value Overrides"
- Add override entries:
- Field name (e.g.,
stage,total,status) - Override value
- Field name (e.g.,
- The workflow evaluates as if the subject has these values
Example:
Testing a workflow that triggers when stage = qualified:
- Override
stagetoqualified - Run dry run to see if the workflow would execute
Run History
Track all test runs for comparison:
- Navigate to test configuration
- Click "View History"
- See all runs with:
- Timestamp
- Mode (dry run / execute)
- Result status
- Conditions pass count
- Compare two runs side-by-side
Technical Details
Models:
WorkflowTest- Saved test configurationsWorkflowTestRun- Individual test run results
Services:
TriggerSimulator- Generates simulation fields and contextWorkflowBatchTester- Handles batch testing against multiple recordsWorkflowEngine::dryRun()- Non-destructive workflow evaluation
Routes:
GET /admin/workflow-tests # List tests
GET /admin/workflow-tests/create # Create form
POST /admin/workflow-tests # Store test
GET /admin/workflow-tests/{test}/edit # Edit form
POST /admin/workflow-tests/{test}/run # Run test
GET /admin/workflow-tests/{test}/{run} # View results
GET /admin/workflow-tests/{test}/history # Run history
GET /admin/workflow-tests/{test}/batch # Batch testing
GET /admin/workflow-tests/{test}/compare # Compare runs
Seeding Test Data:
The WorkflowTestSeeder creates test configurations for all seeded workflows:
php artisan db:seed --class=WorkflowTestSeeder
The seeder:
- Creates a dry run test for each workflow
- Creates execute tests for ~50% of workflows
- Auto-generates value overrides from workflow conditions
- Handles
changed_toconditions with proper context - Configures trigger simulation based on workflow type
Workflow Templates
Workflow templates allow you to save and reuse workflow configurations.
Accessing Templates
Navigate to Admin → More → Workflow Templates or /admin/workflow-templates.
Creating Templates
- Navigate to a workflow detail page
- Click "Save as Template"
- Provide a name and optional category
- Click "Save"
Using Templates
- Navigate to Workflow Templates
- Find the desired template
- Click "Use Template"
- A new workflow is created from the template
- Customize and activate
Template Categories
Templates can be organized by category:
- Filter by category on the templates page
- Categories are user-defined
Template Information
Each template shows:
- Name and description
- Category tag
- Trigger type
- Number of actions
- Number of conditions
- Usage count
- Creator
Webhook Triggers
Workflows can be triggered by external webhooks for integration with third-party services.
Enabling Webhook Triggers
- Create or edit a workflow
- Set trigger type to "Webhook"
- Save the workflow
- A unique webhook URL is generated
Getting Webhook URL
- Navigate to the workflow detail
- Click "Get Webhook URL"
- Copy the provided URL
Webhook URL Format
https://your-domain.com/api/webhooks/workflows/{token}
Regenerating Webhook Token
For security, you can regenerate the webhook token:
- Navigate to workflow detail
- Click "Regenerate Webhook"
- Update any integrations with the new URL
Webhook Request Format
Send a POST request to the webhook URL:
curl -X POST https://your-domain.com/api/webhooks/workflows/{token} \
-H "Content-Type: application/json" \
-d '{"key": "value"}'
The request body becomes available as trigger data.
Technical Architecture
Models
Workflow Model: app/Models/Workflow.php
class Workflow extends Model
{
protected $fillable = [
'name',
'description',
'trigger',
'trigger_config',
'conditions',
'actions',
'is_active',
];
protected $casts = [
'trigger_config' => 'array',
'conditions' => 'array',
'actions' => 'array',
'is_active' => 'boolean',
];
}
WorkflowExecution Model: app/Models/WorkflowExecution.php
class WorkflowExecution extends Model
{
protected $fillable = [
'workflow_id',
'trigger_data',
'status',
'results',
'error',
'started_at',
'completed_at',
];
}
Services
Location: app/Services/Workflow/
WorkflowEngine
Core workflow execution engine.
class WorkflowEngine
{
public function trigger(string $event, array $data): void
{
$workflows = Workflow::active()
->where('trigger', $event)
->get();
foreach ($workflows as $workflow) {
ExecuteWorkflowJob::dispatch($workflow, $data);
}
}
public function execute(Workflow $workflow, array $data): WorkflowExecution
{
$execution = WorkflowExecution::create([
'workflow_id' => $workflow->id,
'trigger_data' => $data,
'status' => 'running',
'started_at' => now(),
]);
try {
if ($this->evaluateConditions($workflow->conditions, $data)) {
$results = $this->executeActions($workflow->actions, $data);
$execution->update([
'status' => 'completed',
'results' => $results,
'completed_at' => now(),
]);
} else {
$execution->update([
'status' => 'skipped',
'completed_at' => now(),
]);
}
} catch (Exception $e) {
$execution->update([
'status' => 'failed',
'error' => $e->getMessage(),
'completed_at' => now(),
]);
}
return $execution;
}
}
WorkflowDebugger
Debugging and analysis service for workflow executions.
class WorkflowDebugger
{
public function startSession(WorkflowExecution $execution): array;
public function logStep(int $executionId, string $step, array $data): void;
public function logCondition(int $executionId, array $condition, bool $result): void;
public function logAction(int $executionId, string $action, array $data): void;
public function logError(int $executionId, string $error, array $context): void;
public function endSession(int $executionId, string $status): void;
public function analyzeExecution(WorkflowExecution $execution): array;
public function getExecutionHistory(Workflow $workflow, int $limit): array;
}
WorkflowAnalyticsService
Analytics and insights service for workflow metrics.
class WorkflowAnalyticsService
{
public function getDashboardStats(): array;
public function getPerformanceMetrics(int $days = 30): array;
public function getTopWorkflows(int $limit = 10): array;
public function getWorkflowsNeedingAttention(): array;
public function getTriggerTypeStats(): Collection;
public function getActionTypeStats(): Collection;
public function getHourlyDistribution(): array;
public function getRecentFailures(int $limit = 10): array;
public function getRecommendations(): array;
}
Event Integration
// In EventServiceProvider or Observer
Event::listen(InvoiceCreated::class, function ($event) {
app(WorkflowEngine::class)->trigger('invoice.created', [
'invoice' => $event->invoice->toArray(),
'client' => $event->invoice->client->toArray(),
]);
});
Controller
Location: app/Http/Controllers/Admin/WorkflowController.php
| Method | Route | Description |
|---|---|---|
index() | GET /admin/workflows | List workflows |
create() | GET /admin/workflows/create | Create form |
store() | POST /admin/workflows | Save workflow |
show() | GET /admin/workflows/{id} | View workflow |
edit() | GET /admin/workflows/{id}/edit | Edit form |
update() | PUT /admin/workflows/{id} | Update workflow |
destroy() | DELETE /admin/workflows/{id} | Delete workflow |
test() | POST /admin/workflows/{id}/test | Test run |
logs() | GET /admin/workflows/{id}/logs | Execution logs |
analytics() | GET /admin/workflows-analytics | Analytics dashboard |
debugExecution() | GET /admin/workflow-executions/{id}/debug | Debug execution |
templates() | GET /admin/workflow-templates | Template library |
saveAsTemplate() | POST /admin/workflows/{id}/save-as-template | Save as template |
createFromTemplate() | POST /admin/workflow-templates/{id}/use | Create from template |
getWebhookUrl() | GET /admin/workflows/{id}/webhook-url | Get webhook URL |
regenerateWebhook() | POST /admin/workflows/{id}/regenerate-webhook | Regenerate webhook |
Routes
// Workflow resource routes
Route::resource('workflows', WorkflowController::class);
Route::post('/workflows/{workflow}/test', [WorkflowController::class, 'test']);
Route::post('/workflows/{workflow}/toggle', [WorkflowController::class, 'toggle']);
// Workflow analytics and debugging
Route::get('/workflows-analytics', [WorkflowController::class, 'analytics']);
Route::get('/workflow-executions/{execution}/debug', [WorkflowController::class, 'debugExecution']);
// Workflow templates
Route::get('/workflow-templates', [WorkflowController::class, 'templates']);
Route::post('/workflows/{workflow}/save-as-template', [WorkflowController::class, 'saveAsTemplate']);
Route::post('/workflow-templates/{template}/use', [WorkflowController::class, 'createFromTemplate']);
// Webhook management
Route::get('/workflows/{workflow}/webhook-url', [WorkflowController::class, 'getWebhookUrl']);
Route::post('/workflows/{workflow}/regenerate-webhook', [WorkflowController::class, 'regenerateWebhook']);
Database Tables
Table: workflows
| Column | Type | Description |
|---|---|---|
id | bigint | Primary key |
name | string | Workflow name |
description | text | Description |
trigger_type | string | Trigger event |
trigger_model | string | Model for trigger |
trigger_config | json | Trigger options |
is_active | boolean | Enabled status |
webhook_token | string | Unique webhook token |
visual_config | json | Visual editor config |
version | string | Version number |
priority | integer | Execution priority |
execution_count | integer | Total executions |
last_executed_at | timestamp | Last execution time |
created_at | timestamp | Created date |
Table: workflow_executions
| Column | Type | Description |
|---|---|---|
id | bigint | Primary key |
workflow_id | bigint | Workflow FK |
subject_type | string | Subject model type |
subject_id | bigint | Subject model ID |
status | string | pending/running/completed/failed |
duration_ms | integer | Execution duration in ms |
step_logs | json | Step-by-step logs |
triggered_by | string | Trigger source |
context | json | Execution context |
error_message | text | Error message |
started_at | timestamp | Start time |
completed_at | timestamp | End time |
Table: workflow_templates
| Column | Type | Description |
|---|---|---|
id | bigint | Primary key |
name | string | Template name |
description | text | Description |
category | string | Category name |
definition | json | Workflow definition |
is_public | boolean | Public visibility |
usage_count | integer | Times used |
created_by | bigint | Creator user ID |
created_at | timestamp | Created date |
Discovery Flow Workflows
The system includes pre-built workflows for automating email notifications throughout the client discovery process.
Available Discovery Workflows
| Workflow | Trigger | Condition | Email Template |
|---|---|---|---|
| Discovery: Questionnaire Submitted | model_updated on DiscoveryQuestionnaire | status changed_to 'submitted' | discovery_questionnaire_received |
| Discovery: Lead Qualified | client_stage_changed | stage = 'qualified' | discovery_lead_qualified |
| Discovery: Lead Declined | client_stage_changed | stage = 'declined' | discovery_lead_declined |
| Discovery: Call Scheduled | model_updated on DiscoveryCall | status changed_to 'scheduled' | discovery_call_booked |
| Discovery: Call Completed | model_updated on DiscoveryCall | status changed_to 'completed' | discovery_call_completed |
| Discovery: Proposal Sent | client_stage_changed | stage = 'proposal_sent' | discovery_proposal_sent |
| Discovery: Proposal Accepted | client_stage_changed | stage = 'proposal_accepted' | discovery_proposal_accepted |
Seeding Discovery Workflows
# First, seed the notification templates
php artisan db:seed --class=DiscoveryNotificationTemplatesSeeder
# Then, seed the workflows
php artisan db:seed --class=DiscoveryWorkflowsSeeder
Discovery Variable Resolution
The WorkflowVariableResolver supports DiscoveryQuestionnaire and DiscoveryCall models:
DiscoveryQuestionnaire Variables:
{{ client_name }}
{{ client_email }}
{{ client_phone }}
{{ questionnaire_id }}
{{ submitted_at }}
DiscoveryCall Variables:
{{ client_name }}
{{ client_email }}
{{ client_phone }}
{{ call_id }}
{{ scheduled_at }} (e.g., "Monday, January 20, 2026 at 2:00 PM")
{{ scheduled_date }} (e.g., "January 20, 2026")
{{ scheduled_time }} (e.g., "2:00 PM")
{{ meeting_url }}
Scheduled Call Reminders
In addition to workflow-triggered emails, time-based reminders are sent via a scheduled command:
# Test what reminders would be sent
php artisan discovery:send-call-reminders --dry-run
# Actually send reminders
php artisan discovery:send-call-reminders
The command runs every 15 minutes and sends:
- 24-hour reminders: For calls scheduled 23-25 hours away
- 1-hour reminders: For calls scheduled 30-90 minutes away
Reminder tracking columns (reminder_24h_sent_at, reminder_1h_sent_at) prevent duplicate sends.
Example Workflows
Auto-Complete Project on Final Payment
{
"name": "Complete project on payment",
"trigger": "invoice.paid",
"conditions": [
{"field": "invoice.is_final", "operator": "equals", "value": true}
],
"actions": [
{
"type": "update_status",
"entity": "project",
"id": "{{invoice.project_id}}",
"status": "completed"
},
{
"type": "send_email",
"to": "{{client.email}}",
"template": "project-completed"
}
]
}
Send Overdue Reminders
{
"name": "Send overdue reminder",
"trigger": "scheduled",
"trigger_config": {"cron": "0 9 * * *"},
"conditions": [],
"actions": [
{
"type": "query",
"model": "Invoice",
"where": [
["status", "=", "sent"],
["due_date", "<", "{{now}}"]
],
"each": [
{
"type": "send_email",
"to": "{{item.client.email}}",
"template": "invoice-overdue"
}
]
}
]
}
Related Features
Dependencies
| Feature | Relationship |
|---|---|
| Background Jobs | Async execution |
| Notifications | Email actions |
| Webhooks | Webhook actions |
Complementary Features
| Feature | Description |
|---|---|
| Activity Logging | Logs executions |
| Admin Tools | Management tools |
| Client Intake & Portal Flow | Discovery workflows and email notifications |
Workflow Versioning
Track changes to workflows with version history and rollback capability.
Creating Versions
Versions are automatically created when making significant changes. You can also manually create a version before making changes.
Version History
Navigate to a workflow and click "Version History" to see all versions:
| Field | Description |
|---|---|
| Version | Version number (v1, v2, etc.) |
| Change Notes | Description of changes |
| Created By | User who created the version |
| Published | Whether this version is active |
| Created At | When version was created |
Rolling Back
- Navigate to workflow version history
- Find the version you want to restore
- Click "Rollback to this version"
- Confirm the rollback
The workflow will be restored to the exact state captured in that version.
Technical Details
// Create a version before changes
$versionService = app(WorkflowVersionService::class);
$version = $versionService->createVersion($workflow, 'Before major changes');
// Publish a version (make it active)
$versionService->publish($version);
// Rollback to a specific version number
$versionService->rollback($workflow, 1);
// Get version history
$history = $versionService->getHistory($workflow);
// Compare two versions
$diff = $versionService->compare($version1, $version2);
A/B Testing Workflows
Test different workflow configurations to optimize performance.
Setting Up an A/B Test
- Navigate to workflow detail
- Click "Enable A/B Testing"
- Add variants with different configurations
- Set weights for each variant (e.g., 50/50 split)
- Enable the workflow
Variant Configuration
| Field | Description |
|---|---|
| Name | Variant name (e.g., "Control", "Variant A") |
| Weight | Selection probability (1-100) |
| Is Control | Mark as the baseline variant |
| Definition | Variant-specific workflow configuration |
Monitoring Results
View variant statistics:
- Execution Count: Times this variant was used
- Success Rate: Percentage of successful executions
- Average Duration: Mean execution time
- vs Control: Comparison with control variant
Statistical Significance
The system calculates statistical significance to determine if results are meaningful:
- Minimum 100 executions recommended for significance
- 95% confidence level indicates significant results
Promoting a Winner
Once you have significant results:
- Review comparison statistics
- Click "Promote Variant" on the best performer
- The variant configuration becomes the main workflow
- A/B testing is automatically disabled
Technical Details
// Create variants
$abService = app(WorkflowABTestService::class);
$control = $abService->createVariant($workflow, 'Control', $definition, 50, true);
$variantA = $abService->createVariant($workflow, 'Variant A', $altDefinition, 50, false);
// Select variant for execution (weighted random)
$variant = $abService->selectVariant($workflow);
// Get comparison statistics
$comparison = $abService->compareVariants($workflow);
// End test and optionally promote winner
$winner = $abService->endTest($workflow, promoteWinner: true);
Parallel Branch Execution
Execute multiple workflow branches simultaneously for improved performance.
Defining Parallel Branches
In the workflow definition, create a parallel step:
{
"type": "parallel",
"branches": [
{
"name": "Email Branch",
"steps": [
{"type": "send_email", "config": {...}}
]
},
{
"name": "Webhook Branch",
"steps": [
{"type": "webhook", "config": {...}}
]
}
]
}
Branch Execution
- All branches execute concurrently using Laravel job batching
- Each branch tracks its own status and results
- Execution continues after all branches complete (or fail)
Monitoring Branches
View branch status in execution details:
- Individual branch status (pending/running/completed/failed)
- Per-branch execution duration
- Error messages for failed branches
Best Practices
- Test workflows before enabling
- Use specific conditions to avoid unintended triggers
- Monitor execution logs regularly
- Document complex workflows
- Set up alerts for failed workflows
Troubleshooting
| Issue | Solution |
|---|---|
| Workflow not triggering | Check trigger event and is_active |
| Actions not executing | Review condition logic |
| Email not sending | Check email configuration |
| Performance issues | Limit workflow scope |
See Also
- Background Jobs - Async processing
- Notifications - Notifications
- Webhooks - External integration