Create, inspect, update, pause, and delete scheduled tasks
Schedules
Schedules, at a glance
Create, inspect, update, pause, and delete scheduled tasks
POST /v1/schedules- 01Configuretask + timing + timezone
- 02Computenext fire time
- 03Claimdedupe due attempt
- 04Rundurable execution
A schedule runs one Task against an owned managed or external machine on a preset cadence, custom cron expression, or one-shot run_at. Create it with POST /v1/schedules and schedules:write. Supply exactly one timing mode: a recurring frequency (plus cron when frequency is custom) or a one-shot ISO-8601 run_at. timezone is an IANA name; weekly day_of_week uses Monday=0 through Sunday=6.
The three timing refinements are not accepted by every frequency, and sending one where it does not apply is a 422 VALIDATION_ERROR rather than a silent no-op. These combinations were previously accepted and then discarded, which is why the rules are strict now.
None of the three may be combined with frequency: "custom": the cron is the schedule, so encode the timing in the expression itself. Whether a preset can carry a time is derived from that preset's own cron (both its minute and hour fields must be fixed literals), so the rejection body lists the frequencies that currently qualify under valid_options rather than a hand-kept list.
{
"name": "Daily invoice sweep",
"machine_id": "550e8400-e29b-41d4-a716-446655440000",
"task_prompt": "Open billing and download every new invoice",
"frequency": "daily",
"time": "09:00",
"timezone": "America/New_York",
"max_consecutive_failures": 5,
"llm": {
"provider": "openai",
"model": "gpt-5.6-sol"
},
"action_policy": {
"blocked_keys": [
"escape"
],
"block_window_close": true,
"max_actions": 80
}
}action_policy as an immutable boundary inherited by every firing, retry, worker recovery, and nested delegation. PATCH intentionally cannot change it; create a replacement schedule when the boundary must change.PUT /v1/llm/keys/{provider}. A creation-time X-LLM-Api-Key is validation-only and must match that stored key. The schedule stores only provider/model-role selections and a fingerprint, never provider key material.Create, get, list, PATCH, pause, and resume return a sanitized schedule with id, timing, machine/task fields, enabled, next/last run times, counters, pause reason, metadata, and a credential-free llm preference when BYOK is configured. One-shot schedules pause after firing. Reaching max_consecutive_failures trips the circuit breaker and pauses future fires.
PATCH accepts name, task_prompt, timing fields,max_consecutive_failures, enabled, and metadata. It deliberately does not accept machine_id, run_at, or llm. A schedule's BYOK provider/model-role preference is immutable; delete and recreate the schedule to change it. Rotating the stored key is different: each fire resolves the current stored key, so a replacement takes effect on the next fire without recreating the schedule. The schedule's displayed fingerprint remains its creation-time reference; per-fire usage attribution records the fingerprint of the key actually used.
frequency, cron, and timezone, so the stored cron is the surviving record of a time, day_of_week, or day_of_month you sent at create time; it is now read back out and merged. Previously a PATCH that changed only the timezone silently reset a "daily at 14:30" job to the preset default of 09:00. A component is inherited only where the target frequency can express it, so retargeting a daily-at-14:30 schedule to hourly drops the time instead of turning a harmless PATCH into a 422. To clear a selector, send the new timing explicitly.The schedule reads return the ordinary error envelope, not a bare 500: GET /v1/schedules, GET /v1/schedules/{id}, and GET /v1/schedules/{id}/runs/{run_id} surface 404 NOT_FOUND or 404 RUN_NOT_FOUND for an id outside the calling key's namespace, and 503 DB_UNAVAILABLE with a Retry-After when the schedule store is briefly unreachable. A 404 body echoes schedule_id and key_kind so a mode mix-up is obvious.