Guest Checkout Recovery

New

Handle cart recovery when you don't have the customer's email at cart time. Common in guest checkout flows.

The Challenge

Many e-commerce sites allow guest checkout where the email is only captured at the final payment step. If the user abandons before that point, we don't have their email to send recovery messages. This recipe shows two solutions.

Solution 1. Webhook-Based Recovery

Track cart without email, then use webhooks to look up the email from your database when the cart is marked as abandoned.

Step 1: Track cart without email

typescript
// Track cart without email
await retake.track({
  event: "INTENT",
  type: "cart",
  userId: getSessionId(), // Session-based ID
  // No email yet!
  value: 149.99,
  items: cart.items
});

Step 2: Handle the abandoned webhook

typescript
// app/api/retake/webhook/route.ts
export async function POST(request: Request) {
  const payload = await request.json();

  if (payload.event === 'intent.tracked') {
    const { intentId, userId, items, value } = payload.data;
    
    // Look up customer email from your database
    const customer = await db.users.findUnique({
      where: { id: userId }
    });

    if (customer?.email) {
      // Send recovery email using your email service
      await sendRecoveryEmail({
        to: customer.email,
        subject: "You left something behind!",
        cartItems: items,
        cartTotal: value,
        recoveryLink: `https://yourstore.com/cart?resumed_intent=${intentId}`
      });
    }
  }

return Response.json({ received: true });
}

Configure Webhook URL

Go to your Retake Dashboard → Settings → Webhooks and add your webhook endpoint URL.

Solution 2. Early Email Capture

Change your checkout flow to capture email first, before payment details. This is actually a UX best practice used by Amazon, Shopify, and most modern checkouts.

Two-Step Checkout
tsx
// Two-step checkout: capture email first
function CheckoutPage({ cart }: { cart: Cart }) {
    const [email, setEmail] = useState('');
    const [step, setStep] = useState<'email' | 'payment'>('email');

    async function handleEmailSubmit(e: FormEvent) {
        e.preventDefault();

        // 🔥 Now we have their email - update the intent!
        await retake.track({
            type: 'checkout',
            userId: getSessionId(),
            email: email, // Now included!
            value: cart.total,
            items: cart.items
        });

        setStep('payment');
    }

    if (step === 'email') {
        return (
            <form onSubmit={handleEmailSubmit}>
                <input
                    type="email"
                    value={email}
                    onChange={(e) => setEmail(e.target.value)}
                    placeholder="Enter your email to continue"
                    required
                />
                <button type="submit">Continue to Payment</button>
            </form>
        );
    }

    return <PaymentForm email={email} cart={cart} />;
} 

Better for recovery

Retake can send recovery emails automatically without webhooks.

Better for UX

Users can save their progress and complete checkout later.

Which Solution Should I Use?

CriteriaWebhook ApproachEarly Email Capture
Implementation effortMore complexSimpler
Requires code changesBackend onlyFrontend + Backend
Relies on existing user dataYesNo
Best forSites with existing session-to-email mappingNew builds or checkout redesigns