Building a Trigger-to-Message Map from Synthetic Usage Data
Objective: Given 6 rows of raw account usage data, classify each into a real upgrade trigger or noise, then build complete earned-context, named-constraint, reversible-step copy for each real trigger.
You're on the lifecycle marketing team at Adyen, the Amsterdam-founded global payments platform for enterprise merchants, given a weekly usage export across 6 accounts to turn into an expansion messaging plan.
Classify the usage rows first, then write the message only for the rows that are real triggers.
Before you start
What you'll need
Free path (everything below is enough to finish)
Free, fast filtering for a small usage export
Free tier supports custom properties and workflow tables without a paid seat
The process
2 steps
Step 01 of 02
The lesson names three signals that do most of the work: hitting a hard limit, requesting a locked feature, and team growth via new unfamiliar logins; the signal, not the calendar, should trigger the message.
Given 6 usage rows (a merchant nearing its transaction-volume cap, a merchant that clicked into a locked fraud-scoring setting, a merchant with 4 new team logins this month, a merchant that logged in once after 3 months idle, a merchant that opened the product changelog email, and a merchant that hit its API call limit), tag each as hard-limit / locked-feature / team-growth / noise.
Procedure
- Read each row's raw event description
- Tag hard-limit for transaction-volume or API-call caps being approached
- Tag locked-feature for any click into a gated setting
- Tag team-growth for a meaningful jump in unique logins
- Tag noise for anything that isn't a self-evident signal (a single login, an email open) and exclude it from the message plan
Merchant A, nearing transaction-volume cap -> hard-limit Merchant B, clicked locked fraud-scoring setting -> locked-feature Merchant C, 4 new team logins this month -> team-growth Merchant D, single login after 3 months idle -> noise, exclude Merchant E, opened changelog email -> noise, exclude Merchant F, hit API call limit -> hard-limit 4 real triggers, 2 excluded as noise.
Healthy
Only rows with a self-evident, account-specific signal make it into the message plan; the rest are excluded.
Unhealthy
Treating every row as a trigger, including a single login or an email open, because 'more messages can't hurt' - this is exactly the blast-in-disguise pattern the lesson warns against.
What this means
A trigger has to be something the account did that makes the upgrade argument for you; anything weaker is noise.
So what do I do about it?
| Symptom | Action | Effort |
|---|---|---|
| Every usage uptick, however small, gets queued for an expansion email | Restrict the trigger list to hard-limit, locked-feature, and team-growth events only, exclude everything else | 5 min |
Step 02 of 02
The lesson's framework requires 3 moves in every expansion message: earned context (the specific thing the account did), a named constraint (what they'll hit if they don't act), and a reversible next step (a trial, downgrade path, or no-penalty opt-out).
For the 4 real triggers classified in Step 1, write the earned-context opener, the named constraint, and the reversible CTA for each, in a single reference table.
Procedure
- For each real trigger, write one sentence naming the specific account behavior (earned context)
- Write one sentence naming the exact limit or gate the account is approaching (named constraint)
- Write one CTA that offers a trial, downgrade path, or opt-out, never a hard upgrade-only button (reversible next step)
- Load all 4 rows into the HubSpot workflow reference table, tagged by trigger type
TRIGGER: hard-limit (transaction volume) Earned context: 'Your processed volume is on pace to cross your current plan's cap next month.' Named constraint: 'At your current growth rate you'll hit the cap around the 20th.' Reversible CTA: 'See a 14-day preview of the next tier, no commitment, cancel any time.' TRIGGER: locked-feature (fraud scoring) Earned context: 'Your team opened the advanced fraud-scoring settings last week.' Named constraint: 'That control is available on the Enterprise plan only.' Reversible CTA: 'Try it for 14 days on us, no charge until you confirm.'
Healthy
Every one of the 4 real-trigger rows has all 3 moves present and specific to that account's actual behavior.
Unhealthy
Reusing the same generic constraint sentence across all 4 rows just with the account name swapped in.
What this means
The map is only useful if each row could not be sent to any other account without editing it.
So what do I do about it?
| Symptom | Action | Effort |
|---|---|---|
| Message map rows are identical except for the merchant name | Rewrite each row's earned-context sentence to name the exact behavior from that account's usage data | 30 min |
Final deliverable
A trigger-to-message map covering the 4 real usage signals, with complete earned-context, named-constraint, and reversible-step copy for each, ready to load into a lifecycle tool.
See a reference example
Adyen Merchant Expansion Map (excerpt) TRIGGER: team-growth (4 new logins) Earned context: 'Your workspace added 4 new teammates this month.' Named constraint: 'Your current plan supports up to 6 seats before per-seat billing kicks in.' Reversible CTA: 'Add the next 3 seats on a 30-day trial rate, downgrade any time.' The same discipline shows up in how Intercom times its Pro-only publish prompts: the copy only fires the moment a team clicks into the gated setting, never on a fixed send date.
Success criteria
You're done when you can:
- Correctly excludes the 2 noise rows from the message plan
- All 4 real-trigger rows contain a distinct, account-specific earned context, named constraint, and reversible CTA