Workflows

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:

  1. Log in to your application
  2. Navigate to /workflows
  3. You'll see the workflow builder interface
  4. 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 created
  • updated — Model was updated
  • deleted — Model was deleted
  • restored — Deleted model was restored
  • saved — 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

  1. Go to Slack API
  2. Create incoming webhook
  3. Select channel to receive messages
  4. 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:

  1. Create workflow → Go to /workflows → New Workflow
  2. Add trigger → Observer Trigger on User model "created" event
  3. Add task 1 → HtmlInput: Create welcome email
  • Content: "Welcome {{$model->first_name}}!"
  1. Add task 2 → SendMail
  • To: ModelResource ($model->email)
  • Subject: "Welcome!"
  • Body: From DataBus (task 1 output)
  1. Add task 3 → SendSlackMessage
  • Message: "New user signed up: {{$model->email}}"
  1. 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:

  1. Create workflow → Go to /workflows → New Workflow
  2. Add trigger → Button Trigger (render in Order details page)
  3. Add task 1 → HtmlInput: Create invoice HTML
  • Use: ModelResource to access order details
  • Use: Blade to format amounts
  1. Add task 2 → DomPDF: Generate PDF
  • Input: From task 1
  • Output: PDF path to DataBus
  1. Add task 3 → SendMail
  • To: ModelResource ($model->customer_email)
  • Attach: DataBusResource (PDF path)
  1. 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 Email
  • Order Confirmation and Invoice
  • Daily Report Generation

❌ Poor names:

  • workflow1
  • test
  • misc

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


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