SDK Examples

This page provides practical examples and common use cases for the CallMeLater JavaScript SDK. These examples demonstrate real-world scenarios where scheduled HTTP requests can solve business problems.

User Onboarding Flow

Schedule a series of onboarding emails and check-ins for new users:
import { CallMeLater } from "@callmelater/sdk";

const callMeLater = new CallMeLater({
  apiKey: process.env.CALLMELATER_API_KEY,
});

const scheduleOnboardingFlow = async (userId, userEmail) => {
  const baseUrl = "https://api.yourapp.com";
  const now = new Date();

  // Welcome email - immediate
  await callMeLater.schedule({
    targetUrl: `${baseUrl}/emails/welcome`,
    targetMethod: "POST",
    targetHeaders: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.EMAIL_SERVICE_TOKEN}`,
    },
    targetBody: {
      userId,
      email: userEmail,
      template: "welcome",
    },
    triggerAt: new Date(now.getTime() + 30000), // 30 seconds delay
  });

  // Feature introduction - 1 day later
  await callMeLater.schedule({
    targetUrl: `${baseUrl}/emails/features`,
    targetMethod: "POST",
    targetHeaders: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.EMAIL_SERVICE_TOKEN}`,
    },
    targetBody: {
      userId,
      email: userEmail,
      template: "features",
    },
    triggerAt: new Date(now.getTime() + 24 * 60 * 60 * 1000), // 1 day
  });

  // Check engagement - 3 days later
  await callMeLater.schedule({
    targetUrl: `${baseUrl}/analytics/engagement-check`,
    targetMethod: "POST",
    targetHeaders: {
      "Content-Type": "application/json",
    },
    targetBody: {
      userId,
      checkType: "onboarding_engagement",
    },
    triggerAt: new Date(now.getTime() + 3 * 24 * 60 * 60 * 1000), // 3 days
  });

  // Follow-up survey - 1 week later
  await callMeLater.schedule({
    targetUrl: `${baseUrl}/emails/survey`,
    targetMethod: "POST",
    targetHeaders: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.EMAIL_SERVICE_TOKEN}`,
    },
    targetBody: {
      userId,
      email: userEmail,
      template: "onboarding_survey",
    },
    triggerAt: new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000), // 1 week
  });
};

// Usage
await scheduleOnboardingFlow("user_123", "user@example.com");

Subscription Renewal Reminders

Remind users about upcoming subscription renewals:
const scheduleRenewalReminders = async (subscription) => {
  const { userId, renewalDate, planType } = subscription;
  const renewal = new Date(renewalDate);

  // 7 days before renewal
  await callMeLater.schedule({
    targetUrl: "https://api.yourapp.com/notifications/renewal-reminder",
    targetMethod: "POST",
    targetBody: {
      userId,
      reminderType: "7_day",
      planType,
      renewalDate: renewalDate,
      message: "Your subscription renews in 7 days",
    },
    triggerAt: new Date(renewal.getTime() - 7 * 24 * 60 * 60 * 1000),
  });

  // 1 day before renewal
  await callMeLater.schedule({
    targetUrl: "https://api.yourapp.com/notifications/renewal-reminder",
    targetMethod: "POST",
    targetBody: {
      userId,
      reminderType: "1_day",
      planType,
      renewalDate: renewalDate,
      message: "Your subscription renews tomorrow",
    },
    triggerAt: new Date(renewal.getTime() - 24 * 60 * 60 * 1000),
  });

  // On renewal day
  await callMeLater.schedule({
    targetUrl: "https://api.yourapp.com/billing/process-renewal",
    targetMethod: "POST",
    targetBody: {
      userId,
      planType,
      subscriptionId: subscription.id,
    },
    triggerAt: renewal,
  });
};

E-commerce Order Follow-up

Schedule follow-up actions after order completion:
const scheduleOrderFollowUp = async (order) => {
  const { orderId, customerId, customerEmail, orderDate } = order;
  const orderTime = new Date(orderDate);

  // Shipping confirmation - 1 hour after order
  await callMeLater.schedule({
    targetUrl: "https://api.yourstore.com/orders/check-shipping",
    targetMethod: "POST",
    targetBody: {
      orderId,
      action: "check_shipping_status",
    },
    triggerAt: new Date(orderTime.getTime() + 60 * 60 * 1000), // 1 hour
  });

  // Delivery reminder - 2 days after order
  await callMeLater.schedule({
    targetUrl: "https://api.yourstore.com/emails/delivery-update",
    targetMethod: "POST",
    targetBody: {
      orderId,
      customerId,
      email: customerEmail,
      template: "delivery_update",
    },
    triggerAt: new Date(orderTime.getTime() + 2 * 24 * 60 * 60 * 1000), // 2 days
  });

  // Review request - 1 week after order
  await callMeLater.schedule({
    targetUrl: "https://api.yourstore.com/emails/review-request",
    targetMethod: "POST",
    targetBody: {
      orderId,
      customerId,
      email: customerEmail,
      template: "review_request",
    },
    triggerAt: new Date(orderTime.getTime() + 7 * 24 * 60 * 60 * 1000), // 1 week
  });
};

Meeting and Appointment Reminders

Send reminders for scheduled meetings:
const scheduleMeetingReminders = async (meeting) => {
  const { meetingId, attendees, startTime, title } = meeting;
  const meetingDate = new Date(startTime);

  // 24 hours before
  const reminder24h = await callMeLater.schedule({
    targetUrl: "https://api.yourapp.com/meetings/send-reminder",
    targetMethod: "POST",
    targetBody: {
      meetingId,
      attendees,
      reminderType: "24_hour",
      meetingTitle: title,
      meetingTime: startTime,
    },
    triggerAt: new Date(meetingDate.getTime() - 24 * 60 * 60 * 1000),
  });

  // 1 hour before
  const reminder1h = await callMeLater.schedule({
    targetUrl: "https://api.yourapp.com/meetings/send-reminder",
    targetMethod: "POST",
    targetBody: {
      meetingId,
      attendees,
      reminderType: "1_hour",
      meetingTitle: title,
      meetingTime: startTime,
    },
    triggerAt: new Date(meetingDate.getTime() - 60 * 60 * 1000),
  });

  // 15 minutes before
  const reminder15m = await callMeLater.schedule({
    targetUrl: "https://api.yourapp.com/meetings/send-reminder",
    targetMethod: "POST",
    targetBody: {
      meetingId,
      attendees,
      reminderType: "15_minute",
      meetingTitle: title,
      meetingTime: startTime,
    },
    triggerAt: new Date(meetingDate.getTime() - 15 * 60 * 1000),
  });

  return {
    reminder24h: reminder24h.scheduleId,
    reminder1h: reminder1h.scheduleId,
    reminder15m: reminder15m.scheduleId,
  };
};

// Cancel reminders if meeting is cancelled
const cancelMeetingReminders = async (reminderIds) => {
  await Promise.all(
    Object.values(reminderIds).map((id) =>
      callMeLater
        .cancel(id)
        .catch((err) => console.log(`Could not cancel ${id}:`, err.message))
    )
  );
};

Content Publishing Workflow

Schedule content publication and social media posts:
const scheduleContentWorkflow = async (content) => {
  const { postId, publishDate, socialAccounts } = content;
  const publishTime = new Date(publishDate);

  // Publish main content
  await callMeLater.schedule({
    targetUrl: "https://api.yourblog.com/posts/publish",
    targetMethod: "POST",
    targetBody: {
      postId,
      status: "published",
    },
    triggerAt: publishTime,
  });

  // Schedule social media posts 30 minutes after main publication
  const socialDelay = 30 * 60 * 1000; // 30 minutes

  for (const account of socialAccounts) {
    await callMeLater.schedule({
      targetUrl: `https://api.yourblog.com/social/${account.platform}/post`,
      targetMethod: "POST",
      targetHeaders: {
        Authorization: `Bearer ${account.token}`,
      },
      targetBody: {
        postId,
        accountId: account.id,
        message: content.socialMessage,
        link: content.url,
      },
      triggerAt: new Date(publishTime.getTime() + socialDelay),
    });
  }

  // Schedule analytics review 24 hours later
  await callMeLater.schedule({
    targetUrl: "https://api.yourblog.com/analytics/review",
    targetMethod: "POST",
    targetBody: {
      postId,
      action: "generate_24h_report",
    },
    triggerAt: new Date(publishTime.getTime() + 24 * 60 * 60 * 1000),
  });
};

Trial Expiration Management

Handle free trial expirations with grace period:
const scheduleTrialExpiration = async (user) => {
  const { userId, trialEndDate, email } = user;
  const trialEnd = new Date(trialEndDate);

  // 3 days before trial ends
  await callMeLater.schedule({
    targetUrl: "https://api.yourapp.com/emails/trial-ending",
    targetMethod: "POST",
    targetBody: {
      userId,
      email,
      daysRemaining: 3,
      template: "trial_ending_3_days",
    },
    triggerAt: new Date(trialEnd.getTime() - 3 * 24 * 60 * 60 * 1000),
  });

  // 1 day before trial ends
  await callMeLater.schedule({
    targetUrl: "https://api.yourapp.com/emails/trial-ending",
    targetMethod: "POST",
    targetBody: {
      userId,
      email,
      daysRemaining: 1,
      template: "trial_ending_1_day",
    },
    triggerAt: new Date(trialEnd.getTime() - 24 * 60 * 60 * 1000),
  });

  // Trial expired - downgrade account
  await callMeLater.schedule({
    targetUrl: "https://api.yourapp.com/accounts/downgrade",
    targetMethod: "POST",
    targetBody: {
      userId,
      reason: "trial_expired",
      newPlan: "free",
    },
    triggerAt: trialEnd,
  });

  // Grace period email - 1 day after expiration
  await callMeLater.schedule({
    targetUrl: "https://api.yourapp.com/emails/trial-expired",
    targetMethod: "POST",
    targetBody: {
      userId,
      email,
      template: "trial_expired_grace",
    },
    triggerAt: new Date(trialEnd.getTime() + 24 * 60 * 60 * 1000),
  });
};

Batch Processing and Cleanup

Schedule regular maintenance and cleanup tasks:
const scheduleDailyMaintenance = async () => {
  const tomorrow9AM = new Date();
  tomorrow9AM.setDate(tomorrow9AM.getDate() + 1);
  tomorrow9AM.setHours(9, 0, 0, 0);

  // Database cleanup
  await callMeLater.schedule({
    targetUrl: "https://api.yourapp.com/maintenance/cleanup-database",
    targetMethod: "POST",
    targetBody: {
      tasks: ["delete_old_logs", "optimize_tables", "update_statistics"],
    },
    triggerAt: tomorrow9AM,
  });

  // Generate daily reports
  await callMeLater.schedule({
    targetUrl: "https://api.yourapp.com/reports/daily",
    targetMethod: "POST",
    targetBody: {
      reportDate: tomorrow9AM.toISOString().split("T")[0],
      recipients: ["admin@yourapp.com", "analytics@yourapp.com"],
    },
    triggerAt: new Date(tomorrow9AM.getTime() + 30 * 60 * 1000), // 30 minutes after cleanup
  });

  // Backup data
  await callMeLater.schedule({
    targetUrl: "https://api.yourapp.com/backups/create",
    targetMethod: "POST",
    targetBody: {
      backupType: "daily",
      retention: "30_days",
    },
    triggerAt: new Date(tomorrow9AM.getTime() + 60 * 60 * 1000), // 1 hour after cleanup
  });
};

// Schedule for the next 30 days
const scheduleMonthlyMaintenance = async () => {
  for (let i = 0; i < 30; i++) {
    setTimeout(async () => {
      await scheduleDailyMaintenance();
    }, i * 24 * 60 * 60 * 1000); // Spread out over 30 days
  }
};

Error Handling and Monitoring

Example with comprehensive error handling and monitoring:
class ScheduleManager {
  constructor(apiKey) {
    this.callMeLater = new CallMeLater({ apiKey });
    this.retryAttempts = 3;
  }

  async scheduleWithRetry(requestData, context = {}) {
    for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
      try {
        const result = await this.callMeLater.schedule(requestData);

        // Log successful scheduling
        console.log(
          `Successfully scheduled ${context.type || "request"}:`,
          result.scheduleId
        );

        return result;
      } catch (error) {
        console.error(
          `Attempt ${attempt} failed for ${context.type || "request"}:`,
          error.message
        );

        // Don't retry certain errors
        if (error.status === 401 || error.status === 402) {
          throw new Error(`Cannot retry: ${error.message}`);
        }

        // If this was the last attempt, throw the error
        if (attempt === this.retryAttempts) {
          throw error;
        }

        // Wait before retrying (exponential backoff)
        const delay = Math.pow(2, attempt) * 1000;
        await new Promise((resolve) => setTimeout(resolve, delay));
      }
    }
  }

  async scheduleUserNotification(userId, notification) {
    try {
      return await this.scheduleWithRetry(
        {
          targetUrl: "https://api.yourapp.com/notifications/send",
          targetMethod: "POST",
          targetBody: {
            userId,
            ...notification,
          },
          triggerAt: notification.scheduledTime,
        },
        { type: "user_notification", userId }
      );
    } catch (error) {
      // Fallback: schedule a system notification about the failure
      await this.scheduleWithRetry(
        {
          targetUrl: "https://api.yourapp.com/alerts/scheduling-failure",
          targetMethod: "POST",
          targetBody: {
            originalRequest: notification,
            error: error.message,
            timestamp: new Date().toISOString(),
          },
          triggerAt: new Date(Date.now() + 60000), // 1 minute from now
        },
        { type: "error_alert" }
      );

      throw error;
    }
  }

  async monitorAndAlert() {
    try {
      const credits = await this.callMeLater.getCredits();
      const stats = await this.callMeLater.getStats();

      // Alert if credits are low
      if (credits < 50) {
        await this.scheduleWithRetry(
          {
            targetUrl: "https://api.yourapp.com/alerts/low-credits",
            targetMethod: "POST",
            targetBody: {
              creditsRemaining: credits,
              severity: credits < 10 ? "critical" : "warning",
            },
            triggerAt: new Date(Date.now() + 30000), // 30 seconds from now
          },
          { type: "credit_alert" }
        );
      }

      // Alert if failure rate is high
      const failureRate = stats.failedInvocations / stats.totalInvocations;
      if (failureRate > 0.1) {
        // More than 10% failure rate
        await this.scheduleWithRetry(
          {
            targetUrl: "https://api.yourapp.com/alerts/high-failure-rate",
            targetMethod: "POST",
            targetBody: {
              failureRate: failureRate * 100,
              period: stats.timeRange,
            },
            triggerAt: new Date(Date.now() + 30000),
          },
          { type: "failure_rate_alert" }
        );
      }
    } catch (error) {
      console.error("Failed to monitor stats:", error.message);
    }
  }
}

// Usage
const scheduler = new ScheduleManager(process.env.CALLMELATER_API_KEY);

// Run monitoring every hour
setInterval(() => {
  scheduler.monitorAndAlert();
}, 60 * 60 * 1000);
These examples demonstrate practical, real-world scenarios where the CallMeLater SDK can provide significant value. Each example includes error handling, logging, and follows best practices for production applications.