description: "Schedule automated tasks with natural language — cron jobs, delivery options, and the gateway scheduler"
---
# Scheduled Tasks (Cron)
Schedule tasks to run automatically with natural language or cron expressions. The agent can self-schedule using the `schedule_cronjob` tool from any platform.
## Creating Scheduled Tasks
### In the CLI
Use the `/cron` slash command:
```
/cron add 30m "Remind me to check the build"
/cron add "every 2h" "Check server status"
/cron add "0 9 ** *" "Morning briefing"
/cron list
/cron remove <job_id>
```
### Through Natural Conversation
Simply ask the agent on any platform:
```
Every morning at 9am, check Hacker News for AI news and send me a summary on Telegram.
```
The agent will use the `schedule_cronjob` tool to set it up.
## How It Works
**Cron execution is handled by the gateway daemon.** The gateway ticks the scheduler every 60 seconds, running any due jobs in isolated agent sessions:
```bash
hermes gateway install # Install as system service (recommended)
Even if no messaging platforms are configured, the gateway stays running for cron. A file lock prevents duplicate execution if multiple processes overlap.
:::
## Delivery Options
When scheduling jobs, you specify where the output goes:
| `"origin"` | Back to where the job was created | Default on messaging platforms |
| `"local"` | Save to local files only (`~/.hermes/cron/output/`) | Default on CLI |
| `"telegram"` | Telegram home channel | Uses `TELEGRAM_HOME_CHANNEL` env var |
| `"discord"` | Discord home channel | Uses `DISCORD_HOME_CHANNEL` env var |
| `"telegram:123456"` | Specific Telegram chat by ID | For directing output to a specific chat |
| `"discord:987654"` | Specific Discord channel by ID | For directing output to a specific channel |
**How `"origin"` works:** When a job is created from a messaging platform, Hermes records the source platform and chat ID. When the job runs and deliver is `"origin"`, the output is sent back to that exact platform and chat. If origin info isn't available (e.g., job created from CLI), delivery falls back to local.
**How platform names work:** When you specify a bare platform name like `"telegram"`, Hermes first checks if the job's origin matches that platform and uses the origin chat ID. Otherwise, it falls back to the platform's home channel configured via environment variable (e.g., `TELEGRAM_HOME_CHANNEL`).
The agent's final response is automatically delivered — you do **not** need to include `send_message` in the cron prompt.
When a job hits its repeat limit, it is automatically removed from the job list.
## Real-World Examples
### Daily Standup Report
```
Schedule a daily standup report: Every weekday at 9am, check the GitHub
repository at github.com/myorg/myproject for:
1. Pull requests opened/merged in the last 24 hours
2. Issues created or closed
3. Any CI/CD failures on the main branch
Format as a brief standup-style summary. Deliver to telegram.
```
The agent creates:
```python
schedule_cronjob(
prompt="Check github.com/myorg/myproject for PRs, issues, and CI status from the last 24 hours. Format as a standup report.",
schedule="0 9 ** 1-5",
name="Daily Standup Report",
deliver="telegram"
)
```
### Weekly Backup Verification
```
Every Sunday at 2am, verify that backups exist in /data/backups/ for
each day of the past week. Check file sizes are > 1MB. Report any
gaps or suspiciously small files.
```
### Monitoring Alerts
```
Every 15 minutes, curl https://api.myservice.com/health and verify
it returns HTTP 200 with {"status": "ok"}. If it fails, include the
error details and response code. Deliver to telegram:123456789.
```
```python
schedule_cronjob(
prompt="Run 'curl -s -o /dev/null -w \"%{http_code}\" https://api.myservice.com/health' and verify it returns 200. Also fetch the full response with 'curl -s https://api.myservice.com/health' and check for {\"status\": \"ok\"}. Report the result.",
schedule="every 15m",
name="API Health Check",
deliver="telegram:123456789"
)
```
### Periodic Disk Usage Check
```python
schedule_cronjob(
prompt="Check disk usage with 'df -h' and report any partitions above 80% usage. Also check Docker disk usage with 'docker system df' if Docker is installed.",
-`list_cronjobs` — Shows all jobs with IDs, schedules, repeat status, and next run times
-`remove_cronjob` — Removes a job by ID (use `list_cronjobs` to find the ID)
## Job Storage
Jobs are stored as JSON in `~/.hermes/cron/jobs.json`. Output from job runs is saved to `~/.hermes/cron/output/{job_id}/{timestamp}.md`.
The storage uses atomic file writes (temp file + rename) to prevent corruption from concurrent access.
## Self-Contained Prompts
:::warning Important
Cron job prompts run in a **completely fresh agent session** with zero memory of any prior conversation. The prompt must contain **everything** the agent needs:
- Full context and background
- Specific file paths, URLs, server addresses
- Clear instructions and success criteria
- Any credentials or configuration details
**BAD:** `"Check on that server issue"`
**GOOD:** `"SSH into server 192.168.1.100 as user 'deploy', check if nginx is running with 'systemctl status nginx', and verify https://example.com returns HTTP 200."`
Scheduled task prompts are scanned for instruction-override patterns (prompt injection). Jobs matching threat patterns like credential exfiltration, SSH backdoor attempts, or prompt injection are blocked at creation time. Content with invisible Unicode characters (zero-width spaces, directional overrides) is also rejected.