The Bloomreach Engagement implementation playbook: Part 3
Part 3: Scenarios, nodes, and Jinja2 personalization
Scenarios are where Bloomreach earns its keep. A scenario is a visual workflow, triggers, conditions, actions, delays, that turns raw events into coordinated customer experiences. A mature Bloomreach instance will run dozens of scenarios concurrently, from simple transactional confirmations to multi-week lifecycle journeys with AI-selected content.
This part covers scenario design patterns, node-level best practices, and a working primer on the Jinja2 templating language that powers personalization inside every message.
Scenario design patterns
Before building a specific scenario, it helps to have patterns in mind. The four that cover 80% of use cases:
Transactional. Triggered by a purchase, subscription change, or other commerce event. Single message, immediate, no frequency cap. Purpose: confirm the action. Example: order confirmation email.
Trigger-based lifecycle. Triggered by a behavioral event (abandoned cart, browse abandonment, price drop watch). Short path, usually 1–3 messages over a few days. Purpose: recover the moment. Example: abandoned cart series.
Audience-based lifecycle. Triggered by a customer entering a segment (became a VIP, hasn’t purchased in 90 days). The longer path often spans weeks. Purpose: move the customer between lifecycle stages. Example: win-back campaign.
Event-batched. Runs on a schedule and evaluates a segment each time. Used for weekly digests, periodic newsletters, and any communication where the trigger is time rather than behavior. Example: weekly product recommendations.
Each pattern has different node architecture. Transactional scenarios are short and should never branch on consent (a transactional message is exempt from marketing consent in most jurisdictions). Trigger-based lifecycle scenarios usually start with a wait node to handle the “user just completed the thing” race condition. Audience-based scenarios lean heavily on condition nodes to keep audiences fresh across long durations.
Node order matters: condition nodes early
The single most common scenario mistake is placing condition nodes too late in the flow. Every node in a Bloomreach scenario consumes processing capacity, and scenarios that evaluate millions of profiles against conditions at the end of a flow create unnecessary load, and, more subtly, produce delivery delays that compound on high-traffic days.
The rule: the earlier a filter eliminates a customer from the flow, the better. After a trigger, the first meaningful node should almost always be a condition node that checks reachability and consent.
A well-designed abandoned cart scenario:
TRIGGER:
cart_updatedevent, where items array is non-empty.WAIT: 2 hours, give the customer time to naturally complete the purchase.
CONDITION: customer has NOT completed a purchase event for this cart since the trigger. (Exit if purchased.)
CONDITION:
email_consentis true AND email attribute is valid. (Exit if not reachable.)CONDITION: customer has not received this scenario in the last 7 days. (Frequency cap.)
ACTION: send email.
WAIT: 24 hours.
CONDITION: customer has NOT completed a purchase event. (Exit if purchased.)
ACTION: send a second email with a discount incentive.
Notice that every condition is a pre-filter against action. Notice also that the flow exits early on purchase and re-checks at every step. This is the shape of every well-built lifecycle scenario.
Wait nodes and timezones
Wait nodes look simple and hide real complexity. Three patterns to know:
Fixed duration. “Wait 24 hours.” Simple and predictable, but this ignores timezone, a customer in Tokyo triggered at 2am will receive the follow-up at 2am the next day.
Until a specific time. “Wait until 9am local time.” This respects the customer’s timezone as stored in their profile. Use this pattern for any message where the arrival time matters to the recipient (daily digests, back-in-stock alerts).
Until the event. “Wait until product_purchased event OR 7 days, whichever comes first.” This is the pattern for conditional continuation, it lets the scenario react to the customer’s next action without needing branching logic.
Wait Pattern | Best Use Case | Customer Impact |
Fixed Duration | Immediate follow-ups | High risk of "3 AM" emails. |
Until Specific Time | Commercial newsletters | Respects local time/sleep cycles. |
Until Event | Dynamic re-engagement | Reacts to real-time behavior. |
💡Scalero strongly recommends the “until specific time” pattern for any scenario that sends commercial messages. Customers don’t love receiving your Tuesday newsletter at 3am.
Frequency capping
A mature Bloomreach instance needs a frequency management layer. Individual scenarios shouldn’t know about each other’s sending cadence, but the customer doesn’t see individual scenarios, they see their inbox, and if four scenarios all fire in the same morning, they unsubscribe.
The pattern Scalero deploys: a global “marketing eligibility” segment that combines:
Not in opt-out.
Has valid email on file.
Has not received a marketing email in the last 24 hours.
Has not received more than 3 marketing emails in the last 7 days.
Every marketing scenario (but not transactional) includes a condition node that checks membership in this segment before firing the send. The segment is configured in one place and changes propagate to every scenario automatically.
Jinja2 in Bloomreach: a working primer
Bloomreach uses Jinja2 for personalization inside email, SMS, and weblayer content. If you’ve written Django templates you’ll be at home; if not, the syntax is worth learning thoroughly, because it’s the difference between “personalization that uses a first name” and “personalization that genuinely reflects the customer.”
Three tag types:
{{ expression }}, outputs a value. Used for variable substitution.{% statement %}, executes logic. Used forif,for,set.{# comment #}, not rendered. Using comments is a best practice for complex Jinja logic, as it helps other team members understand the code without adding extra weight or visible text to the final email.
Variable substitution with defaults. Always provide a default for any variable that might be missing. An email that renders Hi! without a first name looks broken.
Hi {{ customer.first_name|default("there") }},
Conditionals.
Loops over event properties. The canonical abandoned-cart block:
The "%.2f"|format(...) filter ensures prices render as 89.00, not 89 or 89.000001.
Filters for clean output. Jinja2 ships with dozens of filters. The ones you’ll use constantly:
default("fallback"), replace missing or empty values.
lower, upper, title, case transforms.
length, count elements in a list.
round(2), numeric rounding.
datetimeformat("%B %d"), format timestamps.
Catalog lookups. Catalog lookups (covered in Part 4) let you enrich minimal events with full product data at render time:
This pattern, minimal events in catalog enrichment at render time, is the secret to emails that reflect current pricing and stock rather than stale data from when the event fired.
Testing scenarios before launch
No scenario goes live without three tests:
Debug mode with a test profile. Trigger the scenario against a test customer and step through every node. Confirm the right branches fire and the rendered message looks right.
Render preview with real customer data. Render the email against five real profiles from different segments (including edge cases: missing first name, missing cart items, customers in different countries). This catches the Jinja2 bugs debug mode misses.
Small percentage launch. For any significant new scenario, launch to 5–10% of the audience for 24–48 hours before scaling to 100%. This catches volume-dependent issues (rate limits, deliverability reputation impact) before they hit everyone.
With orchestration established, Part 4 extends Bloomreach beyond email.




