> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tracklysms.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Update Schedule

> Update an existing schedule. Completed schedules cannot be edited.

Update the configuration of an existing schedule. Only the fields you include in the request body will be modified -- omitted fields remain unchanged. Schedules with status `complete` cannot be edited and will return a `400` error.

## Path Parameters

<ParamField path="id" type="string" required>
  The unique identifier of the schedule to update.
</ParamField>

## Body Parameters

All body parameters are optional. Only provided fields are updated.

<ParamField body="name" type="string" optional>
  A descriptive name for this schedule.
</ParamField>

<ParamField body="send_type" type="string" optional>
  The type of schedule. Must be `blast` or `automated`.
</ParamField>

<ParamField body="audiences" type="array of strings" optional>
  Array of audience IDs to target. Each audience must exist and be active on your account.
</ParamField>

<ParamField body="is_repeating" type="boolean" optional>
  Set to `true` for a recurring schedule or `false` for a one-time send.
</ParamField>

<ParamField body="send_time" type="string" optional>
  Time of day to send, in `HH:MM` format (24-hour clock).
</ParamField>

<ParamField body="source_lists" type="array of integers" optional>
  IDs of source sending lists.
</ParamField>

<ParamField body="status" type="string" optional>
  Updated status. Allowed values on update: `draft`, `off`, `on`, `complete`, `archive`.
</ParamField>

<ParamField body="send_date" type="datetime" optional>
  The date to send for a one-time schedule. Required when `is_repeating` is `false` (whether being set or already set).
</ParamField>

<ParamField body="start_date" type="datetime" optional>
  Start date for a repeating schedule. Required when `is_repeating` is `true` (whether being set or already set).
</ParamField>

<ParamField body="end_date" type="datetime" optional>
  End date for a repeating schedule. Required when `is_repeating` is `true` (whether being set or already set).
</ParamField>

<ParamField body="days_of_week" type="object" optional>
  An object specifying which days the schedule should run. Keys: `monday`, `tuesday`, `wednesday`, `thursday`, `friday`, `saturday`, `sunday`. Values are booleans. At least one day must be `true` if provided.
</ParamField>

<ParamField body="skip_within_hours" type="float" optional>
  Skip sending to a contact if they were already sent to within this many hours.
</ParamField>

<ParamField body="use_local_time" type="boolean" optional>
  When `true`, `send_time` is interpreted in each recipient's local timezone.
</ParamField>

<ParamField body="filter_bots" type="boolean" optional>
  Enable or disable bot filtering.
</ParamField>

<ParamField body="is_mms" type="boolean" optional>
  Set to `true` to send MMS messages with image attachments.
</ParamField>

<ParamField body="is_cto" type="boolean" optional>
  Enable or disable click-to-open mode.
</ParamField>

<ParamField body="automated_percent" type="float" optional>
  Percentage of the audience for automated creative selection (0-100).
</ParamField>

<ParamField body="ir_revenue" type="float" optional>
  Expected or target revenue from this send.
</ParamField>

<ParamField body="blasts" type="array" optional>
  Updated array of blast variant objects. When provided, this replaces the entire blasts array.

  <Expandable title="Blast object properties">
    <ParamField body="blasts[].message" type="string" required>
      The message body. Use `{{link1}}`, `{{link2}}`, etc. as placeholders for offer links.
    </ParamField>

    <ParamField body="blasts[].offer_links" type="array" optional>
      Array of offer link objects. Each contains:

      * `key` (string) — placeholder key, e.g. `"link1"`
      * `offer_id` (string) — ID of the offer
      * `url_params` (object) — additional URL query parameters as a key/value object
    </ParamField>

    <ParamField body="blasts[].percent_audience" type="float" required>
      Percentage of the audience that should receive this variant. Required on every blast: all values must sum to exactly `100`, and a blast that omits it is treated as `0`, which fails that check.
    </ParamField>

    <ParamField body="blasts[].variant_name" type="string" optional>
      A label for this variant.
    </ParamField>

    <ParamField body="blasts[].mms_image_url" type="string" optional>
      URL of an image to attach as MMS.
    </ParamField>
  </Expandable>
</ParamField>

## Response Fields

The response is the full updated schedule object. See [List Schedules](/api-reference/v2/schedules/list-schedules) for the complete schedule object schema.

<Expandable title="Schedule object properties">
  <ResponseField name="id" type="string">
    Unique identifier for the schedule.
  </ResponseField>

  <ResponseField name="name" type="string">
    A descriptive name for this schedule.
  </ResponseField>

  <ResponseField name="status" type="string">
    Schedule status: `draft`, `off`, `on`, `complete`, or `archive`.
  </ResponseField>

  <ResponseField name="send_type" type="string">
    The type of schedule: `blast` or `automated`.
  </ResponseField>

  <ResponseField name="source_lists" type="array of integers">
    IDs of source sending lists.
  </ResponseField>

  <ResponseField name="audiences" type="array of strings">
    Array of audience IDs.
  </ResponseField>

  <ResponseField name="is_repeating" type="boolean">
    `true` for a recurring schedule, `false` for a one-time send.
  </ResponseField>

  <ResponseField name="send_time" type="string">
    Time of day to send, in `HH:MM` format (24-hour clock).
  </ResponseField>

  <ResponseField name="send_date" type="datetime">
    The date to send for a one-time schedule. `null` for repeating schedules.
  </ResponseField>

  <ResponseField name="start_date" type="datetime">
    Start date for a repeating schedule.
  </ResponseField>

  <ResponseField name="end_date" type="datetime">
    End date for a repeating schedule.
  </ResponseField>

  <ResponseField name="days_of_week" type="object">
    An object specifying which days the schedule runs. Keys are days of the week, values are booleans.
  </ResponseField>

  <ResponseField name="skip_within_hours" type="float">
    Skip sending to a contact if they were already sent to within this many hours.
  </ResponseField>

  <ResponseField name="use_local_time" type="boolean">
    `true` if `send_time` is interpreted in each recipient's local timezone.
  </ResponseField>

  <ResponseField name="filter_bots" type="boolean">
    `true` if bot filtering is enabled.
  </ResponseField>

  <ResponseField name="is_mms" type="boolean">
    `true` if MMS messages with image attachments are enabled.
  </ResponseField>

  <ResponseField name="is_cto" type="boolean">
    `true` if click-to-open mode is enabled.
  </ResponseField>

  <ResponseField name="automated_percent" type="float">
    Percentage of the audience for automated creative selection (0-100).
  </ResponseField>

  <ResponseField name="ir_revenue" type="float">
    Expected or target revenue from this send.
  </ResponseField>

  <ResponseField name="blasts" type="array">
    Array of blast variant objects.
  </ResponseField>

  <ResponseField name="created_at" type="datetime">
    ISO 8601 timestamp when the schedule was created.
  </ResponseField>

  <ResponseField name="updated_at" type="datetime">
    ISO 8601 timestamp when the schedule was last updated.
  </ResponseField>
</Expandable>

## Examples

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X PUT "https://api.tracklysms.com/api/v2/schedules/6651a3f2e4b0a1c2d3e4f567" \
      -H "X-Api-Key: trk_your_api_key_here" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Weekend Promo — Updated",
        "status": "on",
        "skip_within_hours": 48,
        "blasts": [
          {
            "message": "Huge weekend sale! Save big: {{link1}}",
            "offer_links": [
              {
                "key": "link1",
                "offer_id": "665fa1b2c3d4e5f6a7b8c902",
                "url_params": {"utm_source": "sms", "utm_campaign": "weekend_v2"}
              }
            ],
            "percent_audience": 100,
            "variant_name": "Single Variant"
          }
        ]
      }'
    ```

    ```python Python theme={null}
    import requests

    schedule_id = "6651a3f2e4b0a1c2d3e4f567"

    payload = {
        "name": "Weekend Promo — Updated",
        "status": "on",
        "skip_within_hours": 48,
        "blasts": [
            {
                "message": "Huge weekend sale! Save big: {{link1}}",
                "offer_links": [
                    {
                        "key": "link1",
                        "offer_id": "665fa1b2c3d4e5f6a7b8c902",
                        "url_params": {"utm_source": "sms", "utm_campaign": "weekend_v2"},
                    }
                ],
                "percent_audience": 100,
                "variant_name": "Single Variant",
            }
        ],
    }

    response = requests.put(
        f"https://api.tracklysms.com/api/v2/schedules/{schedule_id}",
        headers={
            "X-Api-Key": "trk_your_api_key_here",
            "Content-Type": "application/json",
        },
        json=payload,
    )

    data = response.json()
    print(f"Updated: {data['name']} — {data['status']}")
    ```

    ```javascript Node.js theme={null}
    const scheduleId = "6651a3f2e4b0a1c2d3e4f567";

    const payload = {
      name: "Weekend Promo — Updated",
      status: "on",
      skip_within_hours: 48,
      blasts: [
        {
          message: "Huge weekend sale! Save big: {{link1}}",
          offer_links: [
            {
              key: "link1",
              offer_id: "665fa1b2c3d4e5f6a7b8c902",
              url_params: { utm_source: "sms", utm_campaign: "weekend_v2" }
            }
          ],
          percent_audience: 100,
          variant_name: "Single Variant"
        }
      ]
    };

    const response = await fetch(
      `https://api.tracklysms.com/api/v2/schedules/${scheduleId}`,
      {
        method: "PUT",
        headers: {
          "X-Api-Key": "trk_your_api_key_here",
          "Content-Type": "application/json"
        },
        body: JSON.stringify(payload)
      }
    );

    const data = await response.json();
    console.log(`Updated: ${data.name} — ${data.status}`);
    ```
  </CodeGroup>
</RequestExample>

<ResponseExample>
  ```json 200 — Success theme={null}
  {
    "id": "6651a3f2e4b0a1c2d3e4f567",
    "name": "Weekend Promo — Updated",
    "status": "on",
    "send_type": "blast",
    "source_lists": [101, 204],
    "audiences": ["665fa1b2c3d4e5f6a7b8c901"],
    "is_repeating": true,
    "send_time": "14:30",
    "send_date": null,
    "start_date": "2025-06-01T00:00:00",
    "end_date": "2025-08-31T00:00:00",
    "days_of_week": {
      "monday": false,
      "tuesday": false,
      "wednesday": false,
      "thursday": false,
      "friday": true,
      "saturday": true,
      "sunday": true
    },
    "skip_within_hours": 48,
    "use_local_time": true,
    "filter_bots": true,
    "is_mms": false,
    "is_cto": false,
    "automated_percent": 0,
    "ir_revenue": 2.50,
    "blasts": [
      {
        "message": "Huge weekend sale! Save big: {{link1}}",
        "offer_links": [
          {
            "key": "link1",
            "offer_id": "665fa1b2c3d4e5f6a7b8c902",
            "custom_url": null,
            "url_params": {"utm_source": "sms", "utm_campaign": "weekend_v2"}
          }
        ],
        "percent_audience": 100,
        "variant_name": "Single Variant",
        "mms_image_url": null
      }
    ],
    "created_at": "2025-05-28T10:15:30",
    "updated_at": "2025-06-05T14:22:10"
  }
  ```

  ```json 400 — Schedule Complete theme={null}
  {
    "error": "Cannot edit completed schedules",
    "code": "schedule_complete"
  }
  ```

  ```json 400 — Validation Error theme={null}
  {
    "error": "blast percent_audience must sum to 100%",
    "code": "invalid_blast_percent"
  }
  ```

  ```json 401 — Unauthorized theme={null}
  {
    "error": "Invalid credentials",
    "code": "invalid_credentials"
  }
  ```

  ```json 404 — Not Found theme={null}
  {
    "error": "Schedule not found",
    "code": "not_found"
  }
  ```
</ResponseExample>

## Error Codes

| HTTP Status | Error Code              | Description                                                                                                                |
| ----------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| 400         | `schedule_complete`     | The schedule has status `complete` and cannot be modified.                                                                 |
| 400         | `invalid_send_type`     | `send_type` must be `blast` or `automated`.                                                                                |
| 400         | `missing_audiences`     | `audiences` was supplied as an empty array; at least one audience ID is required.                                          |
| 400         | `invalid_status`        | `status` must be `draft`, `off`, `on`, `complete`, or `archive`.                                                           |
| 400         | `missing_send_date`     | `send_date` is required for one-time (non-repeating) schedules.                                                            |
| 400         | `missing_start_date`    | `start_date` is required for repeating schedules.                                                                          |
| 400         | `missing_end_date`      | `end_date` is required for repeating schedules.                                                                            |
| 400         | `no_days_selected`      | At least one day must be `true` in `days_of_week` for repeating schedules.                                                 |
| 400         | `missing_blasts`        | Blast variants are required when `send_type` is `blast`.                                                                   |
| 400         | `invalid_blast_percent` | The `percent_audience` values across all blasts must sum to exactly `100`.                                                 |
| 400         | `missing_blast_message` | Each blast variant must include a `message`.                                                                               |
| 404         | `not_found`             | No schedule exists with the given ID.                                                                                      |
| 400         | `invalid_numeric`       | `skip_within_hours`, `automated_percent`, or `ir_revenue` is not a number.                                                 |
| 400         | `invalid_audience_id`   | An audience ID is not a valid identifier.                                                                                  |
| 400         | `audience_not_found`    | One or more audience IDs do not exist or are not active.                                                                   |
| 400         | `source_list_not_found` | One or more source list IDs do not exist on this account.                                                                  |
| 400         | `invalid_offer_id`      | An offer ID referenced in `offer_links` is not a valid identifier.                                                         |
| 400         | `offer_not_found`       | One or more offer IDs referenced in `offer_links` do not exist.                                                            |
| 401         | `invalid_credentials`   | API key is missing, invalid, or revoked.                                                                                   |
| 403         | `account_suspended`     | Your account is suspended. Resolve outstanding billing or contact support.                                                 |
| 429         | `rate_limited`          | Request throttled; retry with exponential backoff after the window resets.                                                 |
| 409         | `schedule_locked`       | The schedule is inside its execution lock window; the fields listed in `lockedFields` cannot be changed until `unlocksAt`. |

A `409 schedule_locked` response carries two extra fields: `lockedFields` (the array of fields currently locked) and `unlocksAt` (an ISO 8601 timestamp for when edits are allowed again).

## Next Steps

<CardGroup cols={2}>
  <Card title="Campaign Scheduling" icon="calendar" href="/guides/campaigns/scheduling">
    Scheduling best practices
  </Card>

  <Card title="List Audiences" icon="users" href="/api-reference/v2/audiences/list-audiences">
    View audiences
  </Card>
</CardGroup>
