Prerequisites
- Node.js 18+
- Redis 7.4+ (Railway or self-hosted; must be
noevictionpolicy) - A Rotor workspace key (
rt_ws_...)
Install
pnpm add @rotorsh/sdk bullmq ioredisWarning
Rotor pins a specific BullMQ major version. Check your package.json after
install — if bullmq is a different major than @rotorsh/sdk expects, you'll
get a startup warning. Pin to the version listed in @rotorsh/sdk's peer
dependencies.
The 5 Resources
1. Queues
Create and inspect queues:
import { Rotor } from "@rotorsh/sdk";
const rotor = new Rotor({ apiKey: process.env.ROTOR_API_KEY! });
// Create a queue
const queue = await rotor.queues.create({
name: "outreach",
retry_attempts: 3,
});
// List queues
const queues = await rotor.queues.list();
console.log(queues.data.map((q) => q.name));
// Get queue stats
const stats = await rotor.queues.stats("outreach");
console.log(stats.waiting, stats.active, stats.completed, stats.failed);2. Jobs
Enqueue individual or batch jobs:
// Single job
const job = await rotor.jobs.enqueue("outreach", {
payload: { leadId: "lead_123" },
});
// Batch (quota checked atomically — all-or-nothing)
const jobs = await rotor.jobs.addBatch("outreach", [
{ payload: { leadId: "lead_001" } },
{ payload: { leadId: "lead_002" } },
{ payload: { leadId: "lead_003" } },
]);
// Get job status
const status = await rotor.jobs.get("outreach", job.id);
console.log(status.state); // waiting | active | completed | failed | delayed3. Schedules
Recurring jobs with cron syntax:
// Create a recurring enrichment job
const schedule = await rotor.schedules.create({
queue: "enrichment",
name: "nightly-enrichment",
cron: "0 2 * * *", // 2am UTC daily
payload: { source: "clearbit" },
timezone: "UTC",
});
// Pause / resume via update
await rotor.schedules.update(schedule.id, { enabled: false });
await rotor.schedules.update(schedule.id, { enabled: true });4. Status
Check workspace health and quota usage in a single call:
const status = await rotor.status.get();
// {
// redis: 'ok',
// api: 'ok',
// version: '1.0.0',
// quota: { used: 12450, limit: 100000, plan: 'pro', resetAt: '2025-05-01T00:00:00Z' }
// }5. Usage
Billing-period usage breakdown:
const usage = await rotor.usage.current();
// { period: { from, to }, executions: { total, byQueue: {...} } }Idempotent Handler Pattern
Prevent duplicate outreach when jobs retry:
import { RotorWorker } from "@rotorsh/sdk";
const worker = new RotorWorker({
workspaceId: process.env.WORKSPACE_ID!,
queueName: "outreach",
connection: process.env.REDIS_URL!,
concurrency: 5,
processor: async (job) => {
const { leadId } = job.data.payload;
// Use job.id as idempotency key for downstream APIs
const alreadySent = await db.outreach.findUnique({
where: { rotorJobId: job.id },
});
if (alreadySent) {
return { skipped: true, reason: "already-sent" };
}
await sendEmail(leadId);
await db.outreach.create({
data: { rotorJobId: job.id, leadId, sentAt: new Date() },
});
return { sent: true };
},
});
await worker.ready();SIGTERM Drain
Graceful shutdown — in-flight jobs complete before the process exits:
const worker = new RotorWorker({
workspaceId: process.env.WORKSPACE_ID!,
queueName: "outreach",
connection: process.env.REDIS_URL!,
concurrency: 5,
drainTimeout: 30_000, // wait up to 30s for in-flight jobs
processor: handler,
});
// RotorWorker registers SIGTERM/SIGINT handlers automatically.
// On Railway/Fly.io/Heroku: deploy sends SIGTERM → worker finishes
// current jobs → process exits cleanly. No jobs are lost.
await worker.ready();Railway Deploy Notes
- Set
ROTOR_API_KEYin Railway environment variables - Set
REDIS_URLto your Railway Redis private URL (useredis://...) - Ensure Redis Memory Policy is
noeviction:railway run redis-cli CONFIG SET maxmemory-policy noeviction - Worker
Procfileor start command:node dist/worker.js - Deploy: Railway will send SIGTERM on deploys — SIGTERM drain ensures zero job loss
Next Steps
- CLI Reference — all
rotorcommands - Job Tags — label and filter jobs by campaign or contact
- Concurrency Keys — prevent duplicate concurrent runs
- Failure Alerts — get notified when jobs terminally fail
- Idempotency — two-layer dedup strategy
- Webhooks — receive HTTP callbacks on job lifecycle events
- API Reference — all REST endpoints