Cron-Based Scheduling

Cron expression syntax in Paperclip, setting per-agent schedules (*/5 * * * * vs 0 * * * *), the global cadence default, and how the scheduler service polls for due agents.

Configuring the Heartbeat Schedule

What you'll learn

Set a custom cron expression for your company's heartbeat, understand how Paperclip implements the cron internally, and choose the right interval for your use case.

The Default Schedule

Out of the box, Paperclip runs the heartbeat every 5 minutes:

*/5 * * * *

This is a reasonable default for development and moderate-traffic production. Adjust it in Company Settings → Heartbeat Schedule.

Cron Expression Reference

┌──────────── minute (0-59)
│  ┌─────────── hour (0-23)
│  │  ┌──────── day of month (1-31)
│  │  │  ┌───── month (1-12)
│  │  │  │  ┌── day of week (0-6, Sun=0)
│  │  │  │  │
*  *  *  *  *

Common Patterns

Expression Meaning
*/5 * * * * Every 5 minutes (default)
*/1 * * * * Every minute (dev/testing only)
*/15 * * * * Every 15 minutes (conservative)
0 * * * * Every hour on the hour
0 9-17 * * 1-5 Business hours only (9am–5pm, Mon–Fri)
0 0 * * * Daily at midnight
0 6 * * 1 Weekly, Monday at 6am

Business-Hours-Only Operation

If agents are expensive to run and the tasks aren't urgent, confine the heartbeat to business hours:

0 9-17 * * 1-5

Issues created outside business hours queue up and are processed when the heartbeat resumes at 09:00 Monday.

Important: check timezones. Paperclip stores and evaluates cron expressions in UTC. Convert your local timezone:
- UTC+1 (BST): business hours 9am–5pm BST = 8am–4pm UTC → 0 8-16 * * 1-5
- UTC-5 (EST): 9am–5pm EST = 2pm–10pm UTC → 0 14-22 * * 1-5

How Paperclip Implements the Cron

Internally, pnpm dev starts the API server and also schedules the heartbeat using node-cron:

// packages/server/src/lib/heartbeat/scheduler.ts
import cron from 'node-cron';

export function startHeartbeatScheduler(company: Company) {
  const expression = company.heartbeat_cron ?? '*/5 * * * *';
  cron.schedule(expression, async () => {
    await runHeartbeatTick(company.id);
  });
}

In production (Docker/PM2) the same scheduler runs inside the server process. You do not need an external cron daemon.

Disabling the Heartbeat

Set heartbeat_enabled = false in Company Settings to pause all automatic execution. Issues continue to queue; runs can still be triggered manually via the API.

Manual Trigger vs Cron

Method When to use
Cron schedule Normal operation; automatic processing
Manual trigger via API Development, testing, one-off runs
Manual trigger via UI Debug a specific issue; check agent output

Checkpoint ✓

Sign in for progress tracking & Q&A →