Skip to main content
A schedule runs an agent automatically on a recurring trigger — no human in the loop. You give it a prompt, a trigger (when to fire), and a deliver_to (where the output lands), and HQ runs the agent on that cadence and delivers the result. This guide walks the full lifecycle: create, observe, pause/resume, fire once on demand, edit, and tear down. All routes live under https://api.hq.zone/v1/api/schedules and authenticate with a personal access token: Authorization: Bearer hq_pat_.... Read operations need the schedules:read scope; everything that mutates a schedule needs schedules:write.

Create a recurring run

1

Pick a trigger

The trigger field is a tagged object — a kind plus its parameters. For a recurring run you’ll use one of:
  • cron — a 5-field cron expression (min hour dom month dow) in a named timezone. tz is an IANA name like "Europe/Stockholm"; fixed offsets such as "UTC+3" are rejected.
  • rrule — an RFC 5545 RRULE for patterns cron can’t express (e.g. “third Tuesday of the month”), also with an IANA tz.
tz is required on the engine-side trigger. Pass an IANA zone name explicitly — a fixed UTC offset is refused because it can’t track DST.
2

Choose where output is delivered

deliver_to is also a tagged object. The supported destinations are:
  • { "kind": "web_inbox" } — the user’s web inbox (read via the /v1/api/conversations surface).
  • { "kind": "slack_dm", "user_id": "U0123..." } — DM a Slack user.
  • { "kind": "slack_channel", "channel_id": "C0123..." } — post in a Slack channel.
  • { "kind": "teams_dm", "conversation_id": "..." } — a Microsoft Teams chat.
  • { "kind": "teams_channel", "team_id": "...", "channel_id": "..." } — a Teams channel.
3

POST the schedule

POST /v1/api/schedules creates a schedule owned by the caller. The body is:
  • name (required) and optional description.
  • trigger (required) — the object from step 1.
  • prompt (required) — the instruction the agent runs on each fire.
  • agent_id (optional) — which agent runs the prompt. If omitted, the workspace’s default enabled agent is used; a non-existent agent_id returns 404.
  • deliver_to (required) — the destination from step 2.
  • reset_each_fire (optional, default false) — false reuses one persistent conversation thread across fires (so the agent can compare against prior runs); true starts a fresh conversation each fire.
  • delivery_locale (optional) — a BCP-47 locale override for this schedule’s deliveries; falls back to the owner’s workspace locale when absent.
A successful create returns 201 with the full schedule object. If your plan’s schedule limit is reached, the request fails with 402.
See the full request and response shapes on Create a schedule.

List and inspect

GET /v1/api/schedules returns your schedules plus a total count for pagination. By default scope=mine returns only the caller’s own schedules.
Each entry carries observability fields: state, last_fire_at, last_fire_status, next_fire_at, fire_count, failure_count, consecutive_failure_count, last_fire_lag_ms, and last_run_conversation_id (a link to the conversation from the most recent run). By default the list excludes archived schedules so you see the working set. To change that:
  • state=<value> filters by an explicit lifecycle state (passes through verbatim).
  • include_archived=true adds archived rows to the default view (the audit history view).
  • limit defaults to 100 (max 500); offset paginates the workspace view.
To fetch one schedule’s full detail by id, use GET /v1/api/schedules/{id}. You can read a schedule only if you own it or are an admin; otherwise it returns 404.

Lifecycle states

A schedule’s state is one of:
  • active — firing on its trigger.
  • paused — temporarily stopped; resumable.
  • quarantined — its stored trigger or args won’t parse; the engine parked it.
  • archived — cancelled; will never fire again (history retained).

Pause and resume

Pause an active schedule to stop it firing until you resume it. POST /v1/api/schedules/{id}/pause moves it from activepaused and clears its next fire.
POST /v1/api/schedules/{id}/resume brings it back and returns the newly computed next fire time.
You can’t resume an archived schedule. A quarantined schedule must have its trigger fixed via an update before it will resume — resuming it as-is would just re-quarantine on the next fire.
Only the schedule’s owner or an admin may pause or resume; others get 403.

Fire once now

POST /v1/api/schedules/{id}/fire_now triggers an immediate run without changing the regular firing cadence.
Owner or admin only (403 otherwise). See Fire a schedule now.

Update a schedule

PATCH /v1/api/schedules/{id} changes only the fields you supply: name, description, trigger, or args. When the trigger changes, the response includes the recomputed next_fire_at.
For description specifically, sending an explicit null clears it, while omitting the field keeps the current value.
Owner or admin only (403 otherwise). See Update a schedule.

Tear down: cancel, then erase

Teardown is a deliberate two-step path so you can’t destroy history by accident.
1

Cancel (archive)

DELETE /v1/api/schedules/{id} cancels the schedule and archives it: it flips statearchived, drops pending timers, and stops all future runs while retaining its history.
2

Erase (permanent)

DELETE /v1/api/schedules/{id}/erase permanently deletes the schedule row. The engine refuses to erase a schedule that isn’t already archived — you must cancel it first.
Erase is destructive and irreversible. It only operates on archived rows; attempting to erase an active, paused, or quarantined schedule is rejected — cancel it first.
Both cancel and erase are owner-or-admin only (403 otherwise). References: Cancel a schedule and Erase a schedule.

Admin: filter by owner

Admins can list across the whole workspace with scope=workspace (non-admins get 403), optionally narrowing to one owner via owner_user_id. To populate a “filter by user” dropdown with only users who actually have schedules, call GET /v1/api/schedules/owners.
This endpoint is admin-only (403 otherwise) and returns each distinct owner with a schedule_count. See List schedule owners.