How the offer gets sent
By the time this step runs, the add-on is chosen and the wording is written. All that’s left is to deliver it well: to wait a beat so it doesn’t crowd the checkout, send exactly one nudge, and make saying yes a single tap. This post is about the send and the one-tap add link — a Function URL redirect that turns “go on then” into an item in the basket.
Key takeaways
- The nudge goes out after a short, deliberate delay — long enough not to crowd the checkout, soon enough to still be relevant.
- A scheduled sender releases due offers; it re-checks opt-out and the cap at send time, so a late opt-out still stops the message.
- The offer is one message carrying the chosen add-on, its price, and a single one-tap add link — nothing to type.
- The add link is a signed token on a second Lambda Function URL that records the tap and 302-redirects into the store’s add-to-cart.
- The link URL is built by code from the chosen SKU, never written by the model, so it can never point at the wrong product.
A beat, then one message
By the time this step runs, the add-on is chosen and its line is written. What’s left is delivery, and delivery has two jobs: send at the right moment, and make saying yes effortless. The right moment is not the instant of checkout — a nudge that lands while the customer is still reading the order confirmation feels grabby, like a shop assistant following you to the door. So the offer carries a send-at time set a short, deliberate delay after the order: an hour or so by default, tunable per shop. Long enough that the checkout has settled; soon enough that a commuter bike still feels like a fresh purchase and lights are an obvious afterthought rather than a cold re-approach a week later.
The send itself is driven by a scheduled sender, not by a per-order timer. On a fixed cadence it queries the offers table for anything whose send-at has passed and which hasn’t yet been sent, and releases those. This is cheaper and simpler than scheduling one timer per order, and it gives the system a natural place to re-check the guardrails: right before sending, it looks again at the opt-out list and the frequency cap. A customer who opted out in the hour between order and send is caught here, and the message is dropped. The decision to send is never older than the send itself.
The one-tap add link
The message is short: the chosen add-on, its price, the model’s one line, and a single link. Everything hangs on that link being one tap. It is not a coupon code to copy, not a “visit our shop” homepage, not a search the customer has to redo — it is a signed token on a second Lambda Function URL that, when tapped, does two things and redirects. First it records the tap against the offer (the attribution signal Part 5 is built on). Then it issues an HTTP 302 to the store’s own add-to-cart or pre-filled-checkout URL for exactly the SKU that was chosen, so the item is sitting in the basket before the customer has finished looking at their phone. The whole distance between “go on then” and “it’s in the basket” is one tap and one redirect.
The token is signed so it can’t be forged or edited — a customer can’t swap the SKU for a pricier item at a discount, and a stranger can’t mint links — and it carries the offer id and the SKU, nothing sensitive. Critically, the destination URL is assembled by code from the chosen SKU, not written by the model. The model wrote the sentence; the code owns the link. A language model is perfectly capable of “improving” a URL or mistyping a product id, and a link that adds the wrong thing (or nothing) is the one error that quietly wastes the whole nudge. By keeping the link out of the model’s hands, the tap always adds exactly the add-on the rules chose.
One channel, one message
The nudge goes out on one channel — an SMS via SNS in the walk-through here, though the same offer can just as well be an email through SES where that suits the shop better. Whichever it is, it’s one message, and the message is checked by code before it leaves: it must be within the length limit, contain exactly one link (the add link we built, and no others), carry the small opt-out line that keeps the shop compliant, and name a price that matches the chosen SKU’s catalogue price rather than anything the model might have typed. If a draft fails any check, or the model’s line is missing, the sender falls back to a plain fixed template — “{name}, to go with your order: {add-on} for {price}. Add it in one tap: {link}. Reply STOP to opt out.” — which is duller but always correct.
What the sender never does is send twice. The moment an offer is sent, its state flips to sent with a timestamp, so the next run of the scheduled sweep passes over it. One order produced one offer in Part 3; that offer produces exactly one message here. There is no “did you see our suggestion?” follow-up, no second reminder, no escalation of the ask. If the customer taps, Part 5 records it; if they don’t, the offer simply expires when its attribution window closes. The whole design rests on a single, unrepeated, easy-to-accept nudge, and the send step is where that promise is kept.
Design rules that shaped the send
- Wait a beat. A short send-at delay keeps the nudge off the checkout screen and out of the way of the order confirmation.
- A sweep, not a timer per order. A scheduled sender releases due offers, which is cheaper and gives a place to re-check the guardrails.
- Re-check at send time. Opt-out and the frequency cap are read again just before sending, so a late opt-out still stops the message.
- Saying yes is one tap. A signed token on a Function URL records the tap and 302-redirects straight into add-to-cart.
- The link is the code’s job. The destination is built from the chosen SKU, never written by the model, so it can’t point at the wrong thing.
- One message, never two. The offer flips to sent on the first message; there is no reminder and no second ask.