Schedules API

Agent-scoped cron schedules — a schedule fires a fixed prompt to a specific agent on a cron cadence. Fully live (not a stub). Backed by the kernel's CronJob records (crates/kernel/hoziron-kernel-types/src/scheduler.rs). See also cron.md for the equivalent daemon-wide /cron/jobs surface, which manages the exact same underlying records (they share the same in-memory store — a schedule created via one surface is visible/manageable via the other).

Endpoints

MethodPathActionDescription
GET/agents/{id}/schedulesAgentStatusList schedules for an agent
POST/agents/{id}/schedulesScheduleCreateCreate a schedule for an agent
POST/schedules/{id}/pauseScheduleUpdatePause a schedule
POST/schedules/{id}/resumeScheduleUpdateResume a schedule
DELETE/schedules/{id}ScheduleDeleteDelete a schedule

ScheduleCreate/ScheduleUpdate/ScheduleDelete are admin/operator only. AgentStatus (read) is broadly readable (admin/operator/developer/viewer/service/auditor).


GET /agents/{id}/schedules

curl -H "Api-Version: v1" -H "Authorization: Bearer hzn_sk_..." \
  http://localhost:4200/agents/550e8400-e29b-41d4-a716-446655440000/schedules

Response (200) — CronJob[]

{
  "schedules": [
    {
      "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": "Check for overdue claims and escalate any past SLA.", "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"
    }
  ]
}

id is a plain UUID (CronJobId's Display prints the inner UUID with no prefix). name defaults to "schedule" for jobs created through this route — there is no name field on the create request. enabled is a plain boolean, not a state string enum. schedule and action are internally-tagged enums ("kind" field): schedule.kind is "cron" (with expr/tz), "every" (with every_secs), or "at" (with at); action.kind is always "agent_turn" (with message/model_override/timeout_secs) — it's the only variant.


POST /agents/{id}/schedules

curl -X POST -H "Api-Version: v1" -H "Authorization: Bearer hzn_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"expression": "0 */6 * * *", "prompt": "Check for overdue claims."}' \
  http://localhost:4200/agents/550e8400-e29b-41d4-a716-446655440000/schedules

Request Body

FieldTypeRequiredDescription
expressionstringYesCron expression
promptstringYesPrompt delivered to the agent when the schedule fires

(agent_id is also a valid field on the underlying request struct but is ignored on this route — the path param {id} takes precedence. It's required, not ignored, on the /cron/jobs variant — see cron.md.)

Response (201)

{"id": "a1b2c3d4-e29b-41d4-a716-446655440000"}

Error (400 — invalid cron expression)

{"error": {"category": "ValidationError", "message": "..."}}

POST /schedules/{id}/pause

curl -X POST -H "Api-Version: v1" -H "Authorization: Bearer hzn_sk_..." \
  http://localhost:4200/schedules/a1b2c3d4-e29b-41d4-a716-446655440000/pause

Response (200)

{"status": "paused"}

POST /schedules/{id}/resume

{"status": "active"}

DELETE /schedules/{id}

Response (204 No Content)

Error (404 — unknown schedule)

{"error": {"category": "NotFound", "message": "Schedule not found: a1b2c3d4-...", "details": {"schedule_id": "a1b2c3d4-..."}}}

  • cron.md — same records, daemon-wide (/cron/jobs) view with enable-toggle and run-now
  • agents.md — the agent a schedule invokes