Replenishment

What it is

Replenishment is the prediction of when a customer is ready to re-buy a recurring product (food, litter, supplements). It is the engine's most direct "right product at the right moment" mechanism and drives Smaily automations.

How it is computed

  • For every customer × product pair bought repeatedly, the engine computes a personal purchase interval (the median gap between purchases). A curve counts as reliable from 3 observations; before that, sector-typical intervals are used.
  • "Product" here means the product family — different sizes/variants of the same product count as one buying cadence (a 30ml and then a 50ml of the same serum = a repeat purchase, not two separate products). The offer itself stays at the variant level: the engine offers the variant the customer bought last. Family detection requires the store to send the product's parent id (Shopify does; other platforms are following) — without it every variant counts as its own product, as before.
  • From the 4th repeat purchase on, the interval adapts to the customer's personal consumption rate (a big household empties a bag faster).
  • Next-purchase prediction = last purchase + personal interval. The recommendation window opens at 85% of the interval — slightly before running out rather than after.
  • Two purchases less than a day apart (a split order or a correction) do not count as a purchase interval — otherwise the personal interval could collapse toward zero and replenishment would look due immediately. A customer whose repeat purchases are mostly same-day may need one more genuine (>1 day apart) repeat before a curve appears.

How to interpret it

  • rec_days_to_replenish is the days until predicted run-out for the customer's most timely recurring product (rec_replenish_sku). A small number = the right moment for an email.
  • The prediction is statistical, not a promise — the customer may buy in-store, gift the bag, or switch brands. The curve learns with every new purchase.
  • Customers without repeat purchases get no replenishment fields — that is normal.

Common misreadings

  • "The customer just bought — why are they offered the same product?" — check the rec_days_to_replenish value: if it is large, this is NOT a replenishment offer but e.g. a complementary product. If an automation sends the same product right after a purchase, the condition is probably set on the field's existence rather than its value (see the recipe below).
  • "The prediction looks wrong" — with fewer than 3 repeat purchases you are looking at the sector default, not a personal rhythm.

Smaily automation recipe

Typical trigger: send a replenishment email when rec_days_to_replenish <= 5 and rec_replenish_sku is not empty. In the email body use {{rec_replenish_sku}} and the recommendation slots as usual.

Technical background

Curves: cadence_curves_customer (median interval, ≥3 observations = "mature", consumption_rate_factor EWMA from the 4th repeat purchase — spec §6.7.1). Purchases are grouped by product family (catalog.tags.product_id, lib/catalog/product-family.ts); the curve is stored under the SKU of the most recently bought variant. Window opening: lib/engine/triggers/cadence-window-reached.ts (buffer_factor 0.85; the cooldown is family-aware — a recent recommendation of a sibling variant postpones the trigger). Smaily fields: rec_predicted_next_replenish_at, rec_days_to_replenish, rec_replenish_sku (spec §8.5).

Last updated: 2026-07-14