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
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
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.