How a price alert reaches the owner
The watcher picked a move — first alert, repeat move, or big swing. Now the dispatch 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 alert is worse than no alert: a 2am Slack ping, a bare “a price changed,” a flood of twenty alerts during a competitor’s sale day. Four small guardrails sit between the move and the actual alert.
Key takeaways
- Owner resolution: per-page override beats per-product-line default beats fallback to the configured admin.
- Slack DMs are the default; email is the fallback if no Slack ID is configured.
- Quiet hours defer alerts to the next business hour; a daily cap stops a sale day from burying an owner.
- Every alert ships with the product, competitor, old and new price, the size of the move, recent history, and your own price.
- A big swing is allowed past the daily cap; the system never edits a price either way.
Four guardrails on every dispatch
Gate 1: resolve the owner
Three places the dispatch Lambda looks for the owner of a page, in order. First, the watch list’s per-page owner_email column — if a row has a specific person assigned, that person owns it regardless of the product-line default. Second, the per-product-line default in the rules doc (“all kettle pages default to Priya”). Third, the configured admin fallback — the person who set up the monitor and gets every unowned alert. The fallback should never fire in steady state; if it does, the weekly digest names every page that hit the fallback so the rules doc can be updated.
Once the dispatch knows which person to alert, it looks up their delivery preference. The voice doc maps each owner to a Slack member ID if one is set, otherwise to an email address. Slack is preferred because price moves feel like work-context messages, and a Slack DM with a Snooze button is more useful than an email link. Email is the fallback so nobody falls through the cracks.
Gate 2: quiet hours
The checks are staggered through the day, so an alert can land at any hour — including outside business hours. A price that drops at 11pm shouldn’t buzz an owner’s phone at 11pm; it can wait until morning, because nobody’s changing a price overnight anyway.
Gate 2 reads the rules doc’s quiet-hours setting (default 7pm to 8am, configurable per business). If the current local time is in the quiet window, the dispatch creates a one-off EventBridge Scheduler rule that fires at the next business-hour minute and exits without sending. The Scheduler invokes the same dispatch Lambda with the same payload at the deferred time, where Gate 2 will let it through. The one exception is a big swing flagged urgent, which the rules doc can allow to bypass quiet hours if the business wants to know the moment a competitor halves a price.
Gate 3: daily cap
A competitor running a storewide sale can move twenty of their products in an hour. Without a cap, that’s twenty Slack DMs in an owner’s morning — the fastest way to teach someone to mute the whole system. Gate 3 counts how many alerts have already gone to this owner today against a per-owner cap (default eight). Once the cap is hit, further first-alert and repeat-move alerts are rolled into that owner’s daily digest instead of sent live: a single message listing everything that moved, sent at a set time.
A big swing is exempt. The whole point of the big-swing band is “this is unusual enough to interrupt for,” so it always goes through, cap or not. The cap shapes the ordinary noise without hiding the rare events that matter.
Gate 4: compose with full context, then ship
The voice doc has one Slack message template per move: a short message with placeholders for the product name, competitor, old and new price, the percent move, a short recent history, your own listed price, and a link to the competitor page. The dispatch Lambda fills the placeholders, attaches a “Snooze” button (covered in Part 5), and ships the message via the Slack chat.postMessage API. The Slack token itself lives in Secrets Manager.
For email fallback, the same template is wrapped in a small HTML email with the same fields and a link that, when clicked, hits a Function URL that records a snooze or a note — the email equivalent of the Slack buttons.
The recent history is what makes a price alert worth reading. “Competitor Acme dropped the kettle 13%” is useful; “...and this is the third cut in two weeks, the price has gone $79 → $74 → $69” tells the owner whether this is a one-off promo or a sustained campaign. The dispatch pulls the last several readings from pm-readings and renders them as a tiny text sparkline so the trend is visible at a glance.
Every dispatch — Slack or email, any move — writes a row to pm-alerts in DynamoDB. The next check reads that row and knows not to alert the same move again.
Why the guardrails exist
None of these gates are exotic. They’re the kind of small care a thoughtful person would take if they were sending the alerts themselves — check who actually owns this product, don’t ping at 11pm, don’t send twenty messages during one sale, include enough context that the recipient can decide without opening five tabs. Putting them in code as four small sequential gates makes them part of the design, not something you’re trusting any one alert to remember. And note what no gate does: none of them touch a price. The system’s job ends at telling the owner clearly.
Next post: how a price alert gets handled once it lands — the three actions an owner can take, and why none of them ever change your own prices.
All posts