Skip to content

Scheduling Workflows

Conductor includes a built-in scheduler that triggers workflow executions on a cron schedule. Schedules are managed through the REST API — no external cron daemon or job scheduler is needed.

How it works

A schedule binds a cron expression to a StartWorkflowRequest. On every cron tick the scheduler starts a new workflow execution with the configured input. Two timestamps are automatically injected into every triggered workflow's input:

Input key Description
_scheduledTime The exact cron slot time (epoch ms)
_executedTime The actual dispatch time (epoch ms)

Cron expression format

Conductor uses Spring's 6-field cron format with second-level precision:

┌─────────────── second (0-59)
│ ┌───────────── minute (0-59)
│ │ ┌─────────── hour (0-23)
│ │ │ ┌───────── day of month (1-31)
│ │ │ │ ┌─────── month (1-12 or JAN-DEC)
│ │ │ │ │ ┌───── day of week (0-7 or MON-SUN)
│ │ │ │ │ │
* * * * * *
Expression Meaning
0 * * * * * Every minute
0 0 9 * * MON-FRI Weekdays at 9 AM
0 0 0 1 * * First day of every month at midnight
*/10 * * * * * Every 10 seconds

Creating a schedule

1. Register the workflow (if not already registered):

curl -X PUT 'http://localhost:8080/api/metadata/workflow' \
  -H 'Content-Type: application/json' \
  -d '[{
    "name": "daily_report_workflow",
    "version": 1,
    "schemaVersion": 2,
    "tasks": [{
      "name": "fetch_report_data",
      "taskReferenceName": "fetch_report_data_ref",
      "type": "HTTP",
      "inputParameters": {
        "http_request": {
          "uri": "https://jsonplaceholder.typicode.com/todos?userId=1",
          "method": "GET"
        }
      }
    }],
    "timeoutPolicy": "TIME_OUT_WF",
    "timeoutSeconds": 120
  }]'

2. Create the schedule:

curl -X POST 'http://localhost:8080/api/scheduler/schedules' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "daily-report-schedule",
    "cronExpression": "0 0 9 * * MON-FRI",
    "zoneId": "America/New_York",
    "startWorkflowRequest": {
      "name": "daily_report_workflow",
      "version": 1,
      "correlationId": "daily-report-${scheduledTime}"
    },
    "runCatchupScheduleInstances": false,
    "paused": false
  }'

The response returns the saved schedule object including its computed nextRunTime.

Schedule definition fields

Field Type Required Description
name string Yes Unique schedule identifier
cronExpression string Yes 6-field Spring cron expression
zoneId string No Timezone (default: UTC)
startWorkflowRequest object Yes Workflow to trigger — includes name, version, input, correlationId
runCatchupScheduleInstances boolean No Fire missed slots if the scheduler was offline (default: false)
paused boolean No Create in paused state (default: false)
scheduleStartTime long No Earliest time the schedule fires (epoch ms)
scheduleEndTime long No Latest time the schedule fires (epoch ms)
description string No Free-text description

Previewing execution times

Before creating a schedule, preview when it will fire:

curl 'http://localhost:8080/api/scheduler/nextFewSchedules?cronExpression=0+0+9+*+*+MON-FRI&limit=5'

Returns an array of epoch-millisecond timestamps for the next 5 execution times.

Pausing and resuming

# Pause
curl -X PUT 'http://localhost:8080/api/scheduler/schedules/daily-report-schedule/pause'

# Pause with a reason
curl -X PUT 'http://localhost:8080/api/scheduler/schedules/daily-report-schedule/pause?reason=maintenance+window'

# Resume
curl -X PUT 'http://localhost:8080/api/scheduler/schedules/daily-report-schedule/resume'

Listing and searching schedules

# List all schedules
curl 'http://localhost:8080/api/scheduler/schedules'

# Filter by workflow name
curl 'http://localhost:8080/api/scheduler/schedules?workflowName=daily_report_workflow'

# Search with pagination
curl 'http://localhost:8080/api/scheduler/schedules/search?workflowName=daily_report_workflow&size=10'

Viewing execution history

curl 'http://localhost:8080/api/scheduler/search/executions?freeText=daily-report-schedule&size=20'

Returns a SearchResult with totalHits and a list of execution records, each containing the scheduledTime, executionTime, workflowId, and state (POLLED, EXECUTED, or FAILED).

Deleting a schedule

curl -X DELETE 'http://localhost:8080/api/scheduler/schedules/daily-report-schedule'

Passing input to scheduled workflows

Static input parameters are merged with the auto-injected _scheduledTime and _executedTime:

{
  "name": "input-param-demo-schedule",
  "cronExpression": "0 * * * * *",
  "zoneId": "UTC",
  "startWorkflowRequest": {
    "name": "input_param_demo_workflow",
    "version": 1,
    "input": {
      "reportOwner": "platform-team",
      "alertThreshold": 100
    }
  }
}

Inside the workflow, access all values via ${workflow.input.*}:

  • ${workflow.input.reportOwner} — your static input
  • ${workflow.input._scheduledTime} — injected cron slot time
  • ${workflow.input._executedTime} — injected actual dispatch time

Configuration

The scheduler is configured under the conductor.scheduler prefix in your application properties:

Property Default Description
conductor.scheduler.enabled true Enable/disable the scheduler
conductor.scheduler.pollingInterval 100 Poll interval in milliseconds
conductor.scheduler.pollBatchSize 5 Schedules processed per poll cycle
conductor.scheduler.pollingThreadCount 1 Number of polling threads
conductor.scheduler.schedulerTimeZone UTC Default timezone
conductor.scheduler.initialDelayMs 15000 Startup delay before first poll
conductor.scheduler.maxScheduleJitterMs 1000 Random jitter added to dispatch times to smooth load

Catchup mode

When runCatchupScheduleInstances is true, the scheduler fires all cron slots that were missed while it was offline. Use this for workflows where every execution matters (e.g., billing, compliance). Leave it false (default) for dashboards or monitoring where only the latest run matters.

Concurrent executions

The scheduler fires on every cron tick regardless of whether the previous execution has completed. If your workflow takes longer than the cron interval, multiple instances will run concurrently. Design your workflows to handle this, or use a longer interval.