Part 2 of 7 · Cart recovery series ~4 min read

How an abandoned cart gets spotted

The system can only win back a cart it knows about. So the first job is catching every cart the moment it forms, and noticing the instant it’s left behind. There are three ways a cart gets onto the list: the store tells the system directly as it happens, the day’s carts land in a sheet a human can read, and carts that started from a saved link get a small tag. The first one does almost all the work. The other two exist because real stores have gaps, and a human still likes to glance at the day.

Key takeaways

  • Three intake lanes feed one cart list: the storefront webhook, a nightly Drive export, and a saved-link tag lane.
  • The webhook is a Lambda Function URL; every add, update, and checkout posts one event.
  • Each event writes or updates one row per cart in DynamoDB and schedules the next wake-up.
  • The nightly Drive export mirrors the day’s carts to a sheet a human can scan.
  • The DynamoDB cart list stays the canonical store. The other lanes are conveniences that write into it.

Three lanes into one cart list

Three intake lanes funnel into one cart list A diagram with three vertical lane columns at the top and a single unified row at the bottom. Lane one, Storefront webhook: the store posts a cart event to a Lambda Function URL the moment a shopper adds, updates, or checks out; the intake Lambda writes or updates one row per cart in DynamoDB and schedules the next wake-up via EventBridge Scheduler. Lane two, Nightly export: a small Lambda runs once a night, reads the day's carts from DynamoDB, and writes them to a Google Sheet in a Drive folder so a human can scan the day; the sheet is read-only and never feeds back into the system. Lane three, Saved-link tag: a cart started from a saved link the shopper emailed themselves arrives with a small flag in its event; the intake Lambda keeps the flag on the row so the reminder copy can match. All three lanes converge on the same DynamoDB cart list, which holds one row per open cart. A note at the bottom: the cart list stays the source of truth — the other lanes are conveniences that read it or tag it. Lane 1 · live Store webhook • Store posts a cart event on change • Function URL Lambda writes the row • Schedules the next wake-up • Source of truth is DynamoDB Lane 2 · nightly Drive export • Lambda runs once a night • Reads day's carts from DynamoDB • Writes a Drive sheet a human can scan • Read-only — never feeds back in Lane 3 · tag Saved-link tag • Cart from a saved link arrives flagged • Intake keeps the flag on the row • Reminder copy can match the context • Same list, one extra column DynamoDB cart list (source of truth) cart id · email · items · total · abandoned at · status · saved-link flag one row per open cart — the waiter reads it on each wake-up to waiter, per-cart wake-up The cart list stays the source of truth — the other lanes are conveniences that read it or tag it.
Fig 2. Three lanes converge on one DynamoDB cart list. The list is the source of truth; the export lane is a read-only convenience and the tag lane just adds context. The webhook does almost all the work, writing one row per cart the moment a cart changes.

Lane 1: the storefront webhook (the one that does the work)

The main lane. Your store calls a small endpoint — a Lambda Function URL — every time a cart changes: an item added, a quantity changed, the email entered at checkout, or the order placed. Most ecommerce platforms can post these events out of the box; if yours can’t, a few lines of theme code can. Each event carries the cart id, the items, the cart total, the shopper’s email (once they’ve typed it), and a timestamp.

The intake Lambda takes the event, writes or updates one row per cart in DynamoDB, and schedules the next wake-up via EventBridge Scheduler — one timer per cart, set to when the first reminder might be due. If the cart changes again before then, the row is updated and the timer is reset, so a shopper who’s still actively adding items never gets a premature nudge. A checkout event flips the row to bought and cancels the timer outright. This lane is the source of truth; everything the waiter reads, it reads from here.

Lane 2: nightly Drive export (the one a human reads)

The cart list lives in DynamoDB, which is great for the system and not great for a person who just wants to glance at the day. So a small Lambda runs once a night, reads the day’s carts, and writes them to a Google Sheet in a Drive folder you control — one row per cart with the items, the total, whether it was recovered, and when. The owner can open it over coffee and see how the day went without ever touching AWS.

The sheet is read-only as far as the system is concerned. It never feeds back in. That keeps the rule simple: the system acts on DynamoDB, and the sheet is a window onto it, not a second copy that can drift out of sync.

Some shoppers save a cart on purpose — they email themselves the link, or tap “save for later,” meaning to come back when payday lands. A reminder that says “you left these behind” reads slightly wrong for that shopper; “still saving these?” reads right. So when a cart arrives from a saved link, the event carries a small flag, and the intake Lambda keeps that flag on the row. It changes nothing about the timing — it just lets the copy in Part 4 match the situation.

This is the most optional of the three lanes. A store that doesn’t distinguish saved carts loses nothing; a store that does gets reminders that feel a touch more human.

Why the cart list stays the source of truth

Three lanes in, but only one place the waiter actually looks. That’s deliberate. If the export sheet could write back, or the webhook and the tag lane each kept their own state, every “why did this reminder go out?” question would mean checking three places. Funneling everything through one DynamoDB list means there is exactly one row per cart, the timing is computed from one timestamp, and the audit trail has one shape. The convenience lanes are first-class for catching carts and reading them, but the list is where the system lives.

Next post: how the waiter actually reads a cart, computes time-since-abandon, and picks one of four moves.

All posts