Send the weekly report

Rung one fired once. This rung fires forever — the same schedule primitive, given a recurrence rule instead of a single instant. A reporting agent should run every Monday at 09:00 in the buyer's own timezone, indefinitely, without anyone re-registering it each week. You hand us a cron expression and an IANA timezone once; we spawn each occurrence in turn.

The story

A finance agent produces a weekly summary and posts it to a channel every Monday morning. It shouldn't stay resident for a week between runs, and daylight-saving shifts shouldn't drag the run to 08:00 or 10:00. So it registers a recurring schedule — 0 9 * * MON in Europe/Paris — and lets AIGears wake it each week. Each fire is a schedule.fired webhook that starts a fresh instance.

Solve it

Go to Schedules → New schedule, choose Recurring, and enter a cron expression plus a timezone. The form shows a live cron preview — the next three fire times in your chosen timezone — so you can confirm the rule reads the way you meant before saving. Paste the webhook_url to POST to and save.

POST /v1/schedules/ with recurrence (a cron expression) and recurrence_tz (an IANA timezone). The server evaluates the cron in that timezone, so 0 9 * * MON stays at 09:00 local across daylight-saving shifts, and computes the first fire_at for you.

curl https://aigears.example.com/v1/schedules/ \
  -H "Authorization: Bearer aigears_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "recurrence": "0 9 * * MON",
    "recurrence_tz": "Europe/Paris",
    "webhook_url": "https://hooks.example.com/aigears",
    "payload": {"report": "weekly-summary"}
  }'

Before committing to a rule, dry-run it with POST /v1/schedules/cron-preview/ ({"cron": ..., "tz": ...}) — it returns the next occurrences, creates nothing, and touches no quota. The runnable version, which previews then creates, is examples/python/schedules_agent.py and examples/typescript/schedulesAgent.ts:

preview = preview_cron(client, cron="0 9 * * 1", tz="Europe/Paris")
# ... confirm the occurrences read right, then:
recurring = create_recurring_schedule(
    client,
    recurrence="0 9 * * 1",
    recurrence_tz="Europe/Paris",
    webhook_url=webhook_url,
    payload={"kind": "standup"},
)

Ask the agent for the cadence in words; it maps it onto a schedule_create call with recurrence + recurrence_tz.

"Send the weekly report every Monday at 9am, Paris time."

schedule_create(
  recurrence="0 9 * * MON",
  recurrence_tz="Europe/Paris",
  webhook_url="https://hooks.example.com/aigears",
  payload={"report": "weekly-summary"},
)
  → { "id": "01J9Z8XKQR...", "status": "pending",
      "recurrence": "0 9 * * MON",
      "fire_at": "2026-07-06T07:00:00Z" }

Understanding the series

A recurring schedule is not fifty-two records booked up front. At any moment a live series has exactly one pending occurrence. When that occurrence resolves — whether it fired successfully or failed to deliver — we spawn its successor, computed from the moment it actually fired, so a backlog never snowballs into a burst of catch-up callbacks. The occurrences sharing a rule are joined by a recurrence_series_id, and GET /v1/schedules/?series_id=<uuid> walks them.

The rule worth internalising: cancelling the pending occurrence terminates the series. There is no separate "delete the series" action — cancel the one live occurrence and no successor is spawned, so the whole recurrence stops.

Variations

  • Stop after it has served its purpose. To end the series, cancel its pending occurrence (schedule_cancel / POST /v1/schedules/{id}/cancel/). No successor spawns.
  • Change the cadence. There is no in-place edit of a recurrence rule: cancel the current series and create a new one with the new cron. The new series gets its own recurrence_series_id.

Next rung

Renew the certificate — when "fire and forget" isn't enough and you need an obligation that tracks completion.