Retention Independent solution

Lifecycle Messaging

Generates individually personalised lifecycle messages — abandoned cart, post-purchase, replenishment and win-back — grounded in each customer's actual behaviour and your brand voice, then schedules them through your ESP.

The problem

Lifecycle flows are set-and-forget — and generic

Most stores run the same three templated emails for every customer. They ignore what someone actually browsed or bought, so they under-convert and slowly train customers to tune them out.

This solution writes each message to the individual — their products, their timing, their objections — while keeping brand voice and compliance intact, with a human approving the flow.

Primary objective
Recover more revenue with per-customer messaging that stays on-brand and on-policy.
How it works

Trigger → personalise → approve flow → send via ESP

Trigger
Cart, purchase, inactivity
Personalise
Per-customer copy & offer
Approve
Sign off the flow & rules
Send
Schedule through your ESP
What it does
  • ✓ Abandoned-cart recovery
  • ✓ Post-purchase & replenishment
  • ✓ Win-back & re-engagement
  • ✓ Per-customer personalisation
  • ✓ Subject-line variants
  • ✓ Send-time optimisation
Built with
  • ✓ Claude for copy & subjects
  • ✓ ESP (Klaviyo/Braze) API
  • ✓ Behavioural event stream
  • ✓ Offer / discount guardrails
  • ✓ Approval queue per flow
Guardrails
  • ✓ Discount caps & rules
  • ✓ Frequency / fatigue limits
  • ✓ Brand & legal tone checks
  • ✓ Unsubscribe compliance
  • ✓ Human approves each flow
Under the hood

Behaviour-grounded message personalisation

Each message is written from the customer's real events and constrained by offer rules — so it's relevant without ever over-discounting.

lifecycle/personalise.py PYTHON
from anthropic import Anthropic
client = Anthropic()

def cart_recovery(customer: dict, max_discount: int = 10):
    prompt = f"""Write an abandoned-cart email.
Items left: {customer['cart']}
Past purchases: {customer['history']}
You MAY offer up to {max_discount}% — only if it helps. Brand voice: friendly, concise.
Return JSON: {{"subject": str, "body": str, "discount": int}}"""
    msg = client.messages.create(model="claude-sonnet-4-20250514",
        max_tokens=500, temperature=0.6,
        messages=[{"role": "user", "content": prompt}])
    out = parse_json(msg.content[0].text)
    out["discount"] = min(out["discount"], max_discount)  # never exceed policy
    return queue_for_review(customer, out)
Outcomes
Recovered
Abandoned revenue
Per-customer
Not templated
Higher
Open & click rates
On-policy
Discounts capped

Make every lifecycle message feel 1:1

Feeds on signals from Review Insights and works with the Content Generator.

Start a conversation →
More E-commerce solutions
Lifecycle Messaging | Smort