Checkout & payments
Cart Recovery Automation
Indicative price, not an offer: EUR 150-300/year or EUR 350 one-off
Generated from docs/plan/paid/cart-recovery-automation/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Cart Recovery Automation watches for abandoned carts, triggers a series of recovery emails via an integrated email service provider (SendGrid, AWS SES, Mailgun), and provides merchants with templates, delay rules, and performance dashboards to recover revenue without manual work.
The problem
Abandoned carts are the largest source of lost revenue. 80% of merchants do nothing—they lack the time and tooling to send recovery emails to their customers. Competitors offer automated recovery sequences that merchants configure once and forget.
What it does
- Automatic detection of carts abandoned for N minutes (configurable: 15, 30, 60, 120 min or custom)
- First recovery email sent immediately after abandonment window closes
- Second email sequence (optional) sent 24h after abandonment if first email was not opened
- Third email (optional) sent 72h after abandonment with a unique discount code incentive
- Email template builder with merchant logo, product images from cart, abandonment reason selector
- Configurable sender address and reply-to header for each sequence
- One-click recovery link that restores full cart state (items, quantities, shipping address, method) without login
- One-time-use discount code generation for final email (e.g., 10% off recovery purchases only)
- Suppression rules: exclude logged-in users, users who purchased in recovery window, one-click unsubscribes
- Email provider credential management: SendGrid, AWS SES, or Mailgun (merchant selects one)
- Webhook listener for bounce/complaint/spam feedback from email provider to auto-suppress bad addresses
- Analytics dashboard: emails sent, open rate, click rate, recovery conversion rate, cart value recovered, unsubscribe rate
- Customer segmentation: send only to high-value customers or carts over a EUR threshold
- A/B test builder: randomize subject line or CTA across half of recovery sends; track performance per variant
- Scheduled pause window: pause all recovery sends during merchant-specified date ranges (e.g., holiday campaigns)
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Email provider account setup or billing management (merchant must already have SendGrid/SES/Mailgun account). Reason: Email at scale is a paid service; AstroBaaS integrates the API, not the vendor relationship.
- SMS recovery channel (separate scope; this feature is email-only). Reason: SMS is a different compliance footprint (GDPR + TCPA in different regions); belongs in a dedicated feature.
- Personalization beyond customer name and cart contents (no behavioral ML scoring). Reason: ML model inference requires external credentials and support expertise; belongs in a separate advanced feature.
- Push notification recovery (separate scope; email is the delivery channel here). Reason: Push requires app installation and device tokens; different audience.
- Incentive generation beyond simple one-time discount codes (no loyalty points, sweepstakes, gift cards). Reason: Complex incentives become a separate feature quickly.
- Retargeting ads or pixel tracking (no Facebook/Google Ads pixels). Reason: Pixel management and ad platforms are a separate feature.
- Bulk cart data export to CSV (merchants access data via analytics dashboard, not export). Reason: GDPR data subject access requests are handled separately; feature focuses on recovery, not export.
Data model
New table: CartRecoveryEmail (join on carts, denormalized email address). Schema: id (pk), cartId (fk), emailAddress (denormalized), abandonedAt (timestamp), stage (enum: pending, sent, opened, clicked, converted, bounced, suppressed), sentAt (timestamp, nullable), openedAt (timestamp, nullable), clickedAt (timestamp, nullable), orderId (fk, nullable; set if recovery led to purchase), discountCodeUsed (string, nullable), unsubscribeToken (string, unique), providerMessageId (string; SendGrid/SES ID for webhook correlation), sequence (int: 1, 2, or 3), metrics (json: openCount, clickCount, bounceReason). Indexes: (cartId, sequence), (emailAddress), (providerMessageId). This IS a migration: new table + indexes.
API
- POST /api/cart-recovery/config — set abandon window, email provider, sender address, reply-to
- POST /api/cart-recovery/templates — save/update recovery email template for sequence 1, 2, or 3
- POST /api/cart-recovery/segmentation-rules — set min cart value, customer segments to target
- POST /api/cart-recovery/test-send — send test email to merchant’s own address
- GET /api/cart-recovery/analytics — aggregated stats (sent, open rate, click rate, recovery rate, cart value recovered)
- POST /api/cart-recovery/suppress-address — manually suppress an email address
- GET /api/cart-recovery/suppression-list — retrieve all suppressed addresses
- POST /api/cart-recovery/pause — pause all recovery sends for N days (campaign window)
- POST /api/cart-recovery/webhook/bounce — inbound from SendGrid/SES/Mailgun (mark bounced)
- POST /api/cart-recovery/webhook/open — inbound from SendGrid/SES/Mailgun (mark opened)
- POST /api/cart-recovery/webhook/click — inbound from SendGrid/SES/Mailgun (mark clicked)
- GET /api/cart-recovery/unsubscribe/{token} — one-click unsubscribe link (public, no auth required)
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Cart Recovery settings card: enable/disable globally, set email provider (SendGrid/SES/Mailgun dropdown), paste API key (masked input). Sequence configuration: set delay in hours for email 1, 2, 3. Template builder per sequence: WYSIWYG editor with merchant logo upload, auto-fetch product images from cart, {{cartTotal}}, {{recoveryLink}}, {{discountCode}} variables. Segmentation: toggle ‘only high-value carts’ with EUR threshold, toggle ‘only returning customers’. A/B testing: toggle on, choose field to randomize (subject or CTA), see split assignment. Analytics dashboard: line chart of recovery rate over 30 days, table of top-performing subjects, unsubscribe trend. Suppression list viewer: bulk add/import CSV, manual remove. Campaign pause: date range picker. Test email form with success/error toast.
The seam — why this is paid
Core owns: cart/order schema, customer identity, discount code generation, email sending adapter. Paid pack owns: recovery workflow orchestration (detecting abandoned carts via scheduled job, filtering, templating), email sequencing (delays, open/click tracking), webhook listener for ISP feedback, analytics aggregation, suppression rules. Boundary: core’s email adapter is called by paid pack’s scheduler, never the reverse. Core does not know recovery happened; paid pack queries abandoned carts via existing carts API and generates codes via existing discount API.
Requires email service provider credentials (SendGrid, AWS SES, Mailgun). Paid for infrastructure integration and list management support.
Dependencies
- Core cart schema (must already exist)
- Core order schema (must already exist)
- Core customer/contact schema (must already exist)
- Core email adapter + settings (must already exist)
- Core coupon/discount code generation (must already exist)
- Assumption: merchant has active SendGrid, AWS SES, or Mailgun account with valid API credentials
- Assumption: webhook URLs are externally accessible (not behind NAT without reverse tunnel)
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- A cart abandoned 60 minutes ago (if window is 60 min) receives a recovery email within 5 minutes of window close
- Recovery email contains merchant’s logo, product images from the cart, and a one-click recovery link
- Recovery link opens the storefront with cart items, quantities, shipping address, and method pre-filled; user can proceed to checkout in one click
- Second email is sent 24h after abandonment IF first email was not opened; sending can be toggled off in config
- Discount code in final email is unique per-cart, one-time use, and scoped to the customer’s email address only
- A customer who opened the first email but did not click does not receive email 2 (suppression rule applies)
- If email provider (SendGrid) is unreachable, pending sends queue and retry with exponential backoff; merchant sees ‘pending’ status in admin
- Suppression list includes ISP bounces and one-click unsubscribe clicks; those addresses never receive another recovery email
- A/B test: emails with subject ‘Complete your order’ are split evenly between test and control groups; analytics show open rate per variant
- Disabling the feature stops all pending sends but preserves sent/bounce/click history for audit purposes
Risks
Email reputation damage: if recovery emails are marked as spam at scale, merchant’s sending domain gets blacklisted by ISPs. Feature must include clear unsubscribe link and respect ISP feedback loops. GDPR compliance: recovery emails to EU customers must include unsubscribe link and honor data retention limits (delete on request). Feature must support one-click withdraw of consent. Email provider rate limits: high-traffic merchant with 10k abandoned carts per day hits SendGrid/SES quota; queuing will fall behind. Must implement exponential backoff and quota alerts. Webhook signature validation: inbound bounce/open/click events from email provider must be HMAC-signed; if not validated, attacker can forge metrics. Must verify signature on every webhook. Duplicate sends: scheduler crashes between ‘mark sent’ and ‘send email’; retry re-sends same email twice. Make all sends idempotent (check sentAt before resending). Discount code abuse: if code generation is not atomic, two carts could receive the same code. Must generate + reserve in single DB transaction.
Commercial context
| Suggested price | EUR 150-300/year or EUR 350 one-off |
| Rival anchor | Mageplaza Abandoned Cart Email, Aheadworks Follow Up Email |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.