How an onboarding step comes due
Once a day, at 9am local time, an EventBridge Scheduler rule fires the guide Lambda. The Lambda reads the onboarding list, looks at one customer at a time, computes how many days it’s been since they signed up, checks which steps they’ve already finished, and decides whether to do nothing or to send something — and if so, which kind. The whole decision is plain Python. No model. No vector retrieval. Every step and every window lives in the rules doc, where a rep can edit it without a deploy.
Key takeaways
- The guide runs once a day via EventBridge Scheduler at 9am local time.
- Per-plan step plans live in the rules doc — starter gets welcome at day 0, setup at day 2, check-in at day 6.
- Four moves per customer, every tick: on track, next step due, gentle nudge, flag to owner.
- DynamoDB tracks which steps were sent and which are done so messages aren’t duplicate spam.
- The guide itself never calls a model. The decision is entirely deterministic.
The decision flow, per customer
Step plans: day 0/2/6 isn’t magic, it’s in the doc
The rules doc has one short section per plan. Each section names the steps in plain prose: “Starter plan: welcome on day 0, setup on day 2, check-in on day 6. Pro plan: welcome on day 0, connect data on day 1, feature tour on day 3, invite team on day 5, check-in on day 9.” The numbers are days after signup when each step’s message should land. Each step also has a window — how many extra days to wait, with no progress, before sending one gentle nudge, and then how many more before flagging the owner.
The plans exist for a reason. A starter customer wants to be useful fast and out of your inbox; three light steps over a week is plenty. A pro customer has more to set up, so the plan spaces things out and earns the right to send more. The shape of the plan reflects what the customer actually needs to do, not a fixed marketing drip.
Per-customer overrides exist too. The onboarding sheet has an optional column called plan_override. Name a different plan there and the guide uses that plan’s steps for that one customer. This is the right escape hatch for the enterprise account that needs the long plan even though they bought the starter tier.
Four moves, always
Every customer, every tick, lands in exactly one of four buckets. The names are simple on purpose.
- On track. Nothing is due, the customer is moving fast and finishing steps ahead of schedule, or they’re paused or finished. Do nothing. Most customers, most days, are on track.
- Next step due. A step’s day has arrived and the customer hasn’t done it yet, and the message hasn’t been sent. Send the step message with full context. Write a row to the
og-sendsDynamoDB table marking that this step has been sent. - Gentle nudge. A step was sent, its window passed, and the customer still hasn’t finished it. Send one friendly follow-up that names the original message so the customer doesn’t feel like they’re seeing it for the first time. Write the nudge to
og-sends. There is exactly one nudge per step — never a chain of them. - Flag to owner. The final window for a step passed and it’s still undone. Don’t message the customer again. Instead, tell the owner named in the rules doc — usually whoever handles onboarding — so a human can reach out. Mark the customer as flagged in DynamoDB; the guide won’t flag the same step twice. Spamming a stuck customer with more automated mail is exactly the wrong move; a real person is the right one.
State that makes the decision deterministic
The guide reads two DynamoDB tables every tick. og-sends records every message that’s gone out: (customer_id, step_id, kind, sent_date) where kind is step or nudge. og-state records each customer’s progress: (customer_id, steps_done, paused, finished, flagged). With those two tables, the move-decision logic is a few dozen lines of Python and zero magic. A given customer with a given signup date, a given plan, and a given send/done history always produces the same move. Re-running the tick produces no extra messages (because the state in DDB shows what already fired).
When a customer marks a step done — via the one-click link in Part 4 — their row in og-state gets the step added to steps_done. The next tick sees it’s done and moves on to the next step, or marks them finished if it was the last one.
Why the daily tick uses no model
The guide could call a model on the tick to write a smarter message, or to guess whether a customer is the type who needs more help. It doesn’t. Two reasons. First, the daily tick should be the one part of the system that is utterly predictable — if the rules doc says the setup step lands on day 2 and it isn’t done, the message goes out. A model in that loop introduces variance the team can’t reason about. Second, model calls cost money, and most days most customers are on track, so the call would be wasted nine times out of ten.
Bedrock fires elsewhere — on the occasional personalized rewrite of a step message and on the monthly summary mentioned in Part 6. Not on the daily tick. The guide itself is plain Python that reads a doc and writes events.
Next post: how a message finds the right customer, how quiet hours and weekends are honored, and what the one-click done link actually does.
All posts