{
"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_transactional": false,
"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",
"offer_source": "sms",
"product_id": null,
"variant_id": null,
"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"
}
{
"error": "Cannot edit completed schedules",
"code": "schedule_complete"
}
{
"error": "blast percent_audience must sum to 100%",
"code": "invalid_blast_percent"
}
{
"error": "Invalid credentials",
"code": "invalid_credentials"
}
{
"error": "Schedule not found",
"code": "not_found"
}
Schedules (v2)
Update Schedule
Update an existing schedule. Completed schedules cannot be edited.
PUT
/
v2
/
schedules
/
{id}
{
"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_transactional": false,
"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",
"offer_source": "sms",
"product_id": null,
"variant_id": null,
"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"
}
{
"error": "Cannot edit completed schedules",
"code": "schedule_complete"
}
{
"error": "blast percent_audience must sum to 100%",
"code": "invalid_blast_percent"
}
{
"error": "Invalid credentials",
"code": "invalid_credentials"
}
{
"error": "Schedule not found",
"code": "not_found"
}
Keys requiring send approval cannot activate or edit schedules (
A
403 confirmation_required). Reads and exact status-only stop requests remain available. Use pending sends for approved single or bulk SMS.
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.
API keys with a daily send cap cannot activate or edit schedules. These requests return 403 schedule_not_allowed_for_capped_key without saving changes. List restrictions apply to the schedule’s resulting source lists, including stored lists omitted from the request.
Restricted keys must retain nonempty, explicitly allowed source lists. An empty selection means all active account lists during execution, so a restricted key cannot clear it or edit a schedule that still uses that default. Current key policy and schedule changes commit in one transaction. A concurrent schedule change returns 409 schedule_write_conflict with retryable: false; reload the schedule and reconsider the update before submitting a revised request.
To stop a schedule, send exactly {"status":"off"} or {"status":"archive"}. These requests are permitted by the key’s cap and list policies, including when an existing source list is no longer allowed. During the execution lock window, only off is available; archiving still returns 409 schedule_locked. Adding any other field keeps the request subject to cap and list restrictions. Tenant ownership and completed-schedule restrictions still apply.
Blast updates accept inline message text and offer/custom-link mappings without a saved
creative. Sending blasts replaces the entire array. save_as_creative and creative_name
are creation-only options:
including either field in a PUT variant returns 400 invalid_save_as_creative, even when
the flag is false. Updating a schedule never creates or synchronizes a saved creative.
To activate an existing draft with its current content and timing, send {"status":"on"}
using a live API key with no daily cap or send-approval requirement, and with permission for
the schedule’s source lists. This operation does not provide sandbox simulation.
Path Parameters
string
required
The unique identifier of the schedule to update.
Body Parameters
Blast mappings support SMS offers (offer_source omitted or sms) and custom URLs.
OMS offer bindings must be managed through the app’s blast editor.
All body parameters are optional. Only provided fields are updated.
string
A descriptive name for this schedule.
string
The type of schedule. Must be
blast or automated.array of strings
Array of audience IDs to target. Each audience must exist and be active on your account.
boolean
Set to
true for a recurring schedule or false for a one-time send.string
Time of day to send, in
HH:MM format (24-hour clock).array of integers
IDs of source sending lists. Restricted keys must retain a nonempty selection of allowed lists;
[] means all active account lists and is available only to unrestricted keys.string
Updated status. Allowed values on update:
draft, off, on, complete, archive.Conditional date and day requirements are evaluated only against the fields included in this request — the schedule’s already-stored
is_repeating value is not consulted. A requirement applies only when the same request also sets is_repeating. Include is_repeating whenever you change dates or days: a request that clears a date without it is accepted and can leave the schedule without the dates its mode needs.datetime
The date to send for a one-time schedule. Required only when this same request also sets
is_repeating to false; you can clear send_date without resending is_repeating, and no error is raised.datetime
Start date for a repeating schedule. Required only when this same request also sets
is_repeating to true; you can clear start_date without resending is_repeating, and no error is raised.datetime
End date for a repeating schedule. Required only when this same request also sets
is_repeating to true; you can clear end_date without resending is_repeating, and no error is raised.object
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 only when this same request also sets is_repeating to true; omitting days_of_week on such a request is treated as no days selected and rejected.float
Skip sending to a contact if they were already sent to within this many hours.
boolean
When
true, send_time is interpreted in each recipient’s local timezone.boolean
Enable or disable bot filtering.
boolean
Set to
true to send MMS messages with image attachments. For blasts, every resulting variant must have a valid mms_image_url. Omitted fields retain their stored values, so enabling MMS requires images on the existing variants or a replacement blasts array that supplies them. Activating an MMS blast also checks all variant images. Status-only stop and archive requests do not require repairing images first.boolean
Enable or disable click-to-open mode.
float
Percentage of the audience for automated creative selection (0-100).
float
Expected or target revenue from this send.
array
Updated array of blast variant objects. When provided, this replaces the entire blasts array. A resulting SMS or MMS blast must have at least one variant, even when you omit
send_type and keep its stored value. Empty or null replacements are rejected. Omitting blasts retains existing variants; activating a legacy blast with no variants requires supplying them first.Show Blast object properties
Show Blast object properties
string
required
Inline message text, 1–1600 characters, containing non-whitespace text. Every submitted variant must pass content checks. Plain text needs no saved creative or link mappings. Every
{{linkN}} placeholder requires a corresponding entry in offer_links.array
Omit or use
[] for text without link placeholders. Every mapping requires a unique linkN key and exactly one destination:key(string, required) — placeholder key, e.g."link1"; unique within this variant.offer_id(string) — ID of an offer belonging to your account. Use this orcustom_url, never both.custom_url(string) — public HTTPS URL, at most 2048 characters, with no credentials, whitespace, or backslashes. Use this oroffer_id, never both.url_params(object, optional) — additional URL query parameters as a key/value object.
float
required
Required on every variant: a finite number from
0 to 100. Values across all variants must total 100 (rounding tolerance: 0.01). Missing values, strings, and booleans are rejected.string
A label for this variant.
string
HTTP(S) URL of an image to attach as MMS, required for every variant when MMS is enabled. The image must be accessible when the message is sent.
Response Fields
The response is the full updated schedule object. See List Schedules for the complete schedule object schema.Show Schedule object properties
Show Schedule object properties
string
Unique identifier for the schedule.
string
A descriptive name for this schedule.
string
Schedule status:
draft, off, on, complete, or archive.string
The type of schedule:
blast or automated.array of integers
IDs of source sending lists.
array of strings
Array of audience IDs.
boolean
true for a recurring schedule, false for a one-time send.string
Time of day to send, in
HH:MM format (24-hour clock).datetime
The date to send for a one-time schedule.
null for repeating schedules.datetime
Start date for a repeating schedule.
datetime
End date for a repeating schedule.
object
An object specifying which days the schedule runs. Keys are days of the week, values are booleans.
float
Skip sending to a contact if they were already sent to within this many hours.
boolean
true if send_time is interpreted in each recipient’s local timezone.boolean
true if bot filtering is enabled.boolean
Read-only.
true when this schedule is marked transactional. Messages from a transactional schedule bypass the standard SMS sending-hour window, state quiet-hour and holiday rules, and your account’s contact frequency caps. This field can only be set from the Trackly dashboard, where an account owner must explicitly acknowledge that bypass — it cannot be set through this API.boolean
true if MMS messages with image attachments are enabled.boolean
true if click-to-open mode is enabled.float
Percentage of the audience for automated creative selection (0-100).
float
Expected or target revenue from this send.
array
Array of blast variant objects.
Show Blast object properties
Show Blast object properties
string
Message body text. May contain
{{link1}}, {{link2}}, etc. placeholders for offer links.array
Array of offer link objects. Each contains
key (string), offer_id (string, null when the link uses a custom URL), offer_source (string, the source discriminator for offer_id: sms for an offer in your account’s offer library, oms for a partnership offer), product_id (string, null when the link does not point to a product), variant_id (string, null when the link does not point to a specific product variant), custom_url (string, null when the link uses an offer), and url_params (object of key/value query parameters).float
Percentage of the audience that receives this variant.
string
Display name for this variant.
string
URL of the MMS image attachment, if applicable.
datetime
ISO 8601 timestamp when the schedule was created.
datetime
ISO 8601 timestamp when the schedule was last updated.
Examples
cURL
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
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']}")
Node.js
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}`);
{
"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_transactional": false,
"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",
"offer_source": "sms",
"product_id": null,
"variant_id": null,
"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"
}
{
"error": "Cannot edit completed schedules",
"code": "schedule_complete"
}
{
"error": "blast percent_audience must sum to 100%",
"code": "invalid_blast_percent"
}
{
"error": "Invalid credentials",
"code": "invalid_credentials"
}
{
"error": "Schedule not found",
"code": "not_found"
}
Error Codes
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | schedule_complete | The schedule has status complete and cannot be modified. |
| 403 | list_not_allowed_for_key | A resulting source list is outside the key’s allowlist, or a restricted key would leave an empty selection. Existing lists omitted from the request still count. Exact status-only off and archive requests are exempt. |
| 403 | schedule_not_allowed_for_capped_key | The API key has a daily send cap; only exact status-only off or archive updates are allowed, subject to the execution lock. |
| 503 | send_cap_unavailable | Current key policy or transactional schedule persistence is unavailable, including policy changes after authentication. Exact status-only off or archive retains its existing checks. |
| 409 | schedule_write_conflict | The schedule changed concurrently and this update was refused. The response includes schedule_id and retryable: false; reload the schedule before submitting a revised update. |
| 409 | schedule_write_outcome_unknown | The server cannot confirm whether the update committed. The response includes schedule_id and retryable: false; inspect that schedule before any retry. |
| 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 | The resulting blast requires at least one variant. Empty/null replacements and activation of an empty blast are rejected, including when send_type is omitted. |
| 400 | invalid_blasts | blasts must be an array of objects. |
| 400 | invalid_blast_percent | Each percentage must be a finite number from 0 to 100; values must total 100 within 0.01. |
| 400 | missing_blast_message | Each blast variant must include a message. |
| 400 | invalid_blast_message | Every submitted message must contain 1–1600 characters and non-whitespace text and pass content checks. |
| 400 | invalid_mms_image_url | Enabling MMS, changing its blast variants, or activating an MMS blast requires a valid HTTP(S) mms_image_url on every resulting variant. |
| 400 | invalid_save_as_creative | PUT variants must omit both save_as_creative and creative_name; these options are POST-only. |
| 400 | invalid_offer_links | Link mappings must have unique linkN keys, exactly one destination, object url_params, and cover every message placeholder. |
| 400 | invalid_custom_url | custom_url must be a valid public HTTPS URL without credentials, at most 2048 characters. |
| 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. |
| 403 | confirmation_required | This key requires send approval and cannot edit or activate schedules. Exact status-only stop requests remain available. |
| 503 | send_policy_unavailable | The key’s confirmation policy cannot be verified; the requested edit is blocked. |
| 409 | schedule_locked | The schedule is inside its execution lock window; the fields listed in lockedFields cannot be changed until unlocksAt. |
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
Campaign Scheduling
Scheduling best practices
List Audiences
View audiences