Cron API
Daemon-wide view of scheduled jobs — the same underlying CronJob records (crates/kernel/hoziron-kernel-types/src/scheduler.rs) as schedules.md's /agents/{id}/schedules, just addressed without needing the agent ID up front, plus enable-toggle and run-now.
Endpoints
| Method | Path | Action | Description |
|---|---|---|---|
| GET | /cron/jobs | AgentList | List all cron jobs (all agents) |
| POST | /cron/jobs | ScheduleCreate | Create a cron job |
| DELETE | /cron/jobs/{id} | ScheduleDelete | Delete a job |
| PUT | /cron/jobs/{id}/enable | ScheduleUpdate | Toggle enabled state |
| POST | /cron/jobs/{id}/run | AgentInvoke | Manually fire a job now |
Note POST /cron/jobs/{id}/run is gated on AgentInvoke (admin/operator/developer/service), not a schedule-management action — "run now" is treated as an invocation, so the same roles that can invoke an agent directly can also manually fire a scheduled job.
GET /cron/jobs
curl -H "Api-Version: v1" -H "Authorization: Bearer hzn_sk_..." \
http://localhost:4200/cron/jobs
Response (200) — {"jobs": CronJob[]}
{
"jobs": [
{
"id": "a1b2c3d4-e29b-41d4-a716-446655440000",
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "schedule",
"enabled": true,
"schedule": {"kind": "cron", "expr": "0 */6 * * *", "tz": null},
"action": {"kind": "agent_turn", "message": "Process pending claims queue", "model_override": null, "timeout_secs": null},
"created_at": "2026-05-20T00:00:00Z",
"last_run": "2026-06-04T06:00:00Z",
"next_run": "2026-06-04T12:00:00Z"
}
]
}
Same shape as /agents/{id}/schedules (see that page for the full field reference), just unfiltered across all agents.
POST /cron/jobs
Unlike the agent-scoped POST /agents/{id}/schedules, agent_id is required in the body here (there's no path param to fall back to).
curl -X POST -H "Api-Version: v1" -H "Authorization: Bearer hzn_sk_..." \
-H "Content-Type: application/json" \
-d '{
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"expression": "0 8 * * 1",
"prompt": "Generate weekly claims report"
}' \
http://localhost:4200/cron/jobs
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
agent_id | string (UUID) | Yes | Target agent |
expression | string | Yes | Cron expression |
prompt | string | Yes | Message to send when the job fires |
Response (201)
{"id": "a1b2c3d4-e29b-41d4-a716-446655440000", "agent_id": "550e8400-e29b-41d4-a716-446655440000"}
Error (400 — missing agent_id)
{"error": "agent_id is required in the request body"}
Note this particular error uses a flat {"error": "<string>"} shape, not the category envelope — it's a route-local validation check before the platform call, not a CoreError.
DELETE /cron/jobs/{id}
Response (204 No Content)
PUT /cron/jobs/{id}/enable
curl -X PUT -H "Api-Version: v1" -H "Authorization: Bearer hzn_sk_..." \
-H "Content-Type: application/json" \
-d '{"enabled": false}' \
http://localhost:4200/cron/jobs/a1b2c3d4-e29b-41d4-a716-446655440000/enable
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | No | Defaults to true if omitted |
Response (200)
{"status": "disabled"}
("enabled" when enabled: true.) Internally this calls the same resume_schedule/pause_schedule as /schedules/{id}/resume//pause — it does not return the job's id/enabled fields, just a status string.
POST /cron/jobs/{id}/run
Manually fires the job immediately, synchronously, and returns the actual invocation result — not a fire-and-forget acknowledgment.
curl -X POST -H "Api-Version: v1" -H "Authorization: Bearer hzn_sk_..." \
http://localhost:4200/cron/jobs/a1b2c3d4-e29b-41d4-a716-446655440000/run
Response (200) — InvocationResult
{
"content": "Weekly claims report generated: 47 claims processed, 3 escalations.",
"tokens_used": 890,
"duration_ms": 4100,
"correlation_id": "...",
"source": {"Cron": {"schedule_id": "a1b2c3d4-e29b-41d4-a716-446655440000", "expression": "0 8 * * 1", "scheduled_at": "..."}}
}
Condition Jobs
ADR-065 (condition-driven agent turns) is design-only/demand-gated overall
(#571), but one piece of it is real and live today: a condition job's
enable/disable toggle, structurally identical to
PUT /cron/jobs/{id}/enable above.
| Method | Path | Action | Description |
|---|---|---|---|
| PUT | /conditions/jobs/{id}/enable | ScheduleUpdate | Enable or disable a condition job |
There is no GET/POST/DELETE /conditions/jobs — condition jobs aren't
created through this API; they come from the kernel's ConditionScheduler
(sibling of CronScheduler, ADR-065 §2) and the [condition_tick] config
section (see config-toml.md). This endpoint
only toggles one on or off once it exists.
curl -X PUT -H "Api-Version: v1" -H "Authorization: Bearer hzn_sk_..." \
-H "Content-Type: application/json" \
-d '{"enabled": false}' \
http://localhost:4200/conditions/jobs/c2d3e4f5-a6b7-48c9-9d0e-1f2a3b4c5d6e/enable
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | No | Defaults to true if omitted |
Response (200)
{"status": "disabled"}
("enabled" when enabled: true.)
Related
- schedules.md — agent-scoped view of the same records
- competencies.md — competency-declared schedules and drift detection (
GET /competencies/schedule-health) - ../../../decisions/065-core-mediated-proactive-scheduling.md — ADR-065, condition-driven scheduling design