JavaScript/TypeScript SDK

The official JavaScript/TypeScript SDK for Locci Scheduler provides a type-safe, promise-based API for scheduling webhook tasks.

Installation

npm install @locci/scheduler

Or with other package managers:

bun add @locci/scheduler
pnpm add @locci/scheduler
yarn add @locci/scheduler

Quick Start

import { LocciScheduler } from "@locci/scheduler";

const scheduler = new LocciScheduler({
  baseUrl: "https://api.scheduler.locci.cloud",
  apiToken: "your-api-key",
});

// Create a one-time task
const task = await scheduler.scheduleOnce({
  name: "My First Task",
  webhook: {
    url: "https://your-app.com/webhook",
    method: "POST",
    payload: { message: "Hello!" },
  },
  executeAt: new Date(Date.now() + 5 * 60 * 1000), // 5 minutes from now
});

console.log("Task created:", task.id);

Configuration

import { LocciScheduler } from "@locci/scheduler";

const scheduler = new LocciScheduler({
  baseUrl: "https://api.scheduler.locci.cloud", // API base URL
  apiToken: "your-api-key",                     // Your API key from the console
  timeout: 30000,                               // Request timeout in ms (default: 30000)
  retries: 3,                                   // Number of retries (default: 3)
});

Environment Variables

For production, use environment variables:

import dotenv from "dotenv";
dotenv.config();

const scheduler = new LocciScheduler({
  baseUrl: process.env.LOCCI_SCHEDULER_URL,
  apiToken: process.env.LOCCI_SCHEDULER_TOKEN,
});

Task Management

Create a Task

const task = await scheduler.createTask({
  name: "Daily Report",
  description: "Send daily sales report",
  webhook: {
    url: "https://your-app.com/api/reports",
    method: "POST",
    headers: {
      "X-Custom-Header": "value",
    },
    payload: {
      reportType: "sales",
      period: "daily",
    },
    timeoutSeconds: 30,
  },
  schedule: {
    type: "recurring",
    cronExpression: "0 9 * * *", // Every day at 9 AM
    timezone: "Africa/Nairobi",
  },
  metadata: {
    department: "sales",
    priority: "high",
  },
});

Get a Task

const task = await scheduler.getTask("task-id-here");
console.log(task.name, task.status);

List Tasks

const taskList = await scheduler.listTasks({
  page: 1,
  perPage: 10,
});

console.log(`Total tasks: ${taskList.total}`);
taskList.tasks.forEach((task) => {
  console.log(`- ${task.name} (${task.status})`);
});

Update a Task

const updatedTask = await scheduler.updateTask("task-id", {
  name: "Updated Task Name",
  description: "New description",
});

Delete a Task

await scheduler.deleteTask("task-id");

Task Lifecycle

Pause a Task

const task = await scheduler.pauseTask("task-id");
console.log(task.status); // "Paused"

Resume a Task

const task = await scheduler.resumeTask("task-id");
console.log(task.status); // "Active"

Cancel a Task

const task = await scheduler.cancelTask("task-id");
console.log(task.status); // "Cancelled"

Manually Trigger a Task

await scheduler.triggerTask("task-id");

Scheduling Methods

One-Time Execution

const task = await scheduler.scheduleOnce({
  name: "Send Welcome Email",
  webhook: {
    url: "https://your-app.com/api/emails/welcome",
    method: "POST",
    payload: { userId: "user123" },
  },
  executeAt: new Date("2024-12-25T09:00:00Z"),
  description: "Send welcome email to new user",
  metadata: { userId: "user123" },
});

Recurring with Cron Expression

const task = await scheduler.scheduleRecurring({
  name: "Weekly Backup",
  webhook: {
    url: "https://your-app.com/api/backup",
    method: "POST",
  },
  cronExpression: "0 2 * * 0", // Sundays at 2 AM
  timezone: "UTC",
  endDate: new Date("2025-12-31"), // Optional end date
});

Interval-Based

const task = await scheduler.scheduleInterval({
  name: "Health Check",
  webhook: {
    url: "https://your-app.com/health",
    method: "GET",
    timeoutSeconds: 10,
  },
  intervalSeconds: 300, // Every 5 minutes
  startAt: new Date(),  // Start immediately
});

Daily at Specific Time

const task = await scheduler.scheduleDaily({
  name: "Daily Sales Report",
  webhook: {
    url: "https://your-app.com/api/reports/daily",
    method: "POST",
  },
  time: "09:00", // 9:00 AM (24-hour format)
  timezone: "Africa/Nairobi",
});

Weekly

const task = await scheduler.scheduleWeekly({
  name: "Weekly Team Standup Reminder",
  webhook: {
    url: "https://your-app.com/api/slack/notify",
    method: "POST",
    payload: { message: "Team standup in 15 minutes!" },
  },
  dayOfWeek: 1, // Monday (0 = Sunday)
  time: "09:45",
  timezone: "Africa/Nairobi",
});

Convenience Methods

// Every minute
await scheduler.scheduleEveryMinute({
  name: "Minute Task",
  webhook: { url: "https://example.com/ping", method: "GET" },
});

// Every hour
await scheduler.scheduleEveryHour({
  name: "Hourly Task",
  webhook: { url: "https://example.com/hourly", method: "POST" },
});

Webhook Configuration

Basic Webhook

const webhook = {
  url: "https://your-app.com/webhook",
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Custom-Header": "value",
  },
  payload: {
    key: "value",
  },
  timeoutSeconds: 30,
};

With Retry Configuration

const webhook = {
  url: "https://your-app.com/webhook",
  method: "POST",
  retryConfig: {
    maxAttempts: 5,
    backoffSeconds: 2,
    backoffMultiplier: 2.0,
    maxBackoffSeconds: 300,
  },
};

Authentication Types

API Key

const webhook = {
  url: "https://your-app.com/webhook",
  method: "POST",
  authentication: {
    type: "apiKey",
    apiKey: {
      headerName: "X-API-Key",
      key: "your-api-key-value",
    },
  },
};

Bearer Token

const webhook = {
  url: "https://your-app.com/webhook",
  method: "POST",
  authentication: {
    type: "bearerToken",
    bearerToken: {
      token: "your-bearer-token",
    },
  },
};

Basic Auth

const webhook = {
  url: "https://your-app.com/webhook",
  method: "POST",
  authentication: {
    type: "basicAuth",
    basicAuth: {
      username: "user",
      password: "password",
    },
  },
};

HMAC Webhook Signature

For secure webhook verification, use webhookSecret:

const webhook = {
  url: "https://your-app.com/webhook",
  method: "POST",
  payload: { data: "sensitive" },
  webhookSecret: "your-webhook-secret",
};

The scheduler will include an X-Locci-Signature header with an HMAC-SHA256 signature of the payload. Verify it on your server:

import crypto from "crypto";

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(JSON.stringify(payload))
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

Execution History

const history = await scheduler.getTaskHistory("task-id");

console.log(`Total executions: ${history.total}`);
history.executions.forEach((exec) => {
  console.log(`- ${exec.executedAt}: ${exec.status}`);
  if (exec.errorMessage) {
    console.log(`  Error: ${exec.errorMessage}`);
  }
});

Error Handling

The SDK provides specific error classes for different scenarios:

import {
  SchedulerError,
  AuthenticationError,
  ValidationError,
  NotFoundError,
  RateLimitError,
} from "@locci/scheduler";

try {
  const task = await scheduler.createTask({
    // ... task configuration
  });
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error("Authentication failed - check your API key");
  } else if (error instanceof ValidationError) {
    console.error("Invalid task configuration:", error.details);
  } else if (error instanceof NotFoundError) {
    console.error("Resource not found");
  } else if (error instanceof RateLimitError) {
    console.error("Rate limit exceeded - please wait and retry");
  } else if (error instanceof SchedulerError) {
    console.error(`API error (${error.statusCode}):`, error.message);
  } else {
    console.error("Unexpected error:", error);
  }
}

TypeScript Types

All types are exported from the package:

import type {
  Task,
  TaskExecution,
  TaskStatus,
  ExecutionStatus,
  SchedulerConfig,
  CreateTaskOptions,
  UpdateTaskOptions,
  WebhookConfig,
  ScheduleConfig,
  AuthConfig,
  RetryConfig,
  HttpMethod,
  PaginationOptions,
  TaskListResponse,
  TaskHistoryResponse,
} from "@locci/scheduler";

Type Definitions

// Task status values
type TaskStatus = "Active" | "Paused" | "Completed" | "Failed" | "Cancelled";

// Execution status values
type ExecutionStatus = "Success" | "Failed" | "Retrying" | "TimedOut";

// HTTP methods
type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";

// Schedule types
type ScheduleType = "once" | "recurring" | "interval";

Common Use Cases

User Onboarding Email Sequence

async function scheduleOnboardingEmails(userId: string, email: string) {
  const baseTime = new Date();

  // Welcome email - 2 minutes after signup
  await scheduler.scheduleOnce({
    name: `Welcome Email - ${userId}`,
    webhook: {
      url: "https://your-app.com/api/emails/send",
      method: "POST",
      payload: { to: email, template: "welcome", userId },
    },
    executeAt: new Date(baseTime.getTime() + 2 * 60 * 1000),
    metadata: { userId, emailType: "welcome" },
  });

  // Tips email - 1 day after signup
  await scheduler.scheduleOnce({
    name: `Tips Email - ${userId}`,
    webhook: {
      url: "https://your-app.com/api/emails/send",
      method: "POST",
      payload: { to: email, template: "tips", userId },
    },
    executeAt: new Date(baseTime.getTime() + 24 * 60 * 60 * 1000),
    metadata: { userId, emailType: "tips" },
  });

  // Follow-up email - 3 days after signup
  await scheduler.scheduleOnce({
    name: `Follow-up Email - ${userId}`,
    webhook: {
      url: "https://your-app.com/api/emails/send",
      method: "POST",
      payload: { to: email, template: "followup", userId },
    },
    executeAt: new Date(baseTime.getTime() + 3 * 24 * 60 * 60 * 1000),
    metadata: { userId, emailType: "followup" },
  });
}

Health Monitoring

async function setupHealthMonitoring(services: string[]) {
  for (const service of services) {
    await scheduler.scheduleInterval({
      name: `Health Check - ${service}`,
      webhook: {
        url: `https://${service}.your-domain.com/health`,
        method: "GET",
        timeoutSeconds: 10,
      },
      intervalSeconds: 60, // Check every minute
      metadata: { service, type: "health-check" },
    });
  }
}

Subscription Renewal Reminders

async function scheduleRenewalReminder(
  userId: string,
  email: string,
  renewalDate: Date
) {
  // Reminder 7 days before
  const reminderDate = new Date(renewalDate.getTime() - 7 * 24 * 60 * 60 * 1000);

  await scheduler.scheduleOnce({
    name: `Renewal Reminder - ${userId}`,
    webhook: {
      url: "https://your-app.com/api/emails/send",
      method: "POST",
      payload: {
        to: email,
        template: "renewal_reminder",
        userId,
        renewalDate: renewalDate.toISOString(),
      },
    },
    executeAt: reminderDate,
    metadata: { userId, type: "renewal_reminder" },
  });
}

Resources