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.
Trigger → personalise → approve flow → send via ESP
- ✓ Abandoned-cart recovery
- ✓ Post-purchase & replenishment
- ✓ Win-back & re-engagement
- ✓ Per-customer personalisation
- ✓ Subject-line variants
- ✓ Send-time optimisation
- ✓ Claude for copy & subjects
- ✓ ESP (Klaviyo/Braze) API
- ✓ Behavioural event stream
- ✓ Offer / discount guardrails
- ✓ Approval queue per flow
- ✓ Discount caps & rules
- ✓ Frequency / fatigue limits
- ✓ Brand & legal tone checks
- ✓ Unsubscribe compliance
- ✓ Human approves each flow
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.
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)
Make every lifecycle message feel 1:1
Feeds on signals from Review Insights and works with the Content Generator.