Cron jobs run agent tasks on a schedule. Each job is a natural-language prompt plus a schedule; on fire, it runs in its own session and the result is delivered to your Slack DM and/or the dashboard notifications panel.
Open the Schedule panel in the dashboard and click Add. Fill in a name, the prompt, and the schedule (interval or cron expression). You can also just ask in chat: "Every weekday at 9, summarize my open work" — the agent creates the job for you.
kirocrew cron add "briefing" "give me a morning status report" --cron "0 9 * * 1-5" kirocrew cron add "poll-status" "check the pipeline endpoint" --every 300 kirocrew cron add "reminder" "call the vet" --at "tomorrow 9am" kirocrew cron add "version-check" "" --every 3600 \ --script ~/.kiro/crew/crons/check.py:run --no-persistent-session --minimal-context kirocrew cron add "disk" "" --every 600 --command "df -h /" --timeout 30 \ --no-persistent-session --minimal-context
The CLI registers agent, script, command, recurring, and one-shot jobs. A script file must already live under ~/.kiro/crew/crons/. --script, --command, and --agent cannot be combined, and each job takes one schedule from --every, --cron, or --at.
An installer or another tool can register a job under an owner key with --managed-by:
kirocrew cron add "catalog-sync" "sync the asset catalog" --cron "0 2 * * *" \ --managed-by "mysystem:owner/asset-name"
The key is unique across the store. A second cron add --managed-by with the same key is refused and names the existing job id. There is no replace in place: to reinstall, remove the job by key, then add it again.
kirocrew cron remove --managed-by "mysystem:owner/asset-name"
cron remove takes either a job id or --managed-by KEY, not both.
A command cron runs through sh -c on a POSIX host. Crew validates it when you save the job and again before each run. It refuses command substitution, loops, special shell parameters, ANSI-C quoting, and brace expansion. This includes quoted regex intervals such as grep -E '[0-9]{1,3}'.
Use a script cron when the check needs those shell features. For a basic regular expression, escaped braces inside double quotes remain available, such as grep "[0-9]\{1,3\}".
A stored job using a newly refused shape fails each run until edited. Import skips that job and reports why. Command crons run on hosts where /bin/sh is Bash, including Amazon Linux, RHEL, and Fedora.
The agent itself calls the equivalent MCP tool (cron_add) whenever you say something like "remind me every morning to check…". No syntax to memorize.
The Schedule panel shows all jobs with their status, next-run time, and controls: pause, resume, trigger now, edit, delete.
kirocrew cron list kirocrew cron pause <job_id> kirocrew cron resume <job_id> kirocrew cron trigger <job_id> # run once manually kirocrew cron remove <job_id> kirocrew cron remove-all
All of these are exposed as MCP tools too (cron_list, cron_pause, cron_resume, cron_trigger, cron_remove, cron_remove_all).
On the Schedule page, open a job and switch to its Logs tab. Each run is listed with an OK, Error, Timeout, or Cancelled badge, its start time, duration, trigger, and summary. Expand a run to read its trace.
A run marked Error or Timeout also offers Ask the agent. It opens a new chat with a report already in the composer: the job id, run id, trigger, start time, the run's summary, and the tail of the run output (the last 4,000 characters). Send it and the agent can inspect the failure and help you fix the job.
This is an investigation hand-off. It does not resume or rerun the job; use Run Now or kirocrew cron trigger for that. Successful and cancelled runs do not offer the button.
| Field | Meaning |
|---|---|
--cron "0 9 * * 1-5" | Standard cron expression |
--every 300 | Every N seconds |
--timezone "America/Los_Angeles" | Per-job IANA timezone (defaults to global config timezone, then UTC) |
--skip-dates "2026-12-25,2027-01-01" | Exclude specific local dates |
Cron expressions use the standard five fields: minute, hour, day of month, month, and day of week. The Schedule page displays each job's expression and next run in that job's own timezone.
When a job names a wall-clock time, always supply an explicit --timezone value. Without one, the cron expression uses the global configuration timezone and falls back to UTC only when no global timezone is configured.
| Option | Purpose |
|---|---|
--timeout-secs <n> | Per-job time budget (default 1800s; up to 24 hours) |
--agent <name> | Agent used for the run |
--persistent-session | Reuse one session across runs; persistent is the stored default |
--silent | Deliver nothing unless the job explicitly sends a notification |
--strict-schedule | Disable execution jitter |
--managed-by <key> | Owner key for a job a tool installs; unique across the store (see Let a tool own a job) |
By default, jobs receive a small random delay to spread simultaneous work. Use --strict-schedule when the exact scheduled minute matters.
persistent_session: false through the dashboard or MCP tool for a poller or scanner that should not accumulate prior context.For an agent job that does not need saved history, enable Minimal context in the Schedule create or edit dialog, or set minimal_context: true through the MCP tool. Crew keeps the job instructions and required member or project guidance while omitting the full injected context. This reduces token use without changing the agent or making the job a script.
For a deterministic check that needs no model reasoning, prefer a script job. Ask the cron-cost-optimize Skill to inspect recorded run history and recommend script, minimal-context, or full-context execution. It reports recommendations and never changes a job on its own.
Each job's output is delivered to your Slack DM (if configured) and to the dashboard notifications panel. In the dashboard, a run's result row carries the same usage footer as a chat turn: elapsed time, credits, and model. See Usage: credits and estimated tokens.
You can override delivery per-run with deliver: markers in the prompt or in HEARTBEAT.md:
- [ ] Monitor pipeline for failures <!-- deliver:C0123CHANNEL -->
This routes the result to a specific Slack channel instead of your DM.
Cron jobs persist to ~/.kiro/crew/crons.json and load at Gateway startup. As long as the Gateway is running, jobs fire on schedule.
If the Gateway is down when a job would have fired, the scheduled run is missed; Crew does not replay every missed interval on startup. A run that was already executing when the Gateway stopped may become due again after restart because it has no completed-run timestamp. Check the Schedule page after an unexpected restart when a job has non-idempotent external effects.
Some multi-instance setups don't want the cron scheduler active. Start the gateway with --no-crons to skip it:
kirocrew gateway --no-crons
Jobs are still readable via kirocrew cron list, but they won't fire on this instance.
Cron jobs that trigger the Task Runner don't show up in the dashboard Tasks page — they're filtered out to keep the view focused on user-initiated work. The tasks still run; the state is just hidden from the visible list.
Cron runs are unattended by definition, so they never carry per-run trust. Any tool call inside a cron job:
Extending per-run trust to recurring runs is deliberately not supported — recurring auto-approval is a bigger risk than a single interactive session.
Heartbeats let Crew watch something over time without you babysitting a session. Ask in chat — "watch this PR and tell me when comments land" or "monitor the pipeline and alert if it goes red" — and Crew creates a heartbeat task that checks every 60 seconds.
Unlike cron jobs (which run a prompt on a fixed schedule), heartbeats are reactive — they only surface results when something changes.
Heartbeat tasks live in ~/.kiro/crew/workspace/HEARTBEAT.md. The heartbeat service reads the file every 60 seconds, runs all tasks in parallel, and:
| Heartbeat | Cron | |
|---|---|---|
| Trigger | Every 60s tick | Fixed schedule or interval |
| Output | Only when something changes | Every run |
| Lifetime | Until condition is met or you remove it | Runs indefinitely until paused/removed |
| Best for | "Tell me when X happens" | "Do Y every morning" |
By default, results go to your Slack DM or dashboard notifications. Override per-task with a delivery marker in the heartbeat file: <!-- deliver:C0123CHANNEL --> routes to a specific Slack channel.
External systems can trigger Crew agent sessions over HTTP. A CI pipeline can notify Crew when a build fails, or a monitoring tool can escalate an alert — all without someone at the terminal.
The endpoint:
POST /api/hooks/agent Authorization: Bearer <token> Content-Type: application/json { "message": "Build failed for pipeline X. Check logs and report the root cause." }
Requests need a valid Bearer token (mint one with kirocrew token). Up to 6 concurrent webhook sessions; each has a 10-minute default timeout.
Example — CI failure alert:
curl -X POST http://localhost:5476/api/hooks/agent \ -H "Authorization: Bearer $KIROCREW_TOKEN" \ -H "Content-Type: application/json" \ -d '{"message": "Build failed. Summarize what went wrong."}'
Crew analyzes the failure and responds. The result routes to your configured notification surface.
For persistent workflows that carry context across multiple webhook calls (e.g., a multi-stage pipeline), the agent can use register_hook to save workflow state that the next webhook call picks up.
For monitoring that stays within a live session (rather than a background heartbeat), Crew has autonudge — a self-check loop that re-injects your instructions on an idle timer.
Use it when you want Crew to keep checking something in your current conversation:
Autonudge works from dashboard chat, Slack threads, and Discord DMs. Stop it by sending a new message that takes over the session, or with the autonudge_stop tool.
Talking to a monitoring session mid-loop no longer restarts its countdown, so checks land when they were meant to. A transient blip — a throttle or a server error — is retried rather than counted toward auto-pause; a successful cycle resets the failure count, and an unattended loop that loses tool approval says so instead of stopping quietly.
Cron & scheduling