Email Module

Send transactional and marketing emails via SMTP or Resend API with variable interpolation, HTML templates, and per-organization rate limiting.

Module SMTP Resend API Rate Limited

What It Does

The Email Module enables workflows to send emails as action nodes. It supports two providers:

Use cases include:

How to Activate

  1. Navigate to Settings → Modules in the sidebar
  2. Find the Email module in the available modules list
  3. Click Install to register the module in your organization
  4. Click Activate to enable it
  5. Click Configure to enter your SMTP or Resend credentials

Permission Required

You need the modules.configure permission to install and configure modules. By default, only owner and admin roles have this permission.

Configuration

SMTP Provider

FieldTypeRequiredDescription
providerstringYesSet to "smtp"
smtp_hoststringYesSMTP server hostname (e.g., smtp.gmail.com)
smtp_portintegerYesPort number: 587 (TLS), 465 (SSL), or 25 (unencrypted)
smtp_usernamestringYesAuthentication username
smtp_passwordstringYesAuthentication password (stored encrypted)
smtp_encryptionstringNotls (default), ssl, or null
from_addressstringYesDefault sender email address
from_namestringNoDefault sender display name

Resend Provider

FieldTypeRequiredDescription
providerstringYesSet to "resend"
resend_api_keystringYesResend API key (starts with re_)
from_addressstringYesVerified sender address in Resend
from_namestringNoDefault sender display name
JSON — Example SMTP Configuration
{
  "provider": "smtp",
  "smtp_host": "smtp.gmail.com",
  "smtp_port": 587,
  "smtp_username": "your-app@gmail.com",
  "smtp_password": "app-specific-password",
  "smtp_encryption": "tls",
  "from_address": "noreply@yourcompany.com",
  "from_name": "Your Company"
}

Node Type: action.email.send

This node sends an email when reached during workflow execution.

Config FieldTypeRequiredDescription
tostringYesRecipient email address (supports variables)
subjectstringYesEmail subject line (supports variables)
bodystringYesEmail body — HTML or plain text (supports variables)
fromstringNoOverride default sender address for this email
JSON — Node Configuration Example
{
  "type": "action.email.send",
  "config": {
    "to": "{{trigger.email}}",
    "subject": "Welcome to our platform, {{trigger.name}}!",
    "body": "<h1>Hello {{trigger.name}}</h1><p>Thanks for signing up. Your account is ready.</p>",
    "from": "welcome@yourcompany.com"
  }
}

Variable Interpolation

All text fields in the email node support double-brace variable syntax. Variables are resolved at execution time from the trigger payload and outputs of previous nodes.

VariableSourceExample Value
{{trigger.email}}Event trigger payloaduser@example.com
{{trigger.name}}Event trigger payloadJohn Doe
{{trigger.data.order_id}}Nested trigger payloadORD-12345
{{step_id.output.field}}Output from a previous node(varies)

Unresolved Variables

If a variable cannot be resolved (e.g., the field does not exist in the trigger payload), it will be replaced with an empty string. The email will still be sent — check your trigger payload structure to avoid missing data.

Rate Limiting

To prevent abuse and protect your SMTP reputation, the Email Module enforces per-organization, per-hour rate limits.

Limit TypeDefaultConfigurable
Emails per hour per organization100Yes (via module config)
Emails per execution10Yes

When the rate limit is exceeded, the node execution fails with error code rate_limit_exceeded and the workflow execution step is marked as failed. The email is not queued for retry — it must be re-triggered.

CRLF Injection Protection

The Email Module sanitizes all header fields (to, from, subject) by stripping \r and \n characters before sending. This prevents CRLF injection attacks that could add arbitrary headers (like BCC) to outgoing emails.

PHP — Header Sanitization
// Strips CRLF characters from email headers
$to = str_replace(["\r", "\n"], '', $config['to']);
$subject = str_replace(["\r", "\n"], '', $config['subject']);
$from = str_replace(["\r", "\n"], '', $config['from'] ?? $defaultFrom);

Logging

Every email sent (or attempted) is logged in the email_send_logs table for auditing and debugging:

ColumnDescription
idPrimary key
organization_idOwning organization
execution_idWorkflow execution that triggered this email
toRecipient address
subjectEmail subject
statussent, failed, or rate_limited
providersmtp or resend
error_messageError details if failed
sent_atTimestamp

Common Issues

ProblemCauseSolution
Connection timed out SMTP server unreachable or port blocked Verify firewall allows outbound on port 587/465. Try telnet smtp.host 587 from your server.
Authentication failed Wrong SMTP credentials For Gmail, use an App Password (not your account password). Verify username is the full email.
Rate limit exceeded Too many emails sent in the current hour Wait for the rate limit window to reset, or increase the limit in module configuration.
Emails going to spam Missing SPF/DKIM/DMARC records Configure DNS records for your sending domain. Use a dedicated IP or service like Resend.
Connection timeout (Resend) Network issue or API outage Check Resend status page. Verify server has outbound HTTPS access.