DocumentationGuidesEmail Workflows

Email Workflows

Common patterns for sending emails as background jobs.

Welcome Email

Send a welcome email when a user signs up:

jobs/send-welcome-email.ts
import { pidgey } from '@/lib/pidgey';
 
export const sendWelcomeEmail = pidgey.defineJob({
  name: 'send-welcome-email',
  handler: async (data: { userId: string; email: string }) => {
    const user = await db.users.findById(data.userId);
 
    await resend.emails.send({
      to: data.email,
      subject: `Welcome, ${user.name}!`,
      template: 'welcome',
    });
 
    return { sent: true };
  },
  config: {
    retries: 3,
    timeout: 30000,
  },
});
app/actions/signup.ts
'use server';
 
import { sendWelcomeEmail } from '@/jobs/send-welcome-email';
 
export async function signup(formData: FormData) {
  const email = formData.get('email') as string;
  const user = await db.users.create({ email });
 
  // Send welcome email in background
  await sendWelcomeEmail.enqueue({
    userId: user.id,
    email: user.email,
  });
 
  return { success: true };
}

Delayed Reminder

Send a follow-up email 24 hours after signup:

await sendFollowUpEmail.enqueue(
  { userId: user.id },
  { delay: 24 * 60 * 60 * 1000 } // 24 hours
);

Daily Digest

Use scheduled jobs for recurring emails:

jobs/daily-digest.ts
export const dailyDigest = pidgey.defineScheduledJob({
  name: 'daily-digest',
  handler: async () => {
    const users = await db.users.findMany({ digestEnabled: true });
 
    for (const user of users) {
      const activity = await getActivitySummary(user.id);
      await sendDigestEmail(user.email, activity);
    }
  },
  schedule: {
    cron: '0 9 * * *', // Every day at 9am
    timezone: 'America/New_York',
  },
});
💡
Remember to call pidgey.syncSchedules() at startup to register scheduled jobs.

Bulk Email with Rate Limiting

Process emails in batches to avoid rate limits:

export const sendBulkEmail = pidgey.defineJob({
  name: 'send-bulk-email',
  handler: async (data: { emails: string[]; template: string }) => {
    for (const email of data.emails) {
      await resend.emails.send({
        to: email,
        template: data.template,
      });
      // Small delay between emails
      await new Promise((r) => setTimeout(r, 100));
    }
  },
  config: {
    queue: 'bulk', // Separate queue for bulk operations
    timeout: 600000, // 10 minute timeout
  },
});

Run bulk queue with lower concurrency:

npx pidgey worker start --queue bulk --concurrency 1