BirrJS
Concepts

Reminders

Notify customers before their subscription expires.

Reminders fire subscription.reminder events ahead of expiry so you can send emails, SMS, or in-app notifications.

How it works

A daily cron sweep checks subscriptions expiring on the exact days listed in reminderLeadDays. For each match, it fires subscription.reminder with daysUntilExpiry. A reminder_sent table prevents duplicate sends — the same subscription on the same day only fires once.

Reminder records are automatically cleared when a subscription is reactivated, so renewed subscriptions get fresh reminders for the new period.

Configuration

createBirr({
  scheduling: {
    reminderSweepCron: "0 8 * * *",   // daily at 8am UTC
    reminderLeadDays: [7, 3, 1],       // send 7, 3, and 1 day before
  },
});
OptionTypeDefaultDescription
reminderSweepCronstring0 8 * * *Cron expression for the reminder sweep
reminderLeadDaysnumber[][7, 3, 1]Which days before expiry trigger a reminder. Sub expires June 20 → reminders on June 13, June 17, June 19

Event payload

"subscription.reminder": {
  customerId: string;
  subscriptionId: string;
  planId: string;
  planName: string;
  customerEmail: string | null;
  customerPhone: string | null;
  expiresAt: Date;
  daysUntilExpiry: number;
}

Examples

Email via Resend

import { resend } from "@birrjs/email-resend";

createBirr({
  plugins: [
    resend({
      apiKey: process.env.RESEND_API_KEY!,
      from: "BirrJS <noreply@birrjs.dev>",
      subscriptionReminder: {
        subject: "Your {plan} expires in {days} days",
        text: "Renew now: https://myapp.com/renew?sid={subscriptionId}",
      },
    }),
  ],
});

Custom handler

createBirr({
  on: {
    "subscription.reminder": async (payload) => {
      await resend.emails.send({
        to: payload.customerEmail,
        subject: `Your ${payload.planName} expires in ${payload.daysUntilExpiry} days`,
        text: `Renew here: https://myapp.com/renew?sid=${payload.subscriptionId}`,
      });

      if (payload.daysUntilExpiry === 1) {
        // Also send SMS for last-day reminder
        await sms.send(payload.customerPhone, "Last day! Renew now.");
      }
    },
  },
});

SMS via Afromessage

import { afromessage } from "@birrjs/sms-afromessage";

createBirr({
  plugins: [
    afromessage({
      apiKey: process.env.AFROMESSAGE_API_KEY!,
      sender: process.env.AFROMESSAGE_SENDER!,
      messages: {
        subscriptionReminder: "Your {plan} expires in {days} days. Renew now!",
      },
    }),
  ],
});

Scheduling modes

Reminders run in all scheduling modes:

ModeHow reminders run
AutoInternal cron (default daily at 8am UTC)
ExternalHTTP endpoint: POST /api/birrjs/send-reminders
ManualCall birrjs.sendReminders() directly

See Scheduling for mode setup and external auth.