Part 4 of 7 · Onboarding guide series ~5 min read

How a nudge reaches a customer

The guide picked a move — next step due, gentle nudge, or flag to owner. Now the sender Lambda has to figure out who to send it to, on what channel, at what time of day, and with what context attached. Get any of those wrong and the message is worse than no message: a 2am email, a generic “keep going,” a nudge to someone who already finished. Four small guardrails sit between the move and the message actually landing.

Key takeaways

  • Channel resolution: a flag goes to the owner in Slack; a step or nudge goes to the customer by email.
  • Email is the default for customers; the owner’s flag uses Slack.
  • Quiet hours and weekends defer messages to the next available business hour.
  • Every message ships with the next step, a short how-to, a link in, and a one-click done link.
  • A flag goes to the owner instead of the customer; the customer is not messaged again automatically.

Four guardrails on every message

Four guardrails between the guide's chosen move and the sent message A horizontal flow diagram. On the far left, a "Move chosen" box: the guide emitted an event with the customer, the plan, and one of three sending moves — next step due, gentle nudge, or flag to owner. Four guardrail gates sit in a row to the right, each drawn as a vertical bar. Gate 1: Resolve channel — for a next-step or nudge move, the recipient is the customer at their email; for a flag move, the recipient is the onboarding owner from the rules doc, delivered in Slack. Returns the address and the channel. Gate 2: Quiet hours — checks the local time against the rules-doc quiet-hours window (default 6pm to 8am); if outside business hours, defers the send to the next available business minute via an EventBridge Scheduler one-off rule. Gate 3: Weekend and holiday — checks the date against weekends and the configured holiday list; if it falls on one, defers to the next business day. Gate 4: Final compose — formats the message from the voice doc template for the chosen step, attaches the customer context (next step, a short how-to, a link into the product) and a one-click "I've done this" done link backed by a Function URL. After all four gates pass, the message ships via SES outbound for a customer email, or via the Slack webhook for an owner flag. A note at the bottom: every send is logged to DynamoDB so the next tick knows the message has gone out. Move chosen step due, nudge, or flag Gate 1 Resolve channel step/nudge? customer email flag? owner pick address and channel Gate 2 Quiet hours local time in business window? if not, defer via Scheduler Gate 3 Weekend + holiday date on a weekend/holiday? if yes, defer to next business day Gate 4 Compose + context voice template for the step + step, how-to, link in, done link Send — SES outbound email (customer) or Slack flag (owner) SES SendRawEmail for the customer · Slack webhook for the owner every send logged to DDB og-sends — the next tick won’t duplicate Every gate is a deterministic check — no model calls, no surprise message on a Sunday at midnight.
Fig 4. Four guardrails between the move and the sent message. Resolve the channel. Honor quiet hours. Skip weekends and holidays. Compose with full context. Then send via email or Slack and log it so the next tick doesn’t duplicate.

Gate 1: resolve the channel

The first question is who this message is even for. A next step due or gentle nudge move is for the customer — it goes to their email address from the onboarding sheet. A flag to owner move is not for the customer at all; it goes to the onboarding owner named in the rules doc, delivered in Slack so it lands where the team already works. The sender resolves the right recipient and the right channel before it does anything else.

For customer messages, email is the right surface: it’s where a welcome and a how-to belong, it survives if the person isn’t logged in, and the one-click done link works from any inbox. For owner flags, Slack is the right surface: it’s a work-context ping the team will actually see, and it can carry buttons to pause or hand off, which Part 5 covers. The split keeps customer-facing messages calm and team-facing messages actionable.

Gate 2: quiet hours

The guide itself runs at 9am local time, so the first time a move fires it’s already in business hours. But deferred sends — a message held from a weekend, or a one-off computed send — can come due later in the day or outside the configured window.

Gate 2 reads the rules doc’s quiet-hours setting (default 6pm to 8am, configurable per business). If the current local time is in the quiet window, the sender creates a one-off EventBridge Scheduler rule that fires at the next business-hour minute and exits without sending. The Scheduler invokes the same sender Lambda with the same payload at the deferred time, where Gate 2 will let it through. A welcome email landing at 2am reads as spam; the same email at 9am reads as care.

Gate 3: weekend and holiday

The rules doc lists the holidays you observe — either a static list (“Christmas Day, New Year’s Day, Independence Day...”) or a reference to a Google Calendar that holds them — and a setting for whether weekends count as business days for onboarding messages (off by default; most B2B onboarding shouldn’t email on a Saturday). Gate 3 checks the current local date and, if it’s a weekend or a configured holiday, defers the send to the next business day.

The list is explicit on purpose — the guide won’t auto-detect a country’s public holidays for you. A holiday you forgot to add sends a message that lands on a closed laptop; a holiday in the list that’s no longer observed just delays a message by one business day, which is fine. The trade-off favors keeping the list explicit.

Gate 4: compose with full context, then ship

The voice doc has one email template per step: a short message with placeholders for the customer name, the step name, a one-line how-to, and a link into the product where the step actually happens. The sender fills the placeholders, attaches a one-click “I’ve done this” link, and sends the email via SES outbound. The done link is a Function URL that, when clicked, records the step as finished for that customer — no login, no support ticket, one tap. The signed token in the link ties it to the exact customer and step so it can’t be reused or guessed.

For an owner flag, the same context is wrapped in a Slack message instead: the customer name, plan, the step they’re stuck on, how many days it’s been, and buttons to pause or hand off. The owner gets the whole picture without opening the sheet.

A gentle nudge is composed from a softer template than the first step message — it references the original (“a couple of days ago I sent over how to connect your data”) so it reads as a friendly follow-up, not a fresh demand. There is only ever one nudge per step; after that the move becomes a flag, and the customer is left alone.

Every send — customer email or owner flag — writes a row to og-sends in DynamoDB. The next day’s tick reads that row and knows not to send the same step again.

Why the guardrails exist

None of these gates are exotic. They’re the kind of small care a thoughtful human would take if they were sending the messages themselves — check who this is actually for, don’t email at 11pm, skip the weekend, include enough context that the customer can act without asking a follow-up question. Putting them in code as four small sequential gates makes them part of the design, not a feature you’re trusting the writer of any one message to remember.

Next post: how an onboarding finishes once a customer has worked through their steps — how the guide sends a wrap-up, how the owner can pause a sequence, and how a hand-off to a human works.

All posts