Docs/Gateway/Scheduled runs (cron)
Gateway

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.

POST/cron/jobs
Create a job. Returns 201 with the stored job (including its server-assigned id and computed nextRunUtc), or 400 if the schedule is invalid.
GET/cron/jobs
List all jobs, wrapped as { "jobs": [...] }.
GET/cron/jobs/{id}
Fetch one job. PUT to the same path updates it; DELETE removes it.
POST/cron/jobs/{id}/run
Run the job now, regardless of its schedule. Returns 409 if a run is already in flight and the job prevents overlap.
GET/cron/jobs/{id}/runs
The job's run history - when each run was scheduled and fired, which machine and Director handled it, and how it ended.

Job fields

ParameterTypeRequiredDescription
namestringRequiredDisplay name for the job.
enabledbooleanOptionalWhether the job fires. Defaults to true. One-off jobs disable themselves after firing.
scheduleKindstringRequired"recurring" (uses cronExpression) or "oneOff" (uses runAt).
cronExpressionstringOptionalStandard 5-field cron expression, e.g. "0 7 * * 1-5" for 7:00 on weekdays. Required when scheduleKind is "recurring".
runAtstringOptionalLocal wall-clock timestamp, e.g. "2026-07-10T18:00:00", interpreted in timeZoneId. Required when scheduleKind is "oneOff".
timeZoneIdstringRequiredTime zone the schedule is evaluated in, e.g. "America/New_York".
target.machinestringRequiredThe machine the session runs on. Machine names come from GET /directors.
action.repoPathstringRequiredWorking folder for the session. Use forward slashes, e.g. "D:/repos/my-project".
action.seedstringOptionalThe prompt the new session starts with. Either seed or workListName must be set.
action.workListNamestringOptionalInstead of a seed prompt, drain a named work list. Takes precedence over seed.
preventOverlapbooleanOptionalSkip a firing if the previous run is still going. Defaults to true.
notifyOnstringOptional"none" (default), "always", or "failure" - when to call the webhook.
notifyWebhookUrlstringOptionalWebhook 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

create-job.json
{
  "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
}
PowerShell
$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).

Warning
Seed prompts run in a brand-new session with zero context. Write them self-contained: name the repo, the goal, and where to put the result. "Continue yesterday's work" means nothing to a fresh session.
Tip
Leave 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.