Scheduled runs with the Gateway cron
5 min read
The Gateway has a built-in scheduler: define a job once and it starts an agent session on a schedule - every morning, every hour, or once at a specific time - with nobody at the keyboard. If no Director is open when a job fires, the Gateway launches one. It is the DevThrottle-native alternative to wiring up an external task scheduler.
The Schedule page
The friendly way to manage jobs is the Schedule page in the Cockpit (open Cockpit from the Director toolbar, then Schedule in the left navigation). Create, edit, enable or disable, and run jobs there, and see each job's run history. Everything below is the REST surface behind that page, for when you want to script it.
The REST API
The scheduler lives on the Gateway's local API at http://127.0.0.1:7878. Requests need the machine's shared Gateway token as a bearer token; it is in %LOCALAPPDATA%\cc-director\config\director\gateway-token.txt.
/cron/jobsid and computed nextRunUtc), or 400 if the schedule is invalid./cron/jobs{ "jobs": [...] }./cron/jobs/{id}PUT to the same path updates it; DELETE removes it./cron/jobs/{id}/run/cron/jobs/{id}/runsJob fields
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Required | Display name for the job. |
enabled | boolean | Optional | Whether the job fires. Defaults to true. One-off jobs disable themselves after firing. |
scheduleKind | string | Required | "recurring" (uses cronExpression) or "oneOff" (uses runAt). |
cronExpression | string | Optional | Standard 5-field cron expression, e.g. "0 7 * * 1-5" for 7:00 on weekdays. Required when scheduleKind is "recurring". |
runAt | string | Optional | Local wall-clock timestamp, e.g. "2026-07-10T18:00:00", interpreted in timeZoneId. Required when scheduleKind is "oneOff". |
timeZoneId | string | Required | Time zone the schedule is evaluated in, e.g. "America/New_York". |
target.machine | string | Required | The machine the session runs on. Machine names come from GET /directors. |
action.repoPath | string | Required | Working folder for the session. Use forward slashes, e.g. "D:/repos/my-project". |
action.seed | string | Optional | The prompt the new session starts with. Either seed or workListName must be set. |
action.workListName | string | Optional | Instead of a seed prompt, drain a named work list. Takes precedence over seed. |
preventOverlap | boolean | Optional | Skip a firing if the previous run is still going. Defaults to true. |
notifyOn | string | Optional | "none" (default), "always", or "failure" - when to call the webhook. |
notifyWebhookUrl | string | Optional | Webhook URL for run notifications. |
The Gateway fills in the rest: id, createdUtc, lastFiredUtc, lastStatus, and nextRunUtc - the next due time in UTC, computed from your schedule and time zone, and recomputed whenever the Gateway restarts.
Example: a weekday morning run
{
"name": "Morning triage",
"scheduleKind": "recurring",
"cronExpression": "0 7 * * 1-5",
"timeZoneId": "America/New_York",
"target": { "machine": "MY-PC" },
"action": {
"repoPath": "D:/repos/my-project",
"seed": "Review open issues in this repo and post a triage summary."
},
"preventOverlap": true
}$token = Get-Content "$env:LOCALAPPDATA\cc-director\config\director\gateway-token.txt"
curl.exe -s -X POST http://127.0.0.1:7878/cron/jobs `
-H "Authorization: Bearer $token" `
-H "Content-Type: application/json" `
--data-binary "@create-job.json"For a one-off, set "scheduleKind": "oneOff" and "runAt": "2026-07-10T18:00:00" instead of the cron expression - the job fires once at that local time and disables itself.
How firing works
The Gateway sweeps the job list about once a minute. When a job is due, it resolves the target machine to a running Director - launching one if none is open - and starts a fresh agent session in repoPath with the seed prompt. The run record keeps both the infrastructure outcome (did a session start) and the task outcome (completed, or needs a human).
preventOverlap on for anything that might run long. A nightly job that occasionally takes all night should skip a beat, not pile up.Where jobs live
Jobs persist to %LOCALAPPDATA%\cc-director\cronjobs.json (run history alongside it in cronruns.json), so schedules survive restarts and reboots. See keeping agents running for the full restart story.