Workflows
What Are Workflows?
Workflows automate email marketing tasks by connecting triggers, tasks, and data together in a visual editor. Instead of manually performing repetitive actions, you define a workflow once and it executes automatically.
Real-world examples:
- Welcome email: When a new user signs up (trigger) → send welcome email (task)
- Customer notification: When an order is placed (trigger) → send confirmation email (task) → save to file (task)
- Report generation: On schedule (trigger) → generate PDF (task) → send via email (task) → notify Slack (task)
Key concepts:
- Trigger — Event that starts the workflow
- Tasks — Individual actions that execute in sequence
- DataBus — Passes information between tasks
- Visual editor — Drag-and-drop interface (no code required)
Getting Started
Access Workflows
Workflows are built into Laravel Mail Platform and ready to use immediately:
- Log in to your application
- Navigate to
/workflows - You'll see the workflow builder interface
- Start creating your first workflow
Workflow Basics
Every workflow has this structure:
Trigger (Start Event)
↓
Task 1 (First Action)
↓
Task 2 (Second Action)
↓
Task 3 (Third Action)
↓
Complete
Key principles:
- Workflows start with exactly one trigger
- Tasks execute in the order you set them
- Information flows through the DataBus between tasks
- Each task is independent (can be reused in other workflows)
Triggers
A trigger is the event that starts your workflow. Triggers define when and how your workflow executes.
Available Triggers
Observer Trigger
Watch Eloquent model events and automatically start workflows.
How it works:
- Monitors database model changes (created, updated, deleted, etc.)
- When event occurs, passes the model to workflow
- Workflow executes automatically
Setup:
Add WorkflowObservable trait to your Eloquent model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use LaravelMail\Workflows\Traits\WorkflowObservable;
class User extends Model
{
use WorkflowObservable;
}
Example workflow:
Trigger: User model created
↓
Task: Send welcome email
↓
Task: Add to "New Users" segment
When a new user is created in database, this workflow runs automatically.
Supported events:
created— Model was createdupdated— Model was updateddeleted— Model was deletedrestored— Deleted model was restoredsaved— Model was saved (created or updated)
Button Trigger
Render clickable buttons in your interface that execute workflows when clicked.
How it works:
- Display button in frontend/UI
- User clicks button
- Workflow executes with passed data (optional model)
- Button can include custom styling/classes
Rendering buttons:
Option 1: By Workflow Name
{!! LaravelMail\Workflows\Triggers\ButtonTrigger::renderButtonByName(
'workflow-name',
$model // optional: passes model to workflow
) !!}
Option 2: By Workflow ID
{!! LaravelMail\Workflows\Triggers\ButtonTrigger::renderButtonByWorkflowId(
workflow_id,
$model // optional
) !!}
Option 3: By Category
Render all buttons in a category:
{!! the42coders\Workflows\Triggers\ButtonTrigger::renderButtonsByCategory(
'category-name',
$model // optional
) !!}
Customize button appearance:
You can modify button styling by publishing the blade template:
php artisan vendor:publish --tag=workflow-buttons
Then edit the published view to customize colors, text, classes, etc.
Example button workflow:
Trigger: User clicks "Send Welcome Email" button
↓
Task: Send email to selected user
↓
Task: Log activity
↓
Task: Notify admin via Slack
Triggers Coming Soon
Additional triggers in development:
- Schedule Trigger — Run on schedule (daily, weekly, etc.)
- Webhook Trigger — Triggered by external webhook
- Email Event Trigger — Triggered by email opens/clicks
- API Trigger — Called from API endpoint
Tasks
Tasks are individual actions that execute in sequence. Each task performs one specific job and can use data from previous tasks via the DataBus.
All Available Tasks
| Task | Purpose | Use Case |
|---|---|---|
| ChangeModel | Modify Eloquent model in memory | Update user properties before saving |
| DomPDF | Generate PDF from HTML | Create invoice or report PDF |
| Execute | Run shell commands | Run backups, generate files |
| HtmlInput | Rich text with Blade rendering | Create dynamic email content |
| HttpStatus | Check HTTP status of URL | Monitor endpoints, verify links |
| PregReplace | Find/replace using regex | Transform text data |
| LoadModel | Load model from database | Fetch related user/order data |
| SaveFile | Save data to file | Create logs, exports, archives |
| SaveModel | Save model to database | Persist changes |
| SendMail | Send email | Send notifications, confirmations |
| SendSlackMessage | Send Slack notification | Alert team, post updates |
Task Details
ChangeModel
Modify an Eloquent model's attributes in memory (without saving to database).
Use cases:
- Prepare model for next task
- Update properties before saving
- Transform data
Configuration:
- Select which model attribute to change
- Provide new value
- Value can be static or from DataBus/model
Example workflow:
Trigger: User signs up
↓
Task: ChangeModel - Set status to "pending"
↓
Task: SendMail - Send verification email
↓
Task: SaveModel - Save updated user
DomPDF
Generate PDF files from HTML. Integrates with HtmlInput task for dynamic content.
Use cases:
- Generate invoices
- Create reports
- Export data as PDF
Configuration:
- HTML content (from HtmlInput or static)
- Optional: filename, orientation (portrait/landscape)
- PDF placed on DataBus for next task
Example workflow:
Trigger: Order placed
↓
Task: HtmlInput - Create invoice HTML with order data
↓
Task: DomPDF - Generate PDF from HTML
↓
Task: SendMail - Email invoice PDF to customer
Execute
Run shell commands and capture output.
Use cases:
- Run backups
- Generate files
- Execute system commands
- Process data via CLI
Configuration:
- Command to execute (e.g.,
ls -la,tar czf backup.tar.gz ...) - Output can be placed on DataBus
⚠️ Security: Only use with trusted input. Don't execute user-supplied commands.
Example workflow:
Trigger: Schedule (monthly)
↓
Task: Execute - Run database backup command
↓
Task: SendSlackMessage - Notify backup complete
HtmlInput
Rich text editor that renders Blade templates. Use to create dynamic email content.
Features:
- WYSIWYG editor (Trix)
- Supports Blade syntax
- Access model data:
{{ $model->name }} - Access DataBus:
{{ $databus['variable_name'] }}
Use cases:
- Create dynamic email body
- Generate HTML reports
- Template emails with variables
Configuration:
- Write HTML/Blade in editor
- Can reference:
- Model properties:
{{ $model->email }} - DataBus values:
{{ $databus['key'] }}
Example HTML:
<h1>Welcome, {{ $model->first_name }}!</h1>
<p>Thank you for signing up.</p>
<p>Your account status: {{ $databus['status'] }}</p>
HttpStatus
Check HTTP status of a URL and get the response code.
Use cases:
- Monitor website uptime
- Verify links are working
- Check API endpoints
Configuration:
- URL to check
- Status code placed on DataBus
- Can use DataBus values in URL
Example workflow:
Trigger: Schedule (hourly)
↓
Task: HttpStatus - Check https://example.com
↓
Task: SendSlackMessage - Alert if status is not 200
PregReplace
Find and replace using regular expressions (regex).
Use cases:
- Transform text patterns
- Clean data
- Extract/modify strings
Configuration:
- Regex pattern to match
- Replacement text
- Source (model property or DataBus)
- Result placed on DataBus
Example:
Pattern: /phone_(\d+)/
Replacement: Ext. $1
Input: "call_phone_5551234"
Output: "call_Ext. 5551234"
LoadModel
Fetch an Eloquent model from database.
Use cases:
- Load related data
- Get user by ID
- Retrieve order information
Configuration:
- Model class (e.g.,
App\Models\User) - ID to load (static or from DataBus)
Example workflow:
Trigger: Payment received (webhook passes payment_id)
↓
Task: LoadModel - Load Payment model by ID
↓
Task: LoadModel - Load related Order model
↓
Task: SendMail - Send order confirmation
SaveFile
Write data to file on disk.
Use cases:
- Create logs
- Export data
- Save reports
- Archive information
Configuration:
- File path (e.g.,
logs/workflow_log.txt) - Content to save (static or from DataBus)
- Storage disk (
local,s3, etc.) - Append or overwrite
Example workflow:
Trigger: API request processed
↓
Task: SaveFile - Log request to "api_requests.log"
SaveModel
Save (persist) an Eloquent model to database.
Use cases:
- Save modified model
- Commit changes after ChangeModel task
- Record workflow results
Configuration:
- Model to save (usually from workflow context)
Example workflow:
Trigger: User signs up
↓
Task: ChangeModel - Set verification token
↓
Task: SendMail - Send verification email
↓
Task: SaveModel - Save user with token
SendMail
Send email with dynamic content and attachments.
Use cases:
- Send notifications
- Send confirmations
- Send reports
- Bulk email campaigns
Configuration:
- Recipient email
- Subject line
- Body content (from HtmlInput or static)
- Attachments (PDF from DomPDF, etc.)
Example workflow:
Trigger: Order placed
↓
Task: HtmlInput - Create invoice HTML
↓
Task: DomPDF - Generate invoice PDF
↓
Task: SendMail - Send invoice to customer
(attach: PDF from previous task)
SendSlackMessage
Send notifications to Slack channel.
Uses:
- Alert team of important events
- Post workflow results
- Send reminders
- Daily reports
Setting Up SendSlackMessage
SendSlackMessage requires three setup steps:
Step 1: Install Laravel Slack Notifications
Follow Laravel Slack Documentation:
composer require laravel/slack-notification-channel
Step 2: Create Slack Webhook
- Go to Slack API
- Create incoming webhook
- Select channel to receive messages
- Copy webhook URL
Step 3: Configure Laravel Mail Platform
Add webhook URL to .env:
WORKFLOW_SLACK_CHANNEL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL
Example workflow:
Trigger: Campaign sent
↓
Task: SendSlackMessage
Message: "Campaign to {{$model->name}} sent successfully"
Message formatting:
Use Blade syntax:
New order from {{ $model->customer_name }}
Order ID: {{ $model->id }}
Amount: ${{ $model->total }}
Status: {{ $databus['order_status'] }}
DataBus: Passing Data Between Tasks
The DataBus is a communication system that passes information from one task to the next. This keeps tasks independent and reusable.
How it works:
Task 1 outputs data → DataBus stores it
↓
Task 2 reads from DataBus, does work, outputs data → DataBus stores it
↓
Task 3 reads from DataBus, continues
Data Resources
Use these resource types to access data in your tasks:
ValueResource
Simple static values you enter directly.
Use:
Type: "hello world"
Email: "user@example.com"
Quantity: "5"
Example: Send email to hardcoded address
ConfigResource
Access Laravel configuration values.
Use:
app.name → Your app name from config
app.url → Your app URL
mail.from.address → Sender email from config
Example: Use app name in email greeting
ModelResource
Access properties from the model passed to the workflow.
Use:
$model->email → User's email
$model->name → User's name
$model->created_at → When created
Example: Send email to user who triggered workflow
DataBusResource
Access data output by previous tasks.
Use:
previous_task_output
generated_pdf_path
calculated_total
transformed_data
Example: Use PDF generated by DomPDF task as email attachment
DataBus Flow Example
Trigger: Order placed (passes Order model)
↓
Task 1: LoadModel
Resource: ModelResource ($model->user_id)
Outputs: user object → DataBus['customer']
↓
Task 2: HtmlInput
Resources: DataBusResource ($databus['customer']->name)
Outputs: invoice HTML → DataBus['invoice_html']
↓
Task 3: DomPDF
Input: DataBusResource ($databus['invoice_html'])
Outputs: PDF path → DataBus['invoice_pdf']
↓
Task 4: SendMail
Recipient: DataBusResource ($databus['customer']->email)
Body: DataBusResource ($databus['invoice_html'])
Attachment: DataBusResource ($databus['invoice_pdf'])
↓
Complete
Building Your First Workflow
Scenario: Welcome Email on New User Signup
Goal: When user signs up, send welcome email and notify team
Steps:
- Create workflow → Go to
/workflows→ New Workflow - Add trigger → Observer Trigger on User model "created" event
- Add task 1 → HtmlInput: Create welcome email
- Content: "Welcome {{$model->first_name}}!"
- Add task 2 → SendMail
- To: ModelResource ($model->email)
- Subject: "Welcome!"
- Body: From DataBus (task 1 output)
- Add task 3 → SendSlackMessage
- Message: "New user signed up: {{$model->email}}"
- Save workflow
Test: Create new user in database, workflow executes automatically
Scenario: Generate and Email Invoice
Goal: Generate PDF invoice and email to customer
Steps:
- Create workflow → Go to
/workflows→ New Workflow - Add trigger → Button Trigger (render in Order details page)
- Add task 1 → HtmlInput: Create invoice HTML
- Use: ModelResource to access order details
- Use: Blade to format amounts
- Add task 2 → DomPDF: Generate PDF
- Input: From task 1
- Output: PDF path to DataBus
- Add task 3 → SendMail
- To: ModelResource ($model->customer_email)
- Attach: DataBusResource (PDF path)
- Save workflow
Test: Click button on order, email sends with PDF
Advanced Workflows
Conditional Branching
Create workflows with different paths based on conditions:
Trigger: User updated
↓
Task 1: Check if status changed
- If status = "premium": execute Task 2
- If status = "suspended": execute Task 3
- Otherwise: end workflow
↓
Task 2: Send upgrade welcome email
↓
Task 3: Send reactivation email
Nested Workflows
Trigger other workflows from within a workflow:
Workflow A (Main):
Task 1: Process order
Task 2: Execute Workflow B (email customer)
Task 3: Execute Workflow C (notify warehouse)
Workflow B:
Generate invoice
Send email
Workflow C:
Create shipment label
Notify warehouse
Error Handling
Workflows continue even if individual tasks fail:
Task 1: SendMail (fails - bad email)
→ Workflow continues
Task 2: SaveFile (logs error)
Task 3: SendSlackMessage (alerts admin about error)
Best Practices
Naming Workflows
Use clear, descriptive names:
✅ Good names:
New User Welcome EmailOrder Confirmation and InvoiceDaily Report Generation
❌ Poor names:
workflow1testmisc
Task Organization
Keep workflows focused on one purpose:
✅ Good:
- One workflow = one business process
- Reuse tasks across workflows
❌ Bad:
- One huge workflow doing everything
- Redundant tasks in multiple workflows
Data Flow
Use DataBus strategically:
✅ Good:
- Pass data through DataBus between tasks
- Keep tasks independent
- Reuse tasks with different data sources
❌ Bad:
- Hardcode data in tasks
- Make tasks dependent on each other
- Duplicate task logic
Error Handling
Add logging and alerts:
Trigger: Something important
↓
Task 1: Try main action (may fail)
↓
Task 2: SaveFile - Log result
↓
Task 3: SendSlackMessage - Alert team
Troubleshooting
"Workflow doesn't execute"
Check:
- [ ] Trigger is properly configured
- [ ] Trigger event actually occurs (check logs)
- [ ] Tasks have required data
- [ ] All DataBus references exist
Debug:
- Enable logging in workflow tasks
- Add SaveFile tasks to log progress
- Check application logs
"Task fails silently"
Check:
- [ ] Task configuration is complete
- [ ] Required fields filled in
- [ ] DataBus variables exist
- [ ] Models/files accessible
Debug:
- Add SendSlackMessage after each task
- Use SaveFile to log intermediate data
- Check logs for error messages
"Data not available in DataBus"
Problem: Task tries to access DataBus value that doesn't exist
Causes:
- Previous task didn't output to DataBus
- Wrong variable name
- Task skipped due to condition
Fix:
- Verify previous task outputs data
- Check variable names exactly
- Add conditional logic if task might not run
"Email not sending"
Check:
- [ ] SendMail task configured
- [ ] Recipient email is valid
- [ ] Mail service connected
- [ ] Subject and body filled in
Debug:
- Test mail service directly
- Check Laravel logs
- Verify model has email field
API Reference
Creating Workflows Programmatically
use the42coders\Workflows\Models\Workflow;
$workflow = Workflow::create([
'name' => 'Welcome Email',
'description' => 'Send welcome email to new users',
]);
// Add trigger
$workflow->triggers()->create([
'type' => 'observer',
'model' => 'App\\Models\\User',
'event' => 'created',
]);
// Add tasks
$workflow->tasks()->create([
'name' => 'Send Email',
'type' => 'send_mail',
'order' => 1,
'config' => [
'recipient' => 'user@example.com',
'subject' => 'Welcome!',
],
]);
Executing Workflows
use the42coders\Workflows\Facades\Workflow;
// Execute workflow by ID
Workflow::execute(workflow_id, $model);
// Execute by name
Workflow::executeByName('workflow-name', $model);
// Execute specific tasks
Workflow::executeTasks($taskIds, $model);
Testing
Run tests:
composer test
Related Documentation
- Tasks — Detailed task reference
- Triggers — Trigger configuration
- DataBus — Data passing guide
- Examples — Common workflow patterns
Quick Reference
Basic Workflow:
Trigger (when) → Task 1 → Task 2 → Task 3 → Complete
↓ ↓ ↓
DataBus passes data between tasks
Common Tasks:
- HtmlInput → Create content
- DomPDF → Generate PDF
- SendMail → Send email
- SendSlackMessage → Notify team
- SaveFile → Log data
Data Resources:
- ValueResource → Static values
- ModelResource → Model data (
$model->property) - DataBusResource → Previous task output
- ConfigResource → Config values
Access Workflows:
- Web interface:
/workflows - API: See API Reference section
- Triggers: Observer (auto), Button (click)
Next Steps:
- Create first workflow
- Add multiple tasks
- Test and refine
- Share with team