Lyfe for Craft CMS

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

SettingDefaultWhat it does
Enabled (enabled)onThe master switch. Off means no workflow is triggered and lyfe/run/due does nothing.
Global dry run (globalDryRun)offEvery workflow runs as a dry run: steps execute and are recorded, messages are logged as "simulated", nothing is sent.
Steps per pass (batchSize)50How many due runs one lyfe/run/due advances. 1 to 1000.
Retry a failed step (maxAttempts)3How many attempts a failing step gets before the run is marked failed. 1 to 20.
Wait before retrying (retryDelayMinutes)15Minutes between attempts.
Abandon steps overdue by more than (staleAfterHours)72A 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

SettingDefaultWhat it does
Also process during web requests (runOnRequest)offFor 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)300Seconds between those queue jobs. Minimum 30.

See Installation for the cron entry.

Contacts

SettingDefaultWhat it does
Create contacts automatically (autoCreateContacts)onWhen 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)onTurn this off if your customers must opt in before you market to them.
New contacts are subscribed to SMS (defaultSubscribedSms)offLeft off deliberately. Explicit opt-in is the only defensible default for a phone number.
Keep history for (historyDays)365Days 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.

Email

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.

SettingDefaultWhat it does
From name (fromName)Craft'sLeave empty to use the name in Craft's email settings.
From address (fromEmail)Craft'sLeave empty to use Craft's address.
Reply-to address (replyToEmail)noneDefault reply-to. A step's own Reply to overrides it.
Email layout template (emailLayoutTemplate)noneA 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, order and workflow
  • unsubscribeUrl

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

SettingDefaultWhat it does
Unsubscribe path (unsubscribePath)lyfe/unsubscribeThe front-end URL that {{ unsubscribeUrl }} points at. Letters, numbers, -, _ and / only.
Unsubscribe template (unsubscribeTemplate)built-in pageA 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)

SettingDefaultWhat it does
Track carts (trackCarts)onRecords when each cart was last touched, so abandonment can be detected.
Consider a cart abandoned after (cartAbandonedAfterMinutes)60Minutes of inactivity. Minimum 5.
Ignore carts with fewer than (cartMinLineItems)1Line items. An empty cart is never treated as abandoned.
Stop tracking a cart after (cartGiveUpDays)30Days 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

SettingWhat 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.

SettingWhat 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

PermissionWhat it allows
View workflowsSee the workflow list, the library and each workflow's settings.
↳ Create, edit and delete workflowsSave, enable, pause, reorder, duplicate and delete workflows, and add presets. Subject to the rules in Who can write what.
View activity, messages and the logThe Activity, Messages, Simulator and Log screens, and the Lyfe panel on Commerce's order edit screen.
↳ Cancel and re-run activityCancel a run, Run now on a waiting run, Process due steps now, and clear the log.
View contactsThe contact list and each contact's history.
↳ Edit contacts, tags and subscriptionsAdd, edit and delete contacts, change their tags and consent, refresh their figures, and start a workflow for a contact by hand.
View tracked cartsThe Carts screen (Pro).
↳ Run the abandoned-cart sweepThe 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. Use order.totalPriceAsCurrency instead of the commerceCurrency filter. 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 $VARIABLE in 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.