Trial Expiring Recovery

SaaS

Convert trial users before they expire. Proactively reach out to users whose trials are ending soon with compelling upgrade offers.

The Problem

A user signs up for your 14-day trial, uses the product for a week, then gets busy and forgets about it. Their trial expires silently, and you lose a potential customer forever. With Retake, they receive timely reminders highlighting their usage and the value they'll lose.

Recommended Email Timing

7 days
Before expiration
Gentle reminder + usage stats
3 days
Before expiration
Urgency + feature highlight
1 day
Before expiration
Last chance + discount offer

Step 1. Set Up Daily Cron Job

Create a cron job that runs daily and identifies users with trials expiring in the next 7 days.

cron/check-expiring-trials.ts
typescript
// cron/check-expiring-trials.ts
// Run daily at 9 AM UTC: 0 9 * * *
import { prisma } from "@/lib/prisma";
import { retake } from "@/lib/retake";

export async function checkExpiringTrials() {
  // Find trials expiring in next 7 days
  const expiringUsers = await prisma.user.findMany({
    where: {
      plan: "TRIAL",
      trialEndsAt: {
        gte: new Date(),
        lte: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
      },
      // Don't send if already converted
      subscription: null
    }
  });

  for (const user of expiringUsers) {
    const daysRemaining = Math.ceil(
      (user.trialEndsAt!.getTime() - Date.now()) / (1000 * 60 * 60 * 24)
    );

    await retake.track({
      event: "INTENT",
      type: "trial_expiring",
      userId: user.id,
      email: user.email,
      value: 29.00, // Expected subscription value
      metadata: { daysRemaining }
    });
  }

  console.log(`Tracked ${expiringUsers.length} expiring trials`);
}

Step 2. Track Trial Expiring Intent

Or trigger manually when user logs in during their final week of trial.

typescript
// Track when user's trial is about to expire
// Best: Run this via a cron job daily, checking all trials expiring in 3-7 days
async function trackTrialExpiring(user: User) {
  const daysRemaining = Math.ceil(
    (user.trialEndsAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24)
  );
  
  await retake.track({
    event: "INTENT",
    type: "trial_expiring",
    userId: user.id,
    email: user.email,
    value: user.expectedPlanPrice || 29.00,
    currency: "USD",
    metadata: {
      trialEndsAt: user.trialEndsAt.toISOString(),
      daysRemaining,
      currentPlan: "trial",
      targetPlan: user.recommendedPlan || "pro"
    }
  });
}

Step 3. Track Conversion

When the user converts to a paid plan, tell Retake to stop the recovery emails.

typescript
// When user subscribes from trial
async function handleTrialConversion(userId: string, subscriptionValue: number) {
  // Tell Retake the trial converted - stop recovery emails
  await retake.track({
    event: "CONVERSION",
    userId: userId,
    value: subscriptionValue,
    transactionId: `sub_${Date.now()}`
  });
}

Best Practices

Include Usage Stats

Pass usage metrics in metadata to personalize emails: "You've created 15 projects and saved 3 hours this week."

Offer Trial Extensions

For engaged users who haven't converted, consider offering a 7-day extension as a last resort.

Don't Spam Inactive Users

If a user hasn't logged in for 10+ days, they likely won't convert. Consider a different re-engagement strategy.

Segment by Engagement

High-usage trial users get "don't lose your work" messaging. Low-usage users get "need help getting started?" messaging.