Real-world examples and use cases for the CallMeLater SDK
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");
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,
});
};
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
});
};
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))
)
);
};
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),
});
};
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),
});
};
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
}
};
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);