curl -X POST "https://api.tracklysms.com/api/v2/creatives" \
-H "X-Api-Key: trk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Summer Sale Promo",
"message": "Hot deals just for you! Shop now: {{link1}} Reply STOP to opt out.",
"offer_links": [
{
"key": "link1",
"offer_id": "665f1a2b3c4d5e6f70819201",
"url_params": {"sub1": "sms"}
}
],
"status": "active",
"creative_type": "automated"
}'
import requests
response = requests.post(
"https://api.tracklysms.com/api/v2/creatives",
headers={
"X-Api-Key": "trk_your_api_key_here",
"Content-Type": "application/json"
},
json={
"name": "Summer Sale Promo",
"message": "Hot deals just for you! Shop now: {{link1}} Reply STOP to opt out.",
"offer_links": [
{
"key": "link1",
"offer_id": "665f1a2b3c4d5e6f70819201",
"url_params": {"sub1": "sms"}
}
],
"status": "active",
"creative_type": "automated"
}
)
data = response.json()
print(f"Created creative #{data['creative']['id']}")
print(f"Segments: {data['creative']['segment_count']}")
const response = await fetch(
"https://api.tracklysms.com/api/v2/creatives",
{
method: "POST",
headers: {
"X-Api-Key": "trk_your_api_key_here",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Summer Sale Promo",
message:
"Hot deals just for you! Shop now: {{link1}} Reply STOP to opt out.",
offer_links: [
{
key: "link1",
offer_id: "665f1a2b3c4d5e6f70819201",
url_params: { sub1: "sms" },
},
],
status: "active",
creative_type: "automated",
}),
}
);
const data = await response.json();
console.log(`Created creative #${data.creative.id}`);
{
"success": true,
"creative": {
"id": 101,
"name": "Summer Sale Promo",
"message": "Hot deals just for you! Shop now: {{link1}} Reply STOP to opt out.",
"offer_links": [
{
"key": "link1",
"offer_id": "665f1a2b3c4d5e6f70819201",
"url_params": {"sub1": "sms"}
}
],
"offers": ["665f1a2b3c4d5e6f70819201"],
"status": "active",
"creative_type": "automated",
"character_count": 114,
"segment_count": 1,
"created_at": "2025-06-10T12:00:00",
"slug": "cre_summer-sale-promo_a1b2c3",
"list_control_mode": "all",
"list_control_ids": []
}
}
{
"error": "message must contain at least one {{linkN}} placeholder",
"code": "missing_link_placeholder"
}
{
"error": "Offer not found: 665f1a2b3c4d5e6f70819201",
"code": "offer_not_found"
}
{
"error": "Invalid credentials",
"code": "invalid_credentials"
}
Creatives (v2)
Create a Creative
Create a reusable message creative with plain text or mapped offer/custom links.
POST
/
v2
/
creatives
curl -X POST "https://api.tracklysms.com/api/v2/creatives" \
-H "X-Api-Key: trk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Summer Sale Promo",
"message": "Hot deals just for you! Shop now: {{link1}} Reply STOP to opt out.",
"offer_links": [
{
"key": "link1",
"offer_id": "665f1a2b3c4d5e6f70819201",
"url_params": {"sub1": "sms"}
}
],
"status": "active",
"creative_type": "automated"
}'
import requests
response = requests.post(
"https://api.tracklysms.com/api/v2/creatives",
headers={
"X-Api-Key": "trk_your_api_key_here",
"Content-Type": "application/json"
},
json={
"name": "Summer Sale Promo",
"message": "Hot deals just for you! Shop now: {{link1}} Reply STOP to opt out.",
"offer_links": [
{
"key": "link1",
"offer_id": "665f1a2b3c4d5e6f70819201",
"url_params": {"sub1": "sms"}
}
],
"status": "active",
"creative_type": "automated"
}
)
data = response.json()
print(f"Created creative #{data['creative']['id']}")
print(f"Segments: {data['creative']['segment_count']}")
const response = await fetch(
"https://api.tracklysms.com/api/v2/creatives",
{
method: "POST",
headers: {
"X-Api-Key": "trk_your_api_key_here",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Summer Sale Promo",
message:
"Hot deals just for you! Shop now: {{link1}} Reply STOP to opt out.",
offer_links: [
{
key: "link1",
offer_id: "665f1a2b3c4d5e6f70819201",
url_params: { sub1: "sms" },
},
],
status: "active",
creative_type: "automated",
}),
}
);
const data = await response.json();
console.log(`Created creative #${data.creative.id}`);
{
"success": true,
"creative": {
"id": 101,
"name": "Summer Sale Promo",
"message": "Hot deals just for you! Shop now: {{link1}} Reply STOP to opt out.",
"offer_links": [
{
"key": "link1",
"offer_id": "665f1a2b3c4d5e6f70819201",
"url_params": {"sub1": "sms"}
}
],
"offers": ["665f1a2b3c4d5e6f70819201"],
"status": "active",
"creative_type": "automated",
"character_count": 114,
"segment_count": 1,
"created_at": "2025-06-10T12:00:00",
"slug": "cre_summer-sale-promo_a1b2c3",
"list_control_mode": "all",
"list_control_ids": []
}
}
{
"error": "message must contain at least one {{linkN}} placeholder",
"code": "missing_link_placeholder"
}
{
"error": "Offer not found: 665f1a2b3c4d5e6f70819201",
"code": "offer_not_found"
}
{
"error": "Invalid credentials",
"code": "invalid_credentials"
}
Creates a reusable creative in your account; this does not create a schedule or send a message.
Use
HTTP
creative_type: "one_time" for a manual-send asset, including plain text with no links.
The default automated type and the welcome type require at least one {{linkN}} placeholder.
Every placeholder must map to an offer from your account or an public HTTPS custom URL.
Create a plain-text asset for manual sends
curl -X POST "https://api.tracklysms.com/api/v2/creatives" \
-H "X-Api-Key: trk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "January Sale Reminder",
"message": "Our January sale starts today! Reply STOP to opt out.",
"creative_type": "one_time",
"status": "active",
"list_control_mode": "include",
"list_control_ids": [42]
}'
201 returns success: true and the full asset in creative. Keep its integer
creative.id for retrieval or updates. It is also
available through Use existing creative in the blast editor; text-only and custom-link-only
assets appear under Creatives without offers. To create the blast and optionally save
the asset in one request, use Create Schedule or Blast.
Body Parameters
string
required
Display name for the creative. Maximum 255 characters.
string
required
Message text containing non-whitespace text, at most 1600 characters. The message must pass content checks. Plain text is allowed for
one_time creatives. Other types require at least one {{linkN}} placeholder (e.g. {{link1}}). Placeholders are replaced with tracked short links at send time.array
Mappings for the
{{linkN}} placeholders in the message. Every placeholder must have a matching entry. Omit or use [] for a one_time message without placeholders; required for automated and welcome creatives.Show Offer link properties
Show Offer link properties
string
required
A unique, lowercase
linkN key. Must match a placeholder in the message (e.g. link1 corresponds to {{link1}}).string
Must be a 24-character hexadecimal ID, and the offer must exist and belong to your account. Exactly one of
offer_id or custom_url is required for each entry.string
A custom destination URL for this link, used instead of an offer. Must be an HTTPS URL, and must not point to a private or non-public address. Exactly one of
offer_id or custom_url is required for each entry.string
Deprecated and ignored. Use
custom_url for a custom destination.object
Optional additional query parameters to append to the link URL.
string
default:"active"
Initial status of the creative. Accepted values:
active, paused.string
default:"automated"
Accepted values:
automated, one_time, welcome. Set one_time for assets you will choose manually, including plain text without links. Omitting this field uses automated, which requires link placeholders.string
default:"all"
List targeting control mode. Accepted values:
all, include, exclude.array
List IDs as JSON integers to include or exclude when
list_control_mode is include or exclude. Boolean values are rejected.Response Fields
boolean
Indicates whether the creative was created successfully.
object
The full creative object.
Show Creative object properties
Show Creative object properties
integer
Unique identifier for the creative.
string
Display name of the creative.
string
Message text, optionally containing
{{linkN}} placeholders for a one_time creative.array
Array of offer link objects.
array
Array of offer ID strings referenced by this creative.
string
Current status of the creative.
string
The type of creative.
integer
Estimated character count. Link placeholders are estimated as 23 characters each, plus 34 characters of compliance overhead (e.g. opt-out language).
integer
Estimated SMS segment count, encoding-aware. The message is expanded (link placeholders as 23 characters plus 34 characters of compliance overhead) and segmented the way carriers bill it. GSM-7 messages fit 160 septets in a single segment, or 153 septets per part when concatenated; extension-table characters (
^ { } [ ] ~ | € \ and form-feed) cost 2 septets each. If the message contains any non-GSM-7 character (e.g. emoji), UCS-2 encoding applies instead: 70 characters single, 67 per concatenated part.datetime
ISO 8601 timestamp of when the creative was created.
string
Auto-generated unique slug for the creative.
string
List targeting control mode. One of:
all (default), include, exclude.array
List IDs used when
list_control_mode is include or exclude.Segment calculation: Each link placeholder is estimated as 23 characters (short link length), plus 34 characters of compliance overhead for required opt-out language. Segmentation is encoding-aware and matches carrier billing. GSM-7 messages fit 160 septets in a single segment, or 153 septets per part when concatenated (extension-table characters such as
€ { } [ ] ~ | ^ \ cost 2 septets each). If the message contains any non-GSM-7 character (e.g. emoji), UCS-2 encoding applies: 70 characters single, 67 per concatenated part.Examples
curl -X POST "https://api.tracklysms.com/api/v2/creatives" \
-H "X-Api-Key: trk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Summer Sale Promo",
"message": "Hot deals just for you! Shop now: {{link1}} Reply STOP to opt out.",
"offer_links": [
{
"key": "link1",
"offer_id": "665f1a2b3c4d5e6f70819201",
"url_params": {"sub1": "sms"}
}
],
"status": "active",
"creative_type": "automated"
}'
import requests
response = requests.post(
"https://api.tracklysms.com/api/v2/creatives",
headers={
"X-Api-Key": "trk_your_api_key_here",
"Content-Type": "application/json"
},
json={
"name": "Summer Sale Promo",
"message": "Hot deals just for you! Shop now: {{link1}} Reply STOP to opt out.",
"offer_links": [
{
"key": "link1",
"offer_id": "665f1a2b3c4d5e6f70819201",
"url_params": {"sub1": "sms"}
}
],
"status": "active",
"creative_type": "automated"
}
)
data = response.json()
print(f"Created creative #{data['creative']['id']}")
print(f"Segments: {data['creative']['segment_count']}")
const response = await fetch(
"https://api.tracklysms.com/api/v2/creatives",
{
method: "POST",
headers: {
"X-Api-Key": "trk_your_api_key_here",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Summer Sale Promo",
message:
"Hot deals just for you! Shop now: {{link1}} Reply STOP to opt out.",
offer_links: [
{
key: "link1",
offer_id: "665f1a2b3c4d5e6f70819201",
url_params: { sub1: "sms" },
},
],
status: "active",
creative_type: "automated",
}),
}
);
const data = await response.json();
console.log(`Created creative #${data.creative.id}`);
{
"success": true,
"creative": {
"id": 101,
"name": "Summer Sale Promo",
"message": "Hot deals just for you! Shop now: {{link1}} Reply STOP to opt out.",
"offer_links": [
{
"key": "link1",
"offer_id": "665f1a2b3c4d5e6f70819201",
"url_params": {"sub1": "sms"}
}
],
"offers": ["665f1a2b3c4d5e6f70819201"],
"status": "active",
"creative_type": "automated",
"character_count": 114,
"segment_count": 1,
"created_at": "2025-06-10T12:00:00",
"slug": "cre_summer-sale-promo_a1b2c3",
"list_control_mode": "all",
"list_control_ids": []
}
}
{
"error": "message must contain at least one {{linkN}} placeholder",
"code": "missing_link_placeholder"
}
{
"error": "Offer not found: 665f1a2b3c4d5e6f70819201",
"code": "offer_not_found"
}
{
"error": "Invalid credentials",
"code": "invalid_credentials"
}
Error Codes
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | missing_name | The name field is required. |
| 400 | missing_message | message must be a string containing non-whitespace text. |
| 400 | missing_offer_links | A nonempty offer_links array is required for automated and welcome creatives. |
| 400 | name_too_long | The name exceeds 255 characters. |
| 400 | message_too_long | The message exceeds 1600 characters. |
| 400 | blocked_content | The supplied message matches a prohibited-content rule. |
| 400 | invalid_status | The provided status is not active or paused. |
| 400 | invalid_creative_type | The provided creative_type is not automated, one_time, or welcome. |
| 400 | invalid_list_control_mode | The provided list_control_mode is not all, include, or exclude. |
| 400 | invalid_list_control_ids | list_control_ids must be an array of JSON integers; boolean values are rejected. |
| 400 | missing_link_placeholder | automated and welcome messages must contain at least one {{linkN}} placeholder. |
| 400 | missing_offer_links_for_placeholders | One or more {{linkN}} placeholders in the message do not have a matching entry in offer_links. |
| 400 | missing_offer_link_key | An entry in offer_links is missing the required key field. |
| 400 | invalid_offer_link | An entry in offer_links must include exactly one of offer_id or custom_url — an offer or a custom URL is required. |
| 400 | invalid_offer_link_offer_id | An offer_id in offer_links is not a valid 24-character hexadecimal ID. |
| 400 | invalid_offer_link_url | A custom_url in offer_links is not a valid HTTPS URL, or it points to a private or non-public address. |
| 400 | invalid_offer_links | Mappings must be objects with unique lowercase linkN keys and object url_params. |
| 400 | invalid_offer_link_source | An offer mapping specifies an unsupported offer_source. |
| 401 | invalid_credentials | API key is missing or invalid. |
| 403 | account_suspended | Your account is suspended. Resolve outstanding billing or contact support. |
| 400 | offer_not_found | An offer referenced in offer_links does not exist or does not belong to your account. |
Next Steps
Creative Templates
Learn template best practices
Create Schedule
Use your creative in a campaign