Scheduled Agents
Any Nestor agent can be put on a cron schedule. The scheduler persists the schedule in SQLite, validates every expression at write time, and fires the agent with a stored prompt at the configured cadence.
Overview
Scheduled agents answer a simple need: "run this agent every morning at 8:00", "rescan this topic every two hours", "post the weekly digest every Monday at 09:00". Each agent can carry at most one schedule — the agent_id column in agent_schedules is UNIQUE, so re-posting a schedule overwrites the previous one.
Cron expressions are parsed by parseCronExpression from @nestor/agent before the row is written. An invalid pattern returns a 400 with the parser error attached; no malformed cron reaches the runner.
Data model
The agent_schedules table (source: packages/db) stores one row per scheduled agent. Relevant columns:
| Column | Type | Notes |
|---|---|---|
agent_id | TEXT | Foreign key to agents.id. UNIQUE — one schedule per agent. |
cron | TEXT | Standard 5-field cron expression (min / hour / dom / mon / dow). |
prompt | TEXT | Optional. The prompt fed to the agent on each tick. |
enabled | INTEGER | Toggle the schedule without deleting it. |
tenant_id | TEXT | Multi-tenant isolation. Defaults to default. |
REST endpoints
The scheduling API lives in packages/server/src/routes/agents.ts. All endpoints require tenant context and validate their payloads with Zod.
Set or update a schedule
POST /api/agents/:id/schedule
Content-Type: application/json
{
"cron": "0 8 * * *",
"prompt": "Summarise yesterday's commits and post to Obsidian.",
"enabled": true
}
The cron field is required, capped at 128 characters, and parsed before storage. prompt is optional and capped at 10 000 characters. Repeating a POST on the same agent overwrites the row.
Read a schedule
GET /api/agents/:id/schedule
Returns the current schedule for an agent or 404 if none is set.
Delete a schedule
DELETE /api/agents/:id/schedule
Removes the row from agent_schedules. The agent itself is not affected.
List all scheduled agents
GET /api/agents?scheduled=true
Returns only agents that currently have an active row in agent_schedules, with their schedule metadata attached under the schedule key. This is the shape Studio's Scheduler page consumes.
Note: earlier drafts of this documentation referenced a standalone GET /api/scheduled-agents endpoint. The actual implementation uses the query-parameter form on the agents collection shown above.
Sample cron patterns
Nestor uses the standard 5-field Unix cron syntax: minute hour day-of-month month day-of-week. A few common patterns:
| Expression | Fires |
|---|---|
*/15 * * * * | Every 15 minutes |
0 * * * * | Top of every hour |
0 8 * * * | Every day at 08:00 |
0 9 * * 1 | Every Monday at 09:00 |
0 9 1 * * | First day of each month at 09:00 |
0 2 * * 1-5 | Weeknights at 02:00 |
Quiet hours & timezone
Nestor honours the user's declared timezone (stored in the user model; see packages/agent/src/memory/user-model.ts). Cron expressions are evaluated in that timezone rather than server UTC, so 0 8 * * * means 08:00 local.
For quiet hours (no autopilot between midnight and 07:00, weekends off, etc.), the current pattern is to encode the allowed window directly in the cron expression:
# Every 30 minutes between 08:00 and 20:00, Mon-Fri
*/30 8-20 * * 1-5
# Hourly, skipping nighttime (08:00 to 22:00)
0 8-22 * * *
Persona-level quiet hours (Nexus refuses to kick off autopilot during night hours) are enforced independently by the orchestrator on top of the cron trigger. A scheduled tick that hits a quiet window is dropped, not deferred.
End-to-end example
# 1. Create an agent
curl -X POST http://localhost:3000/api/agents \
-H 'Content-Type: application/json' \
-d '{"name":"digest","adapterType":"claude","adapterConfig":{"model":"claude-sonnet-4-6"}}'
# 2. Schedule it every weekday at 08:00
curl -X POST http://localhost:3000/api/agents/<id>/schedule \
-H 'Content-Type: application/json' \
-d '{"cron":"0 8 * * 1-5","prompt":"Summarise yesterday and post to Obsidian.","enabled":true}'
# 3. Inspect the list of scheduled agents
curl http://localhost:3000/api/agents?scheduled=true
# 4. Disable the schedule without deleting it
curl -X POST http://localhost:3000/api/agents/<id>/schedule \
-H 'Content-Type: application/json' \
-d '{"cron":"0 8 * * 1-5","enabled":false}'
# 5. Remove the schedule entirely
curl -X DELETE http://localhost:3000/api/agents/<id>/schedule
For related surface area, see Agents, Workflows (for DAG-level scheduling with cron trigger nodes), and the documentation overview.