Configuration
Lyfe's settings live under Settings → Plugins → Lyfe, or Lyfe → Settings in the control
panel nav. Only admins can open them, and only where allowAdminChanges is on, because plugin
settings are stored in project config and deploy with it.
None of the settings is required. A fresh install saves with every field empty.
Engine
| Setting | Default | What it does |
|---|---|---|
Enabled (enabled) | on | The master switch. Off means no workflow is triggered and lyfe/run/due does nothing. |
Global dry run (globalDryRun) | off | Every workflow runs as a dry run: steps execute and are recorded, messages are logged as "simulated", nothing is sent. |
Steps per pass (batchSize) | 50 | How many due runs one lyfe/run/due advances. 1 to 1000. |
Retry a failed step (maxAttempts) | 3 | How many attempts a failing step gets before the run is marked failed. 1 to 20. |
Wait before retrying (retryDelayMinutes) | 15 | Minutes between attempts. |
Abandon steps overdue by more than (staleAfterHours) | 72 | A step that came due longer ago than this is abandoned and recorded, not sent late. This is what stops a cron or queue outage from delivering a week of "your order shipped" at once. |
A run is created with the dry-run flag if either Global dry run or the workflow's own Dry run is on at the moment it starts. Turning global dry run off later does not make those runs start sending.
Scheduling
| Setting | Default | What it does |
|---|---|---|
Also process during web requests (runOnRequest) | off | For installs with no cron. After a web request, pushes a queue job that processes due runs, at most once per interval. Never runs inline. |
Interval between web ticks (tickIntervalSeconds) | 300 | Seconds between those queue jobs. Minimum 30. |
See Installation for the cron entry.
Contacts
| Setting | Default | What it does |
|---|---|---|
Create contacts automatically (autoCreateContacts) | on | When an order starts a workflow, or (Pro) a cart is tracked, creates a contact for its email address if there isn't one. Off means orders and carts only use contacts that already exist. Leave it on: see the note below. |
New contacts are subscribed to marketing email (defaultSubscribedEmail) | on | Turn this off if your customers must opt in before you market to them. |
New contacts are subscribed to SMS (defaultSubscribedSms) | off | Left off deliberately. Explicit opt-in is the only defensible default for a phone number. |
Keep history for (historyDays) | 365 | Days of finished runs, messages and log entries that lyfe/run/prune keeps. 0 keeps everything. Pending runs are never pruned. |
The defaults apply when a contact is created. Changing them later does not touch existing contacts.
Consent is stored on the contact. A run with no contact, which is what an order from a new customer gives you with Create contacts automatically off, has no consent to check and no unsubscribe link, so its emails are sent without either. Keep the setting on if you send marketing email from order triggers.
Contacts are created as Lyfe handles things, not for every order in the store: an order that no
enabled workflow is listening for does not create one. To build contacts for your whole order
history, run php craft lyfe/contacts/backfill.
The Backfill contacts button builds contacts from your 500 most recent completed orders. For
your whole order history, use php craft lyfe/contacts/backfill.
Lyfe sends through Craft's own mailer, so whatever transport Craft is configured with (SMTP, Postmark, Mailgun, Amazon SES or another adapter) is what Lyfe uses.
| Setting | Default | What it does |
|---|---|---|
From name (fromName) | Craft's | Leave empty to use the name in Craft's email settings. |
From address (fromEmail) | Craft's | Leave empty to use Craft's address. |
Reply-to address (replyToEmail) | none | Default reply-to. A step's own Reply to overrides it. |
Email layout template (emailLayoutTemplate) | none | A site template wrapped around every email body. Leave empty to send bodies as they are. |
The layout template is an ordinary template in your site's templates/ folder, for example
_emails/lyfe-layout. It receives:
body, the rendered message, already marked safe. Output it with{{ body }}.contact,orderandworkflowunsubscribeUrl
If the template does not exist or fails to render, Lyfe logs a warning and sends the body unwrapped rather than failing the step.
Test email sends a message to the address you type (or to you) through the configured from-address and layout.
Unsubscribing
| Setting | Default | What it does |
|---|---|---|
Unsubscribe path (unsubscribePath) | lyfe/unsubscribe | The front-end URL that {{ unsubscribeUrl }} points at. Letters, numbers, -, _ and / only. |
Unsubscribe template (unsubscribeTemplate) | built-in page | A site template to render instead of Lyfe's own plain page. |
A custom unsubscribe template receives contact, found (whether the link matched a contact),
done (whether the form was just submitted), token and channels. It has to post back to the
same action with the token, so include:
<form method="post">
{{ csrfInput() }}
{{ actionInput('lyfe/subscriptions/unsubscribe') }}
<input type="hidden" name="lyfeToken" value="{{ token }}">
{# Checkboxes named "email" and "sms" with value 1 keep a channel on. #}
<input type="hidden" name="email" value="0">
<input type="checkbox" name="email" value="1" {{ contact.subscribedEmail ? 'checked' }}>
<input type="hidden" name="sms" value="0">
<input type="checkbox" name="sms" value="1" {{ contact.subscribedSms ? 'checked' }}>
<button>Save</button>
</form>
A post with neither field unsubscribes the contact from both channels. Opening the link alone changes nothing; the customer has to submit the form.
Abandoned carts (Pro)
| Setting | Default | What it does |
|---|---|---|
Track carts (trackCarts) | on | Records when each cart was last touched, so abandonment can be detected. |
Consider a cart abandoned after (cartAbandonedAfterMinutes) | 60 | Minutes of inactivity. Minimum 5. |
Ignore carts with fewer than (cartMinLineItems) | 1 | Line items. An empty cart is never treated as abandoned. |
Stop tracking a cart after (cartGiveUpDays) | 30 | Days of inactivity after which a cart is no longer tracked. 0 keeps them forever. |
SMS (Pro)
Transport (smsTransport) chooses how texts are sent: twilio or http.
Twilio
| Setting | What it is |
|---|---|
Account SID (twilioAccountSid) | From your Twilio console. |
Auth token (twilioAuthToken) | A credential. Use an environment variable. |
From number (twilioFromNumber) | In E.164 form, for example +15551234567. |
Lyfe calls Twilio's Messages API directly. When Twilio rejects a message, its error code and message are recorded on the message, which tells you whether the number was unreachable or the credentials were wrong.
Generic HTTP gateway
For Vonage, MessageBird, Sinch, a self-hosted gateway or anything else that accepts an HTTP request.
| Setting | What it is |
|---|---|
Gateway URL (smsWebhookUrl) | {{to}} and {{body}} in the URL are replaced with the URL-encoded number and message. |
Method (smsWebhookMethod) | POST or GET. A POST also sends {"to": "...", "body": "..."} as JSON. |
Authorization header (smsWebhookAuthHeader) | Sent as the Authorization header. |
Any 2xx response counts as sent. Anything else is recorded as a failure with the first 300 characters of the response.
To keep the gateway credential out of project config, put the whole header value in one
environment variable (for example SMS_AUTH_HEADER="Bearer abc123") and enter $SMS_AUTH_HEADER
in the field. A value like Bearer $SMS_TOKEN is sent literally, without the variable being
resolved.
Phone numbers
Default country code (smsDefaultCountryCode, default +1) is put in front of numbers stored
in local form. A number that already starts with + or 00 is left alone apart from removing
spaces and punctuation. Lyfe never guesses beyond that, because a wrong guess texts a stranger.
Test SMS sends a test message through whatever is saved, not what is on screen, so save your credentials first.
Where contacts get their phone numbers from is covered in Usage.
Environment variables
These fields accept $VARIABLE and suggest environment variables as you type:
- From name, From address, Reply-to address
- Account SID, Auth token, From number
- Gateway URL, Authorization header
Use them for credentials and anything that differs between environments. A $VARIABLE that is not
defined on the current environment is not treated as a validation error, because it may be
defined where the site is deployed.
As with any Craft plugin, you can also set any of the settings above in config/lyfe.php, which
overrides what is saved in the control panel:
<?php
return [
'globalDryRun' => getenv('CRAFT_ENVIRONMENT') !== 'production',
'cartAbandonedAfterMinutes' => 90,
];
Environment variables in workflow steps follow different rules; see Who can write what.
Permissions
| Permission | What it allows |
|---|---|
| View workflows | See the workflow list, the library and each workflow's settings. |
| ↳ Create, edit and delete workflows | Save, enable, pause, reorder, duplicate and delete workflows, and add presets. Subject to the rules in Who can write what. |
| View activity, messages and the log | The Activity, Messages, Simulator and Log screens, and the Lyfe panel on Commerce's order edit screen. |
| ↳ Cancel and re-run activity | Cancel a run, Run now on a waiting run, Process due steps now, and clear the log. |
| View contacts | The contact list and each contact's history. |
| ↳ Edit contacts, tags and subscriptions | Add, edit and delete contacts, change their tags and consent, refresh their figures, and start a workflow for a contact by hand. |
| View tracked carts | The Carts screen (Pro). |
| ↳ Run the abandoned-cart sweep | The Check for abandoned carts now button. |
Lyfe's settings, Test email, Test SMS and Backfill contacts are admin-only regardless of permissions.
Starting a workflow for a contact needs Edit contacts, tags and subscriptions, not the workflow permission, because it acts on a contact rather than changing the workflow.
Who can write what
Twig in a workflow step can reach anything Twig can reach, including Craft's own configuration, and an email or a webhook is a way to send whatever it prints to an outside address. So Lyfe remembers, for each step, whether it is trusted: written by an admin, or approved by one.
A trusted step renders exactly as written, against the real order, contact and user
objects. Any method, any filter and craft.* all work: {{ order.getShortNumber() }},
{{ order.totalPrice|commerceCurrency(order.currency) }}.
An untrusted step (one a marketer with Create, edit and delete workflows added or
changed, and no admin has approved) depends on Craft's Twig sandbox, which is available from Craft 5.9 with the enableTwigSandbox general
config setting turned on:
- With the sandbox on, the step renders inside it, against the same variable names as plain
data rather than objects.
{{ order.reference }},{{ order.totalPriceAsCurrency }},{{ contact.firstName }},{{ cartRestoreUrl }}and the rest work; methods,craft.*and filters the sandbox does not allow do not. Useorder.totalPriceAsCurrencyinstead of thecommerceCurrencyfilter. The full list of values is in Usage. - With the sandbox off (or on Craft before 5.9), the step can contain plain text but not Twig.
Any
{counts as Twig here, because Craft's object-template shorthand ({firstName}) runs too. Saving such a step is refused with a message explaining why.
Two things need a trusted step whatever the sandbox setting:
- Webhook steps. The server makes the request, so it can reach anything the server can, including internal services. Only an admin can add a webhook step or change one.
- Environment variables in steps. A
$VARIABLEin a webhook header resolves only in a trusted step.
How trust is decided
Trust follows authorship, step by step, each time a workflow is saved:
- A step someone adds or changes (its action or any of its settings) runs under that person's rules: trusted if they are an admin, untrusted otherwise.
- A step left exactly as it was keeps whatever trust it had. Reordering steps, changing a delay, pausing or enabling a step or the workflow, and editing the conditions do not change any step's trust. A marketer can work on an admin's workflow without breaking it.
- An admin saving the workflow does not trust anybody else's step. Untrusted steps carry an orange Written by someone who isn't an admin badge in the editor. An admin who has read the step trusts it by ticking Approve: run with full Twig and $ENV values on that step and saving.
Steps that existed before step trust was introduced start untrusted, and are approved the same way. Presets install trusted, whoever adds them, because their copy ships with the plugin.
If a step stops rendering after a non-admin edits it, see Troubleshooting.