Skip to content

Activation States & Re-engagement

Activation states and re-engagement are a way to persist a user’s progress through onboarding as explicit fields on the user record, then use state changes and timers to decide when to contact them. Instead of re-evaluating raw behaviour for every campaign, the system records what product state the user is in.

The problem is that activation often starts life as an analytics question, then quietly becomes product control logic. Teams query events to decide who is activated, who is stuck, and who should receive help. Over time, each query drifts: different jobs use different thresholds, late events change answers, and users can match the same campaign repeatedly. Re-engagement becomes hard to reason about because there is no single recorded decision the rest of the system can trust.

The usual implementation is a small state machine attached to the user. A sign-up event creates an initial state, setup actions advance the state, the key value action marks the user activated, and inactivity can move them into a stalled or re-engagement-eligible state. Alongside the status, the record stores timestamps such as when activation happened, what step was last reached, and when another message may be considered. Messaging systems then react to transitions or scheduled checks against those fields.

The trade-off is that stored state must be maintained correctly. You need clear rules for which events change it, how to handle retries and out-of-order events, and what happens when the definition of activation changes. It is commonly misunderstood as replacing event history. It does not. Events remain the source for audit, analysis, and backfills; activation state is the product’s current operational summary, optimised for consistent decisions.

Engineers meet this in user tables, profile services, lifecycle jobs, and marketing automation integrations. A backend handler might update activation_status when a user completes a required action, while a scheduled worker checks reengagement_eligible_at and last_contacted_at before calling an email or webhook provider. Good implementations also include per-message sent markers, cooldowns, unsubscribe checks, and suppression rules, so a stalled user can receive useful prompts without being contacted every time a batch job runs.

Common questions

Why not recompute activation from events every time?
You can recompute it, but doing so everywhere makes the definition easy to fragment and expensive to apply. Persistent state gives the application one operational answer: this user has or has not crossed the threshold. Raw events should still be kept for auditing, analytics, backfills, and changing the model later.
What fields usually belong on the user record?
It depends on the product, but common fields include an activation status, the time activation occurred, the latest onboarding step reached, and the next time re-engagement is allowed. Messaging safety fields are just as important: last contacted time, campaign-specific sent flags, unsubscribe status, and any suppression reason currently in effect.
How do you avoid spamming users?
Trigger messages from state transitions and eligibility windows, not from a raw condition that remains true. Record that a message was sent, apply cooldowns, and check opt-out and suppression rules before enqueueing anything. The goal is to contact a stalled user because something meaningful changed, not because another scanner rediscovered the same state.