abandoned-checkout
What it does
Section titled “What it does”Abandoned-checkout recovery: checkout_started opens a 24-hour window. A
purchase_completed inside it closes the window quietly and marks the cart
recovered, a trait your segments and other journeys can read. Silence
spends exactly one reminder email and closes.
Example: add it with cow add example abandoned-checkout; rename the
events and the email to your domain’s vocabulary.
| Trigger | { event: "checkout_started" } |
| Tags | demo, checkout |
| Consent purpose | emailMarketing |
The pattern
Section titled “The pattern”Recovery window with a success mark. checkout_started opens a 24-hour window; a purchase_completed inside it closes the window quietly and marks the cart cart_recovered, a trait your segments and other journeys can read (“recovered once” vs “never”). Silence spends exactly one reminder email and closes.
Reach for it when
Section titled “Reach for it when”Abandoned carts, incomplete onboarding checklists, unfinished KYC: any funnel step with a clear completion event and a deadline.
Make it yours
Section titled “Make it yours”Rename the events and the email to your domain. To escalate instead of sending once, loop with a second api.waitForEvent and a stronger second email before closing.
The journey
Section titled “The journey”import { defineJourney } from "@cowliss/cli/journeys";
/** * Abandoned-checkout recovery: `checkout_started` opens a 24-hour window. A * `purchase_completed` inside it closes the window quietly and marks the cart * recovered, a trait your segments and other journeys can read. Silence * spends exactly one reminder email and closes. * * Example: add it with `cow add example abandoned-checkout`; rename the * events and the email to your domain's vocabulary. */export default defineJourney({ trigger: { event: "checkout_started" }, purpose: "emailMarketing", tags: ["demo", "checkout"], run: async (event, api) => { const purchased = await api.waitForEvent("purchase_completed", { timeout: "24h", }); if (purchased) { await api.traits.set("cart_recovered", true); return; } // The checkout link belongs to your store, so the trigger event carries // it; the email drops its button when the prop is missing. const checkoutUrl = event.properties.checkoutUrl; const me = await api.profile.get(); await api.email.send({ template: "abandoned-checkout", props: { checkoutUrl: typeof checkoutUrl === "string" ? checkoutUrl : undefined, firstName: typeof me.traits.firstName === "string" ? me.traits.firstName : undefined, }, }); },});Its scenario
Section titled “Its scenario”The example ships this scenario, copied in beside the journey. cow test replays it on a virtual clock: the trigger fires at the start, events arrive at their offsets, and expect is the ordered list of capability calls the run must make, each input matched as a subset.
{ "user": { "id": "usr_1", }, "events": [], "expect": [ { "activity": "sendEmail", "input": { "template": "abandoned-checkout", "props": { "firstName": "Ada" } } } ]}Try it
Section titled “Try it”# copy it into your projectcow add example abandoned-checkout
# run its scenario on a virtual clockcow test abandoned-checkout --scenario scenarios/abandoned-checkout.timeout.json