
Cart Recovery Sms
- 60 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Recover abandoned carts with targeted SMS sequences using urgency messaging, product reminders, discount incentives, and TCPA-compliant opt-in flows.
About
A skill for SMS-only cart abandonment recovery with well-timed, personalized sequences and TCPA/GDPR-compliant opt-in. A developer uses it to add high-recovery-rate text messaging via a dedicated SMS app.
- Personalized, timed SMS sequences with cart contents
- TCPA/GDPR opt-in compliance; use a dedicated SMS app
Cart Recovery Sms by the numbers
- 60 all-time installs (skills.sh)
- Ranked #1,276 of 1,879 Marketing & SEO skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill cart-recovery-smsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 60 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Recover abandoned carts with targeted SMS sequences using urgency messaging, product reminders, discount incentives, and TCPA-compliant opt-in flows.
Files
Cart Recovery SMS
Overview
SMS cart abandonment recovery consistently achieves 20–35% recovery rates — typically 3–5× higher than email — because text messages are read within 3 minutes of delivery for 90% of recipients. The key is a properly timed sequence (not spam), deep personalization using cart contents, and bulletproof TCPA/GDPR compliance. Most merchants should use a dedicated SMS app rather than building custom infrastructure.
Note: For multi-channel cart recovery (email + SMS + push), see @cart-abandonment-recovery. This skill focuses on SMS-only recovery.
When to Use This Skill
- When email-only cart recovery sequences plateau below a 10% recovery rate
- When launching SMS as a new marketing channel and cart recovery is the highest-ROI starting point
- When re-platforming to a new SMS provider and need to rebuild flows
- When A/B testing recovery channels to find the optimal message mix
Core Instructions
Step 1: Choose the right SMS platform
| Platform | Recommended Tool | Why |
|---|---|---|
| Shopify | Postscript or Attentive | Deep Shopify integration, automatic cart event capture, built-in TCPA compliance, Shopify checkout opt-in widget |
| WooCommerce | SMSBump (by Yotpo) or Klaviyo SMS | WooCommerce plugin available, hooks into WooCommerce cart events automatically |
| BigCommerce | Attentive or Klaviyo SMS | Native BigCommerce integrations, automatic event tracking |
| Custom / Headless | Klaviyo SMS (via API) or Twilio + custom logic | Klaviyo handles compliance and sequencing via API; Twilio for full custom control |
Step 2: Set up SMS opt-in (compliance first)
TCPA (US) requires express written consent before sending marketing SMS. Build this before any send logic.
---
Shopify
Using Postscript:
1. Install Postscript from the Shopify App Store 2. Go to Postscript → Opt-in Tools → Checkout Opt-in 3. Postscript automatically adds a compliant SMS opt-in checkbox to your Shopify checkout with the required TCPA disclosure text 4. Enable Keyword Opt-in under Opt-in Tools → Keywords — customers can text your keyword to subscribe 5. Postscript handles STOP/HELP responses, quiet hours, and consent storage automatically
Using Attentive:
1. Install Attentive from the Shopify App Store 2. Attentive's two-tap mobile opt-in (sign-up units) achieves higher opt-in rates than checkbox-only 3. Go to Attentive → Subscribers → Sign-up Units to configure placement and offer 4. Checkout integration is automatic after app install
---
WooCommerce
Using SMSBump:
1. Install SMSBump plugin from WordPress plugin directory 2. Go to SMSBump → Opt-in → Checkout Opt-in and enable the checkout checkbox with TCPA language 3. Configure quiet hours under SMSBump → Settings → Compliance — enforce 8am–9pm in recipient timezone 4. Go to SMSBump → Automations → Cart Abandonment and enable the abandonment flow
---
BigCommerce
1. Install Attentive from the BigCommerce App Marketplace 2. Configure the two-tap opt-in unit and checkout SMS opt-in through the Attentive dashboard 3. BigCommerce cart events sync automatically after installation
---
Step 3: Build the cart recovery sequence
Use your SMS platform's automation builder. The optimal 3-message sequence:
Message 1 — Reminder (20–30 minutes after abandonment)
- No discount — most recoveries happen here without one
- Include the product name and a direct checkout link
- Example: "Hi [First Name], you left [Product Name] in your cart. Complete your order: [link] Reply STOP to opt out."
Message 2 — Social proof or urgency (60 minutes)
- Reference low stock or popularity
- Example: "[Product Name] is almost gone — only 3 left. Your cart is saved: [link] Reply STOP to opt out."
Message 3 — Discount (24 hours)
- Only for carts above your minimum threshold (recommend $50+)
- Use a unique single-use code
- Example: "Last chance, [First Name]! Use [CODE] for 10% off before your cart expires. Checkout: [link] Reply STOP to opt out."
Configuration in Postscript: 1. Go to Postscript → Automations → New Automation → Checkout Abandoned 2. Add three messages with the timing above 3. Add a filter on each step: "Has NOT placed an order since starting" — this auto-cancels on conversion 4. For the discount message: use Postscript's built-in unique coupon generation (connects to Shopify's discount system) 5. Set a cart value filter: only send the discount step for carts over $50
Configuration in SMSBump (WooCommerce): 1. Go to SMSBump → Automations → Cart Abandonment 2. Enable the three-step sequence with the same timing 3. Add "Order Not Placed" condition to each step 4. Connect WooCommerce coupon generation for the discount step
Step 4: Configure cart value thresholds and discount strategy
1. Set a minimum cart value ($30–$50) for SMS recovery — low-value carts have negative ROI after SMS cost 2. Reserve discounts for the last message only — offering discounts too early trains customers to abandon on purpose 3. Use single-use codes — prevents sharing and controls attribution 4. Segment by customer value: new customers get 10% off; repeat customers get free shipping only; VIP customers (5+ orders) get no discount
Step 5: Measure performance
| Metric | Target | How to Find |
|---|---|---|
| Recovery rate | 15–25% of abandoned carts | SMS app → Automation analytics |
| Click-through rate | 25–40% of delivered messages | SMS app → Message analytics |
| Opt-out rate per step | < 3% | SMS app → Subscriber analytics |
| Deliverability rate | > 95% | SMS app → Deliverability report |
Best Practices
- Capture phone number before payment — on Shopify, Postscript captures the phone number at checkout step 1 so you can recover even if the customer never completes payment
- Enforce quiet hours — Postscript, Attentive, and SMSBump all enforce this automatically; verify it is enabled in settings (8am–9pm recipient local time)
- Keep messages under 160 characters — longer messages split into multiple SMS and increase cost
- Always include STOP opt-out — required by TCPA; all major SMS apps include this automatically
- Set a 7-day suppression after a completed sequence — if a customer abandons again right after a recovery sequence, give them space
Common Pitfalls
| Problem | Solution |
|---|---|
| SMS sent after order placed | Ensure each message step has "Order Not Placed" condition enabled in your automation |
| Anonymous cart abandonment not captured | Postscript and Attentive capture phone at checkout step 1; enable this in your checkout settings |
| Customers learn to abandon for discounts | Never discount on messages 1 or 2; skip discounts for repeat customers |
| Carrier filtering (messages not delivered) | Avoid all-caps, free link shorteners, and excessive punctuation; use your platform's verified sending number |
| High opt-out rate after first message | Message is too aggressive or cart value threshold is too low; raise the threshold and soften copy |
| TCPA lawsuit exposure | Use your SMS platform's built-in compliance tools; never import phone numbers without proof of explicit consent |
Related Skills
- @cart-abandonment-recovery
- @sms-marketing
- @email-marketing-automation
- @lifecycle-marketing-automation
- @win-back-reactivation
{
"context": "Tests whether the agent implements the correct abandonment threshold, cron cadence, 3-step timing sequence, cart value gating, quiet-hours enforcement per recipient timezone (using the specified libraries), rescheduling behaviour, idempotency safeguards, and the sequence length limit.",
"type": "weighted_checklist",
"checklist": [
{
"name": "20-minute abandonment threshold",
"max_score": 8,
"description": "Cart is classified as abandoned after 20 minutes of inactivity from checkout start (not a shorter or longer default)"
},
{
"name": "5-minute cron cadence",
"max_score": 4,
"description": "DESIGN.md or code comments state the detection job runs every 5 minutes"
},
{
"name": "recoveryState field usage",
"max_score": 6,
"description": "The code filters candidates by recoveryState === 'pending' and updates it to sms1_sent / sms2_sent / sms3_sent after each send"
},
{
"name": "3-step timing sequence",
"max_score": 10,
"description": "The schedule configuration defines exactly 3 steps with delays of 20 minutes, 60 minutes, and 1440 minutes (24 hours) respectively"
},
{
"name": "Step 3 includes discount",
"max_score": 8,
"description": "The schedule marks step 3 (1440 min) with includeDiscount: true and steps 1 and 2 with includeDiscount: false"
},
{
"name": "Cart value threshold",
"max_score": 8,
"description": "Carts are only enqueued for recovery if their value is at or above a minimum threshold (any value between $30 and $50 inclusive)"
},
{
"name": "Quiet hours use correct libraries",
"max_score": 10,
"description": "sendWindow.ts imports or references all three of: libphonenumber-js (or parsePhoneNumber), countries-and-timezones (or getTimezone), and luxon (or DateTime)"
},
{
"name": "Quiet hours window 8am–9pm local",
"max_score": 8,
"description": "The send window is 08:00–20:59 in the recipient's local timezone (hour >= 8 and hour < 21)"
},
{
"name": "Quiet hours: per-recipient timezone",
"max_score": 8,
"description": "The timezone is derived from the recipient's phone number country, not from the server or a hardcoded timezone"
},
{
"name": "Reschedule on quiet hours block",
"max_score": 8,
"description": "When the send window check fails, the job is re-enqueued for the next available window rather than silently dropped"
},
{
"name": "Job queue retry config",
"max_score": 8,
"description": "Jobs are added to the queue with attempts: 3 and exponential backoff with a delay of 5000ms"
},
{
"name": "Pre-send consent re-check",
"max_score": 4,
"description": "sendRecoverySms checks that the phone number still has active consent immediately before sending (not relying solely on consent checked at sequence creation time)"
},
{
"name": "Idempotency / no double-send",
"max_score": 4,
"description": "DESIGN.md or code explicitly describes how duplicate sends are prevented (e.g. checking recoveryState or using idempotency keys before dispatching)"
},
{
"name": "Max 3-message sequence",
"max_score": 6,
"description": "The sequence contains no more than 3 steps; no fourth step is scheduled"
}
]
}
SMS Cart Recovery Sequence Scheduler
Problem/Feature Description
A fashion e-commerce brand has built out its SMS consent layer and is now ready to implement the core cart recovery engine. The operations team wants a backend service that detects when shoppers have walked away from their checkout and automatically queues a timed sequence of recovery messages. They're especially worried about two failure modes from their previous email recovery system: messages arriving in the middle of the night (which generated a wave of complaints from customers in different time zones), and the same customer receiving duplicate messages after a service restart.
The brand's head of growth has specified that only carts with meaningful order value should qualify — low-value carts don't justify the cost or the opt-out risk. The sequence should be limited in length to avoid feeling like spam, and each step should fire at a specific interval after abandonment. The engineering lead wants the scheduler to use a persistent job queue so messages survive restarts, with automatic retry on transient failures.
Output Specification
Produce the following files:
src/jobs/detectAbandonedCarts.ts— the function that identifies newly abandoned carts and enqueues recovery sequencessrc/jobs/sendRecoverySms.ts— the function that executes a single step of the recovery sequencesrc/lib/sendWindow.ts— the quiet-hours utility that determines whether it is currently safe to send to a given phone numbersrc/config/recoverySchedule.ts— the sequence configuration (steps, delays, types)DESIGN.md— a short document explaining: (1) the abandonment time threshold chosen, (2) the cron frequency for detection, (3) how the system avoids sending duplicate messages
All TypeScript files should be syntactically valid. No actual SMS sending or database connection is required — stubs or mock calls are fine.
{
"context": "Tests whether the agent implements TCPA/GDPR-compliant SMS consent infrastructure: correct disclosure wording, a complete consent schema, proper opt-out keyword handling with mandatory reply, HELP response, EU consent recency expiry, and an audit-trail retention policy.",
"type": "weighted_checklist",
"checklist": [
{
"name": "E.164 phone validation",
"max_score": 8,
"description": "Phone number is validated using libphonenumber-js (import or reference to the library appears in the code)"
},
{
"name": "Consent schema fields",
"max_score": 10,
"description": "Consent data model includes at minimum: phone, consentText (the exact disclosure text), ipAddress, userAgent, consentedAt, and jurisdiction fields"
},
{
"name": "TCPA disclosure wording",
"max_score": 12,
"description": "The disclosure string shown to the user contains all of: 'recurring automated marketing', 'not a condition of purchase', and 'Reply STOP to unsubscribe'"
},
{
"name": "Opt-out keyword coverage",
"max_score": 10,
"description": "The inbound handler recognises all five opt-out keywords: STOP, UNSUBSCRIBE, CANCEL, QUIT, and END (case-insensitive or normalised to uppercase)"
},
{
"name": "Mandatory opt-out reply",
"max_score": 10,
"description": "The handler returns a reply message after processing an opt-out (e.g. 'You have been unsubscribed...Reply START to resubscribe')"
},
{
"name": "HELP/INFO response",
"max_score": 8,
"description": "The inbound handler handles HELP or INFO keywords and returns a response containing store name, opt-out instructions, and support contact"
},
{
"name": "Jurisdiction field",
"max_score": 8,
"description": "The consent schema stores a jurisdiction value distinguishing at minimum US vs EU contacts"
},
{
"name": "EU consent recency check",
"max_score": 12,
"description": "The consent validity utility treats EU contacts who consented more than 18 months ago with no subsequent activity as having expired consent"
},
{
"name": "Opt-out timestamp recorded",
"max_score": 10,
"description": "When an opt-out keyword is received, the handler updates the consent record with an optedOutAt timestamp (not just a boolean flag)"
},
{
"name": "5-year retention policy",
"max_score": 12,
"description": "IMPLEMENTATION_NOTES.md states that consent records are retained for at least 5 years and lists IP address, timestamp, and disclosure text as captured fields"
}
]
}
SMS Marketing Opt-In System for Multi-Region E-Commerce
Problem/Feature Description
A mid-sized online retailer is expanding its SMS marketing program to customers in the United States, Canada, and the European Union. The legal team has flagged that the current approach — a simple checkbox at checkout with no audit trail — creates significant regulatory exposure. In the US, the TCPA imposes statutory damages of $500–$1,500 per unsolicited message; in the EU, GDPR violations can reach 4% of annual revenue. The company needs a compliant opt-in and opt-out system before it can proceed with any SMS campaigns.
The engineering team needs to build two things: a checkout opt-in widget and an inbound SMS handler. The opt-in widget must collect and persist consent in a way that can be produced as evidence in a compliance audit. The inbound handler must process messages customers send back (like STOP or HELP) and return the required responses. The legal team has specified that consent records must support an audit trail that would hold up in court, and the system must properly handle the special case of EU customers who gave consent a long time ago and have since gone dormant.
Output Specification
Produce the following files:
src/components/SmsOptIn.tsx— the React opt-in widget for checkoutsrc/db/schema.ts— TypeScript interface(s) defining the consent data modelsrc/handlers/inboundSms.ts— the inbound SMS webhook handlersrc/lib/consentUtils.ts— utility functions for checking whether a contact's consent is still validIMPLEMENTATION_NOTES.md— a brief document describing the consent retention policy (how long records are kept and what fields are captured)
All TypeScript files should be syntactically valid and importable (no runtime execution needed).
{
"context": "Tests whether the agent produces correct message templates with first-name personalization and STOP opt-out footer on every message, generates recovery links with nanoid tokens and proper UTM parameters, applies discounts only on step 3 above the cart value threshold, generates correctly formatted single-use codes, keeps messages under 160 characters, and avoids carrier-filter pitfalls.",
"type": "weighted_checklist",
"checklist": [
{
"name": "First-name personalisation",
"max_score": 8,
"description": "All three message templates reference the customer first name (e.g. firstName or customer.firstName), with a fallback to 'there' when unavailable"
},
{
"name": "Step 1 content: item + value",
"max_score": 8,
"description": "The step 1 template includes both the item name (or item count when > 1) and the cart value in the message body"
},
{
"name": "Step 2 content: social proof",
"max_score": 8,
"description": "The step 2 template references a viewer or popularity signal (e.g. 'X people viewed it today') rather than just repeating cart contents"
},
{
"name": "Step 3 content: last chance + discount",
"max_score": 8,
"description": "The step 3 template includes an urgency phrase (e.g. 'last chance', 'expires soon') and conditionally inserts the discount code when one is present"
},
{
"name": "STOP footer on every message",
"max_score": 8,
"description": "All three message templates end with an opt-out instruction containing 'STOP' (e.g. 'Reply STOP to opt out.')"
},
{
"name": "nanoid token generation",
"max_score": 8,
"description": "Recovery link uses nanoid (imported from the 'nanoid' package) to generate the token, with a length of 16 characters"
},
{
"name": "UTM parameters in recovery link",
"max_score": 8,
"description": "The recovery link URL includes all three UTM params: utm_source=sms, utm_medium=cart_recovery, utm_campaign=abandoned_cart"
},
{
"name": "48-hour token TTL",
"max_score": 6,
"description": "The recovery token expiry is set to 48 hours from creation (not shorter)"
},
{
"name": "Discount only on step 3",
"max_score": 8,
"description": "DESIGN.md or code explicitly states that discount codes are generated exclusively for the final step of the sequence (step 3 / last_chance)"
},
{
"name": "Discount cart value threshold",
"max_score": 8,
"description": "generateDiscount returns null (or equivalent) when the cart value is below the configured minimum (default $50 or SMS_DISCOUNT_MIN_VALUE)"
},
{
"name": "Discount code format",
"max_score": 6,
"description": "The generated discount code follows the pattern SAVE10-{LAST6} where LAST6 is the last 6 characters of the cart sessionId in uppercase"
},
{
"name": "Single-use discount enforcement",
"max_score": 8,
"description": "The discount is created with usage_limit: 1 and once_per_customer: true (or DESIGN.md explicitly describes these constraints)"
},
{
"name": "Message length under 160 chars",
"max_score": 6,
"description": "The code includes a check, comment, or test verifying that messages stay under 160 characters, OR all three template strings are demonstrably under 160 characters in their typical form"
},
{
"name": "Carrier filter avoidance",
"max_score": 2,
"description": "Message templates do NOT use all-caps words (beyond standard abbreviations) and do NOT reference free URL shortener domains (bit.ly, tinyurl.com, etc.)"
}
]
}
Cart Recovery Message Builder and Discount Code Generator
Problem/Feature Description
A home goods retailer has its cart recovery scheduler up and running, but the engineering team now needs to implement the message content layer. The growth team wants SMS messages that feel personal and contextual rather than generic blasts — referencing what the customer left behind, creating social urgency, and closing with an incentive for high-value holdouts. They also need a system that generates one-time-use discount codes exclusively for the final recovery attempt, so the discount isn't given away prematurely.
The team has had past problems with SMS messages being filtered by carriers (customers reported never receiving them), messages arriving as two separate texts due to length, and discount codes being applied by the wrong customer. They want the new implementation to avoid these issues and to include trackable links so the analytics team can attribute recoveries back to specific SMS messages in their dashboards.
Output Specification
Produce the following files:
src/lib/buildMessage.ts— the function that constructs the SMS body for each step of the sequence, using cart and customer datasrc/lib/generateRecoveryLink.ts— the function that creates a trackable recovery link with a secure tokensrc/lib/generateDiscount.ts— the function that creates a discount code for qualifying cartssrc/lib/messageTemplates.ts— the message templates for steps 1, 2, and 3 (may be inlined in buildMessage.ts instead if preferred)DESIGN.md— a brief document describing: (1) when discounts are applied in the sequence, (2) the minimum cart value to qualify for a discount, and (3) how discount code uniqueness and single-use are enforced
All TypeScript files should be syntactically valid. Actual API calls (e.g. to Shopify or a URL shortener) may be stubbed out.
Input Files
The following type definitions are provided as the data model your functions should operate on. Extract them before beginning.
=============== FILE: src/types/cart.ts =============== export interface CartItem { id: string; name: string; price: number; quantity: number; recentViewers?: number; }
export interface Customer { id: string; firstName?: string; email?: string; }
export interface AbandonedCart { sessionId: string; customerId?: string; phone: string; email?: string; cartValue: number; currency: string; items: CartItem[]; checkoutUrl: string; abandonedAt: Date; recoveryState: string; customer?: Customer; discountCode?: string; }
{
"name": "finsi/cart-recovery-sms",
"version": "0.1.0",
"summary": "Recover abandoned carts with targeted SMS sequences including urgency messaging, product reminders, discount incentives, and TCPA-compliant opt-in flows",
"skills": {
"cart-recovery-sms": {
"path": "SKILL.md"
}
}
}