Engagement states

What it is

Every customer with purchase history is in exactly one lifecycle state at any moment. The state says how "alive" the customer's buying relationship with your store is, and influences what kind of recommendations and content they get.

How it is computed

The state is recomputed every night, primarily from time since the last purchase:

StateRule
Hotlast purchase ≤ 30 days
Warm30-60 days
Cooling60-120 days
Lapsed120-180 days
Dormantover 180 days
Recently won backa lapsed/dormant customer who clicked an email within the last 7 days

Two special rules:

  • Rescue: a lapsed or dormant customer who clicks an email rises to "recently won back" — they have shown a sign of life.
  • Consolidation: if a won-back customer buys within 30 days, they become "hot". If no purchase comes and the click ages out, they fall back to lapsed/dormant.

A customer with no purchases yet has no state — for them, see cold start.

How to interpret it

The most important rule: event vs state. The Insights "won back" number counts events in a period (how many customers entered the state during the period). The Audience filter shows a snapshot (who is in the state right now). "Recently won back" is deliberately a short-lived transitional state — the customer either buys (→ hot) or falls back. That is why the period event count is almost always larger than the current snapshot, and that is correct.

Common misreadings

  • "The KPI says 72 won back, the filter shows 7 — where did 65 go?" — nowhere: they moved on through the lifecycle (some bought and are hot, some fell back). Event vs state.
  • "A customer opened an email — why are they still dormant?" — the rescue is triggered only by a click, not an open (opens are noise due to Apple's privacy protection).
  • "Why is a customer with purchases 'dormant'?" — the state looks at the recency of the last purchase, not the purchase count. Five orders two years ago = dormant.

Technical background

Classifier: lib/engine/learning/engagement-state.ts (thresholds 30/60/120/180 days; rescue click ≤ 7 days; consolidation purchase ≤ 30 days). Nightly cron update-engagement-state; transitions are logged to customer_engagement_history (event counts come from there); the current state lives on customers.engagement_state.

Last updated: 2026-07-06