Skip to main content
Cron triggers allow you to schedule workflows to run at specific times or intervals using standard cron expressions.

Basic usage

import { step, cron } from 'motia'

export const config = step({
  name: 'daily-report',
  triggers: [cron('0 9 * * *')], // Every day at 9 AM
})

export const handler = async (input, ctx) => {
  ctx.logger.info('Generating daily report')
  
  const report = await generateReport()
  
  ctx.logger.info('Report generated', { records: report.length })
}

Cron expression syntax

Cron expressions use five fields:
┌───────────── minute (0 - 59)
│ ┌───────────── hour (0 - 23)
│ │ ┌───────────── day of month (1 - 31)
│ │ │ ┌───────────── month (1 - 12)
│ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday)
│ │ │ │ │
* * * * *

Common patterns

Every minute

import { step, cron } from 'motia'

export const config = step({
  name: 'health-check',
  triggers: [cron('* * * * *')],
})

export const handler = async (input, ctx) => {
  await performHealthCheck()
}

Every hour

export const config = step({
  name: 'hourly-sync',
  triggers: [cron('0 * * * *')],
})

Every day at specific time

export const config = step({
  name: 'daily-backup',
  triggers: [cron('0 2 * * *')], // 2 AM every day
})

Every weekday

export const config = step({
  name: 'weekday-report',
  triggers: [cron('0 9 * * 1-5')], // 9 AM Monday-Friday
})

First day of month

export const config = step({
  name: 'monthly-invoice',
  triggers: [cron('0 0 1 * *')], // Midnight on the 1st
})

Every 15 minutes

export const config = step({
  name: 'frequent-poll',
  triggers: [cron('*/15 * * * *')],
})

Conditional execution

Use conditions to skip executions based on runtime logic:
import { step, cron } from 'motia'

export const config = step({
  name: 'conditional-task',
  triggers: [
    cron('0 * * * *', async (input, ctx) => {
      const hour = new Date().getHours()
      // Only run during business hours
      return hour >= 9 && hour <= 17
    }),
  ],
})

export const handler = async (input, ctx) => {
  ctx.logger.info('Running during business hours')
}

Enqueuing from cron

Trigger async workflows from scheduled jobs:
import { step, cron } from 'motia'

export const config = step({
  name: 'schedule-reports',
  triggers: [cron('0 6 * * *')], // 6 AM daily
  enqueues: ['generate-report'],
})

export const handler = async (input, ctx) => {
  const users = await getActiveUsers()
  
  for (const user of users) {
    await ctx.enqueue({
      topic: 'generate-report',
      data: { userId: user.id },
    })
  }
  
  ctx.logger.info('Queued reports', { count: users.length })
}

Configuration options

expression
string
required
Standard cron expression with five fields:
  • Minute (0-59)
  • Hour (0-23)
  • Day of month (1-31)
  • Month (1-12)
  • Day of week (0-6, Sunday-Saturday)
Supports:
  • * for any value
  • , for lists (e.g., 1,15,30)
  • - for ranges (e.g., 1-5)
  • / for steps (e.g., */15)
condition
function
Optional function (input, ctx) => boolean to conditionally execute the handler

Use cases

Daily data sync

Sync data from external systems:
import { step, cron } from 'motia'

export const config = step({
  name: 'sync-users',
  triggers: [cron('0 3 * * *')], // 3 AM daily
})

export const handler = async (input, ctx) => {
  ctx.logger.info('Starting user sync')
  
  const externalUsers = await fetchExternalUsers()
  const updates = await syncToDatabase(externalUsers)
  
  ctx.logger.info('User sync complete', {
    fetched: externalUsers.length,
    updated: updates.length,
  })
}

Weekly cleanup

Clean up old data:
import { step, cron } from 'motia'

export const config = step({
  name: 'cleanup-old-data',
  triggers: [cron('0 0 * * 0')], // Sunday midnight
})

export const handler = async (input, ctx) => {
  const thirtyDaysAgo = new Date()
  thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30)
  
  const deleted = await deleteOldRecords(thirtyDaysAgo)
  
  ctx.logger.info('Cleanup complete', { deleted })
}

Monitoring and alerts

Regular health checks:
import { step, cron } from 'motia'

export const config = step({
  name: 'system-monitor',
  triggers: [cron('*/5 * * * *')], // Every 5 minutes
})

export const handler = async (input, ctx) => {
  const metrics = await collectMetrics()
  
  if (metrics.errorRate > 0.05) {
    ctx.logger.error('High error rate detected', {
      errorRate: metrics.errorRate,
    })
    
    await sendAlert('High error rate', metrics)
  }
}

Report generation

Generate periodic reports:
import { step, cron } from 'motia'

export const config = step({
  name: 'weekly-analytics',
  triggers: [cron('0 8 * * 1')], // Monday 8 AM
})

export const handler = async (input, ctx) => {
  const startDate = getLastWeekStart()
  const endDate = getLastWeekEnd()
  
  const analytics = await generateAnalytics(startDate, endDate)
  const report = await formatReport(analytics)
  
  await sendReport(report)
  
  ctx.logger.info('Weekly report sent')
}

Cache warming

Pre-populate caches:
import { step, cron } from 'motia'

export const config = step({
  name: 'warm-cache',
  triggers: [cron('0 */6 * * *')], // Every 6 hours
})

export const handler = async (input, ctx) => {
  const popularItems = await getPopularItems()
  
  for (const item of popularItems) {
    await cacheItem(item)
  }
  
  ctx.logger.info('Cache warmed', { items: popularItems.length })
}

Build docs developers (and LLMs) love