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

MethodPathActionDescription
GET/cron/jobsAgentListList all cron jobs (all agents)
POST/cron/jobsScheduleCreateCreate a cron job
DELETE/cron/jobs/{id}ScheduleDeleteDelete a job
PUT/cron/jobs/{id}/enableScheduleUpdateToggle enabled state
POST/cron/jobs/{id}/runAgentInvokeManually 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

FieldTypeRequiredDescription
agent_idstring (UUID)YesTarget agent
expressionstringYesCron expression
promptstringYesMessage 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

FieldTypeRequiredDescription
enabledbooleanNoDefaults 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.

MethodPathActionDescription
PUT/conditions/jobs/{id}/enableScheduleUpdateEnable 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

FieldTypeRequiredDescription
enabledbooleanNoDefaults to true if omitted

Response (200)

{"status": "disabled"}

("enabled" when enabled: true.)