Declare schedules in catalog-info.yaml. iec-cron POSTs to your endpoint on schedule. Return 200 immediately — do slow work asynchronously.
spec:
schedules:
- name: nightly-sync
cron: "0 2 * * *"
endpoint: /internal/cron/nightly-sync
timezone: America/Denver
timeoutMs: 300000
// Return 200 immediately — iec-cron must not wait for slow work
app.post('/internal/cron/nightly-sync', async (req, res) => {
res.json({ success: true })
// Do work asynchronously after responding
doSlowWork().catch((err) => logger.error({ err }, 'Nightly sync failed'))
})
// For fast work (< timeoutMs), you can do it inline:
app.post('/internal/cron/hourly-check', async (req, res) => {
await runQuickCheck()
res.json({ success: true })
})
POST {your-service-url}{endpoint}
X-Schedule-Name: nightly-sync
X-Cron-Expression: 0 2 * * *
X-Fired-At: 2026-01-15T07:00:00.000Z
┌──────── minute (0-59)
│ ┌────── hour (0-23)
│ │ ┌──── day of month (1-31)
│ │ │ ┌── month (1-12)
│ │ │ │ ┌ day of week (0-7, both 0 and 7 = Sunday)
│ │ │ │ │
"*/15 * * * *" every 15 minutes
"0 * * * *" top of every hour
"0 0 * * *" daily at midnight UTC
"0 9 * * 1-5" weekdays at 9am
"0 2 1 * *" 1st of each month at 2am
Only 5-part cron expressions. 6-part (with seconds) is NOT supported.
| Field | Required | Default | Description |
|---|---|---|---|
name | Yes | — | Unique name in your namespace |
cron | Yes | — | 5-part cron expression |
endpoint | Yes | — | Path in your service |
timezone | No | UTC | IANA timezone string |
timeoutMs | No | 30000 | HTTP callback timeout |
description | No | — | Human description |
| Environment | Cost per execution |
|---|---|
| Production | 2 tokens |
| Sandbox | 0 tokens (free) |
# ❌ WRONG: cron endpoints listed in routes (they must be internal-only)
spec:
routes:
- path: /internal/cron/nightly-sync # now exposed through Janus
# ✅ CORRECT: cron endpoints only in schedules
spec:
schedules:
- name: nightly-sync
endpoint: /internal/cron/nightly-sync
# ❌ WRONG: /api/ prefix on cron endpoints
endpoint: /api/cron/nightly-sync # suggests it's a public API route
# ✅ CORRECT
endpoint: /internal/cron/nightly-sync
# ❌ WRONG: 6-part cron (with seconds)
cron: "0 0 2 * * *" # second field not supported
# ✅ CORRECT
cron: "0 2 * * *"
// ❌ WRONG: blocking the response with slow work
app.post('/internal/cron/nightly-sync', async (req, res) => {
await doSlowWork() // iec-cron waits, times out, marks failure
res.json({ ok: true })
})
// ✅ CORRECT: respond immediately
app.post('/internal/cron/nightly-sync', async (req, res) => {
res.json({ ok: true })
doSlowWork().catch(logger.error)
})
Last updated: February 28, 2026