Skip to content

Cron ​

Register and manage scheduled background tasks. Cron jobs run on a recurring schedule defined by a cron expression.

Availability: Backend only (ctx.cron) Required Permission: cron

Methods ​

register(id: string, schedule: string, handler: () => Promise<void>): void ​

Register a handler for a scheduled job. The id is yours to choose and must be unique within your plugin.

Parameters:

NameTypeDescription
idstringJob ID, unique within your plugin
schedulestringCron expression (e.g. */30 * * * * for every 30 minutes)
handler() => Promise<void>Async function to run on each tick

Returns: Promise<void>

register rejects on an invalid cron expression or empty id

Await it. Typed as void this used to be an unhandled rejection in the host log, and the job simply never ran -- with nothing at the call site to tell you.

Example:

typescript
await ctx.cron.register('sync-data', '0 */6 * * *', async () => {
  ctx.log.info('Running 6-hourly data sync')
  const response = await ctx.http.fetch('https://api.example.com/data')
  const data = await response.json()
  await ctx.db.insert('synced_data', {
    timestamp: Date.now(),
    payload: data,
  })
})

await ctx.cron.register('cleanup', '0 3 * * *', async () => {
  ctx.log.info('Running daily cleanup at 3 AM')
  const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000 // 30 days
  await ctx.db.deleteWhere('logs', { before: cutoff })
})

unregister(id: string): void ​

Remove a registered cron handler. The job stops running and the id is forgotten; register it again from activate() on the next plugin start.

Parameters:

NameTypeDescription
idstringJob ID to unregister

Returns: Promise<void>

Example:

typescript
await ctx.cron.unregister('sync-data')

isRegistered(id: string): Promise<boolean> ​

Check whether a handler is currently registered for a given job ID.

Parameters:

NameTypeDescription
idstringJob ID to check

Returns: Promise<boolean> -- resolves true if a handler is registered.

AWAIT this

Every ctx.cron method crosses an RPC, so all three return promises. This one was typed as a bare boolean until 2026-08-11, and the example on this page was the un-awaited guard below -- which is always true, because a Promise is never falsy. Any plugin that copied it re-registered every time, or skipped the branch entirely.

typescript
// WRONG -- this condition can never be false
if (!ctx.cron.isRegistered('sync-data')) { /* dead code */ }

Example:

typescript
if (!(await ctx.cron.isRegistered('sync-data'))) {
  await ctx.cron.register('sync-data', '0 */6 * * *', syncHandler)
}

Cron Expression Format ​

Cron expressions use the standard 5-field format:

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

Common examples:

ExpressionDescription
*/5 * * * *Every 5 minutes
*/30 * * * *Every 30 minutes
0 * * * *Every hour
0 */6 * * *Every 6 hours
0 9 * * *Daily at 9:00 AM
0 3 * * *Daily at 3:00 AM
0 9 * * 1Every Monday at 9:00 AM
0 0 1 * *First day of every month at midnight

Notes ​

  • A cron job is registered at RUNTIME, from your plugin's activate(). There is no manifest block for jobs: the id you pass to register() is yours to choose and must be unique within your plugin.
  • If your handler throws an error, Omniscio logs the failure and the job will run again at the next scheduled tick. Errors do not disable the job.
  • Cron schedules use the system's local timezone.
  • Register your cron handlers in your plugin's activate() function so they start running as soon as the plugin loads.

Omniscio Plugin SDK