# Recurring schedules, and a sandbox that says what it did

Source: https://joinsimplesms.com/changelog/2026-10-05-recurring-schedules-and-a-sandbox-that-says-what-it-did
Index: https://joinsimplesms.com/llms.txt

October 5, 2026

- **Recurring schedules**: `POST /v1/schedules` sends a message every day,
  week or month at a local time in an IANA time zone, correct across daylight
  saving. List, retrieve, change, pause and resume (`PATCH` with
  `active`), delete. Each occurrence is a normal send, checked against
  opt-outs, quota, your spend limit and your credit balance when it fires, and a normal message
  with `schedule_id` (`GET /v1/messages?schedule_id=`). A schedule pauses
  itself after 3 opt-out skips in a row; an occurrence we reach more than an
  hour late is skipped, never sent late. In both SDKs (2.2.0) as `schedules`, and on
  the console's Scheduled page. See [Recurring schedules](/docs/schedules).
- **Webhook events**: `schedule.occurrence_skipped`, `schedule.paused`.
- **`simulated`**: every message, and every `message.sent`,
  `message.delivered` and `message.failed` event, now says whether anything
  was handed to a carrier. A test-key send is simulated (`true`) unless it
  goes to your own verified phone. A simulated send to a real-looking number
  also answers with a `notice`, and test-key sends carry an
  `X-SimpleSMS-Simulated` header. Statuses are unchanged.
- **`from` is optional** on `POST /v1/messages` when the account has one
  number for the key's mode: your sandbox number with a test key, your one
  live number with a live key. Otherwise the error lists the choices.
- **`scheduled_message`** objects now carry `scheduled_at`, matching the
  request field. `run_at` stays as a deprecated alias.
  `GET /v1/scheduled_messages/{id}` exists (it answered 405).
- **Sign-up** ends with an optional step to verify your phone. Skipping it, or
  a code that cannot be sent, never blocks the account.
