Coasty/

Developer API

Schedules

Create, inspect, update, pause, and delete scheduled tasks

Schedules

Schedules, at a glance

Create, inspect, update, pause, and delete scheduled tasks

POST /v1/schedules
A schedule stores one task, machine, timing rule, timezone, and model selection, then claims each due fire exactly once.

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.

FieldValid withRejected with
timedaily, weekly, monthlyEvery sub-daily preset: every_15_minutes, every_30_minutes, hourly, every_6_hours, every_12_hours. Applying a time-of-day to one of those would overwrite its minute and hour fields and collapse the recurrence into a single daily run.
day_of_weekweekly onlyEvery other frequency.
day_of_monthmonthly onlyEvery other frequency.

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.

JSON
{
  "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
  }
}
Schedule creation accepts 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.
Live BYOK schedules require the provider key to be stored first with 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.
EndpointScopeContract
POST /v1/schedulesschedules:writeCreate one schedule; Idempotency-Key provides bounded create replay.
GET /v1/schedulesschedules:readList newest first with limit 1-200 (default 50).
GET /v1/schedules/{id}schedules:readFetch one ownership-scoped schedule.
PATCH /v1/schedules/{id}schedules:writeUpdate at least one mutable field.
POST /v1/schedules/{id}/pauseschedules:writeDisable future dispatch without deleting history.
POST /v1/schedules/{id}/resumeschedules:writeRe-enable a paused schedule subject to plan limits.
DELETE /v1/schedules/{id}schedules:writeDelete the public resource; later reads return 404.

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.

PATCH preserves the stored time-of-day and day selectors when your request does not restate them. A schedule row persists only 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.

API reference — Coasty Computer Use API | Coasty - AI Computer-Use Agent