Skip to content
Unlapse
Menu

How it works

One night, start to finish.

At 03:30 local time the engine starts 13 steps. Nobody touches it. By morning every customer has one decision, the products, a reason and a place in tomorrow's send programme.
9 to 10 minutes
for the modelling and assignment core, measured on a full local run
36 to 51 minutes
on the production VM, including the incremental data pull
13
steps every night, the same order every time

The nightly run

The timeline.

  1. 0103:30

    Incremental pull

    New and changed orders, customers and products since the last high-water mark, minus a 2 hour safety buffer. The nightly incremental covers the cycle in about 1 minute 48 seconds. Bad data is refused: if a field the subscription logic depends on is under 5 percent populated, the run stops rather than corrupt the history.

  2. 02models

    Five models retrained from scratch

    Four decide who will buy: reorder, subscription, cross-sell and resubscribe. The fifth decides what to offer. All five are retrained every night rather than scored from a stale file, and the four customer models are each graded walk-forward on customers they have never seen. On 21 Sep 2026 the three customer models then running finished at 01:30:43, 01:34:00 and 01:37:47 UTC.

  3. 03scoring

    Every customer scored

    All 568,836 customers on the first brand, with calibrated probabilities that are comparable across campaigns.

  4. 04assign

    One campaign per customer

    Exactly one of 14 lifecycle states. Nine are campaigns: reorder, cross-sell, subscription, second order, resubscribe, paused-subscriber resume, win-back, reactivation and dormant. The rest are holds, where the right message is none. One pure function decides, and the same function is used by the nightly run and by the evaluator that grades it.

  5. 05products

    Products picked

    Up to 3 per customer from a ranker over 72 features per customer and product, including the full purchase history. Free samples are never offered.

  6. 06reason

    The reason written

    One sentence per prediction, built only from the signals that drove it. Reorder confidence is a lift multiple, never a raw percentage. A buying rhythm is quoted only when it is plausible.

  7. 07eligibility

    Eligibility computed

    Who may be contacted tomorrow: not inside the 21 day cooldown, not someone who just bought, not anyone Klaviyo cannot deliver to. Send eligibility was ready by 01:54 UTC on 21 Sep 2026.

  8. 08swap

    Database rebuilt beside the live one, then swapped

    The build never touches the live database. It rebuilds against a snapshot copy and swaps atomically; the full rebuild was done by 01:48 UTC on 21 Sep 2026. The API serves the old data the whole time.

  9. 09health

    Health poll

    The service is checked for up to 60 seconds after the swap. If it fails, the previous database goes back automatically and one retry follows 10 minutes later.

  10. 1009:00 to 18:40

    Sends, in 20-minute slices

    A timer fires every 20 minutes from 09:00 to 18:40 local time. Each fire sends what is left divided by the fires still to come, so a late or missed fire corrects itself.

Local step timings from one full run, for scale: recommend 115 s, reorder model 52 s, cross-sell model 85 s, campaigns 135 s, profiles 94 s.

The handoff

What Klaviyo receives.

One event per decision: 16 fields (5 written onto the customer's profile so you can segment on them, 11 on the event), up to 3 products with 16 fields each, and one link that adds every recommended product to the cart. Your flows render the email; Unlapse sends the decision.
One event, demo values, shortened
{
  "metric": "Reorder prediction by Unlapse",
  "email": "lena@example.com",
  "profile_properties": {
    "unlapse_lifecycle_stage": "repeat_buyer",
    "unlapse_top_product": "House Blend 500 g",
    "unlapse_order_cadence_days": 31,
    "unlapse_next_predicted_order": "2026-09-24",
    "unlapse_emailed_at": "2026-09-23"
  },
  "properties": {
    "unlapse_prediction_type": "repeat_buyer",
    "unlapse_score": 0.31,
    "unlapse_reason": "Active buyer, 6 orders ~every 31d, 34d since last order; reorder nudge for House Blend 500 g, model ranks them ~12x likelier to reorder than average.",
    "unlapse_cart_url": "https://morrow-coffee.example/cart/41001:1,41002:1,41003:1",
    "unlapse_cart_total": 29.30,
    "unlapse_products": [
      {
        "name": "House Blend 500 g",
        "price": 14.90,
        "is_reorder_for_customer": true,
        "times_bought_by_customer": 6,
        "avg_reorder_gap_days": 31,
        "cart_url": "https://morrow-coffee.example/cart/41001:1"
      },
      { "name": "Slow Sunday Decaf 250 g", "price": 9.90, "is_reorder_for_customer": false },
      { "name": "Paper Filters, 100", "price": 4.50, "is_reorder_for_customer": true, "times_bought_by_customer": 2 }
    ]
  }
}
Demo data: the email a brand's flow might render from the event, with three products and one cart button.
Demo data. The same event, as a brand's own flow might render it.
  • One builder. The event your platform receives and the feed a partner can pull come from the same code, so they cannot diverge.
  • A native cart link. A store permalink that adds every product at once, with no app in the path.
  • Idempotent. Each event carries a hash of organisation, customer, campaign, day and product, so a same-day re-run cannot double-send.

Tell us about your store.

We read your order history first and tell you what the engine would do with it. Then you decide.