Build the payload contract first
Legacy workflows accepted payload variables without a declared workflow schema. The new Dashboard uses the payload schema to make fields available to templates, conditions, grouping keys, and dynamic action-step settings. For each legacy workflow:- Capture successful production payloads from every trigger producer.
- List each field referenced by content, conditions, variants, delay or digest configuration, and provider overrides.
- Resolve inconsistent types. For example, do not define
orderIdas an integer if one producer sends it as a string. - Add nested objects and array item types.
- Mark a field required only if every valid trigger must provide it.
- Add defaults only when a missing value has an unambiguous meaning.
- Import a representative JSON object or create the properties in Manage workflow schema.
- Preview the workflow with minimum and maximum valid values.
- Enable schema enforcement after all trigger producers conform.
orderId as a string, customerTier as an enum or string, and items as an array of objects with typed name and quantity properties.
See Configure workflow for supported schema types and constraints.
Convert Handlebars to LiquidJS
Do not perform a global brace replacement. Handlebars helpers and Liquid tags have different parsing, scoping, truthiness, and formatting behavior.Variable paths
Conditions and loops
groupBy, implicit with context, or different date-format tokens.
Use the variable picker where possible. It inserts the current namespace and reduces errors caused by renamed fields. See Personalize content for the supported namespaces, conditions, loops, and filters.
Choose the email editing model
The code editor accepts Liquid variables, tags, and filters directly in HTML. The block editor stores structured nodes and inserts variables through its variable controls. Some block attributes use selected variable paths rather than handwritten{{ ... }} expressions.
For an exact legacy HTML migration:
- Start with the code editor.
- Convert Handlebars to LiquidJS in the source.
- Compare the rendered HTML and text output with the legacy message.
Replace variants with conditional flow
Legacy variants selected alternate content or configuration inside one step. The new editor models each alternative as an independent step with its own condition. Assume a legacy Email step has:- A Gold variant when
customerTierequalsgold - A Silver variant when
customerTierequalssilver - A root variant for every other value
All three steps are evaluated in sequence. Their conditions must be mutually exclusive. Otherwise, one trigger can execute more than one replacement step.
Apply the same pattern to variants of action steps:
- A legacy Delay variant becomes a separate conditional Delay step.
- A legacy Digest variant becomes a separate conditional Digest path.
- All downstream steps must have conditions that prevent execution after the wrong action path.
Convert step conditions
Recreate conditions from their intent, not only their labels.Payload and subscriber conditions
Map legacy fields to current namespaced fields:- Payload:
payload.<declared-property> - Standard subscriber fields:
subscriber.firstName,subscriber.email,subscriber.locale, or another supported field - Custom subscriber fields:
subscriber.data.<property>
AND and OR groups. Match the legacy grouping exactly and test boundary values for numeric, range, empty, null, in, and string operators.
Webhook response conditions
Replace a legacy webhook condition with:- An HTTP step before the dependent steps
- A response schema that declares every consumed field
- A condition that reads
steps.<http-step-id>.<response-field> - An explicit failure policy
{ "eligible": true }, a later Email step can use a condition on steps.check-eligibility.eligible.
Test timeout, non-2xx, malformed response, and false-result paths. A successful preview request does not create Activity feed evidence, so also run the complete workflow.
Previous message state
For fallback delivery:- Send the in-app step.
- Add enough Delay time for the subscriber to interact.
- Add a condition to the later step using the exact ID of the in-app step.
- Test read, unread, seen, and unseen outcomes independently.
Migrate email layouts
Legacy layouts and current layouts have different content contracts.
Use this conversion order:
- Copy the legacy HTML to a backup file.
- Replace
{{{body}}}with{{content}}. - Find every payload or tenant-dependent expression.
- Move workflow payload-dependent markup into the Email step.
- Replace stable global values with static layout content.
- Replace environment-specific values with
env.*. - Replace reusable tenant or brand values with
context.*. - Convert remaining Handlebars expressions to LiquidJS.
- Enable and migrate layout translations if the layout contains localized text.
- Select the layout explicitly on every migrated Email step.
- Preview long content, empty optional fields, mobile width, links, images, and dark-mode-sensitive styles.
layoutIdentifier dynamically, decide whether to use separate conditional Email steps or separate workflows. Do not leave the legacy override in application code without proving that the current delivery path supports the intended result.
See Email layouts.
Migrate translations
Legacy translation groups were independent resources referenced with thei18n Handlebars helper. Current translations belong to a workflow or layout and use the t namespace.
Example:
- Identify which workflows and layouts use the group.
- Copy the required keys into each owning workflow or layout.
- Remove the legacy group prefix from expressions when it is no longer part of the current JSON key path.
- Rewrite helper parameters and nested variables using LiquidJS.
- Rename files to
language_REGION.json. - Set the default and target locales.
- Import the default locale, then each target locale.
- Preview with subscribers that have each locale.
- Test fallback behavior for missing keys and unsupported locales.
- Publish translation resources with their workflows and layouts.
Replace tenants with contexts
Map each legacy tenant use separately:
Keep context data reusable and stable. Keep event-specific data in the payload. The current platform limits the number of contexts per trigger, so check Limits before mapping one legacy tenant into several context objects.
Provider routing and workflow step conditions are separate controls. Recreate a tenant-based integration condition in Integrations, then recreate content or orchestration conditions on the workflow steps. Test the matching integration, the fallback integration, and a trigger with no tenant context.
See Contexts.
Review action steps
Delay
Both dashboards support a fixed delay and a payload-driven delay. Map the legacy type before changing anything else.
The Dynamic delay also accepts a
{ "amount": 30, "unit": "minutes" } duration object. Treat that as a follow-up change, because the legacy step only accepted an ISO date.
The new Scheduled delay is calendar-based and uses a recurring minute, hour, day, week, or month. It is not the replacement for the legacy Scheduled delay. Use it only when you intend to change behavior.
Enabling extension to the subscriber’s schedule changes delivery time. Leave it disabled if exact legacy timing is required during migration.
A dynamic delay fails the workflow when the variable is missing, is not a valid ISO date or duration object, or resolves to a past time. Test each of those cases before cutover.
Digest
Both dashboards support a regular window, a repeat-event start, and a scheduled window.
Also confirm subscriber grouping, the optional payload aggregation key, extension to the subscriber schedule, and every downstream reference to digest events and counts.
The aggregation key must exist in the payload schema. Test multiple events for one subscriber, the same subscriber with different aggregation keys, and different subscribers with the same key.
Throttle
Throttle is new relative to the primary legacy action-step model. Do not add it merely because it is available. If you adopt it, define:- Fixed or dynamic window
- Execution threshold
- Subscriber and optional payload grouping
- Behavior for critical workflows
HTTP
Use HTTP to replace legacy webhook-condition enrichment or to fetch application state. Define response schemas, signatures, timeout behavior, and whether workflow execution continues after failure. See Add and configure steps for current step behavior and plan-dependent duration limits.Validate channel parity
Use the current channel template editor reference to validate each copied channel. Keep this page focused on conversion rules rather than duplicating channel configuration. For every channel in a legacy workflow, record the rendered content, provider, credentials, overrides, condition result, and delivery result. For In-app steps, also confirm Data, actions, tags, avatar, and sanitization. Complete the client-side field and feed migration in the Inbox migration guide.Workflow validation record
Create one record per workflow with:
After all workflow records pass, continue with application migration and production cutover.