curl -X PATCH "https://api.telzino.com/v1/sms-templates/3f2a0000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "body": "Updated message. Reply STOP to opt out.", "status": "active" }'
{ "data": { "id": "3f2a...", "status": "active", "version": 4 } }
{ "error": "validation_error", "issues": [ { "path": "", "message": "Unrecognized key(s) in object: 'message_type'" } ] }
{ "error": "sms_templates_locked" }
{ "error": "version_conflict", "message": "This template was changed since you read it. Re-read it and retry." }
SMS Templates
Update SMS Template
Partially updates an SMS template. message_type, organization_id, version, and the server-managed media object (media_key) are immutable and are rejected.
PATCH
/
v1
/
sms-templates
/
{templateId}
curl -X PATCH "https://api.telzino.com/v1/sms-templates/3f2a0000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "body": "Updated message. Reply STOP to opt out.", "status": "active" }'
{ "data": { "id": "3f2a...", "status": "active", "version": 4 } }
{ "error": "validation_error", "issues": [ { "path": "", "message": "Unrecognized key(s) in object: 'message_type'" } ] }
{ "error": "sms_templates_locked" }
{ "error": "version_conflict", "message": "This template was changed since you read it. Re-read it and retry." }
Path Parameters
string
required
The template’s UUID. A value that is not a well-formed UUID returns
400
({"error": "invalid templateId"}) — the format check runs before the lookup. A
well-formed UUID that does not belong to one of the account’s organizations returns 404.Body Parameters
Only author-editable fields are accepted; unknown fields are rejected (400).
string
Max 100 chars, unique within the organization.
string
Max 300 chars.
string
Message body with
{{token}} placeholders. Max 5000 characters; longer is rejected with 400.string[]
Free-form tags. Max 50 tags, each up to 64 characters.
string
Sender identity (phone number, short code, or alphanumeric sender ID) overriding the From Number on the agent’s Send SMS function. A phone number must be an SMS-capable number on the organization’s Twilio account (see Twilio) — verified at agent assignment, not here; an unowned number is rejected at send time with error
21606. Max 64 characters.string
draft, active, or archived. Activating a template flagged is_first_contact requires opt-out language in the body (e.g. “Reply STOP to opt out”), else 400.Moving a template off active breaks every agent already bound to it. Archiving — or
returning it to draft — only hides it from selection; existing bindings are left in
place, and from the next call those agents’ Send SMS fails with template_not_found
instead of sending. The API does not warn you, and unlike DELETE — which is refused while
any agent references the template — this is not blocked. Re-point the bound agents first.boolean
Whether this may be the first SMS to a recipient.
string[]
ISO 3166-1 alpha-2 country codes (e.g.
["US","CA"]). Case-insensitive — values are upper-cased before storage, so ["us"] and ["US"] are equivalent. UK and EL are accepted as aliases for GB and GR. A code that is not ISO 3166-1 alpha-2 is rejected with 400: it would match no recipient and silently block every send from the template. Max 300 entries.string
MMS media URL (max 2048 chars); may contain
{{token}} placeholders resolved at send time. The uploaded media object itself (media_key) is server-managed and cannot be set here.object
Token → sample-value map (max 100 keys, values max 1000 chars) used to preview the rendered message.
integer
Optional optimistic-concurrency guard. Pass the
version you read from the template; if
it has changed since, the update is refused with 409 instead of overwriting whatever
landed in between. Omit it to update unconditionally (last write wins).This is a guard, not a field — it is never written. The stored version is always
incremented by the server.curl -X PATCH "https://api.telzino.com/v1/sms-templates/3f2a0000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "body": "Updated message. Reply STOP to opt out.", "status": "active" }'
{ "data": { "id": "3f2a...", "status": "active", "version": 4 } }
{ "error": "validation_error", "issues": [ { "path": "", "message": "Unrecognized key(s) in object: 'message_type'" } ] }
{ "error": "sms_templates_locked" }
{ "error": "version_conflict", "message": "This template was changed since you read it. Re-read it and retry." }
For an unknown or immutable field the offending key appears in
message, not in path —
path is empty, because the rejection belongs to the object rather than to one of its
properties. Error handling that matches on issues[].path will not catch these. For a
field that exists but holds a bad value (e.g. name over 100 characters), path is the
field name as you would expect.Concurrency
Read-modify-write against this endpoint is last-write-wins by default: two callers that bothGET a template and then PATCH it will each succeed, and the first one’s changes
are lost with no error.
To avoid that, send the version you read as expected_version. A mismatch returns
409 version_conflict and nothing is written — re-read the template, reapply your change,
and retry.
cURL
# Read, then write only if nothing changed in between.
VERSION=$(curl -s "https://api.telzino.com/v1/sms-templates/$ID" \
-H "Authorization: Bearer $TOKEN" | jq '.data.version')
curl -X PATCH "https://api.telzino.com/v1/sms-templates/$ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{ \"body\": \"Updated message. Reply STOP to opt out.\", \"expected_version\": $VERSION }"
409 is also returned when the new name collides with another template in the same
organization — distinguishable by error, which is version_conflict for a concurrency
clash and carries the duplicate-name message otherwise.
Omitting expected_version narrows but does not eliminate the risk: the write is still
guarded against anything landing between this request’s own read and its update. That
window is very small, and it returns the same 409 version_conflict, so a caller need only
handle one case.
Availability
Returns403 when SMS Template Manager is locked for the template’s organization —
except an archive-only patch, which is never blocked. Archiving stays available even
when the feature is otherwise locked, so a locked organization can always tidy up.
An archive-only patch is one whose only content change is {"status": "archived"}.
expected_version may accompany it and still counts as archive-only, since it is a
concurrency guard rather than a field being written:
{ "status": "archived" } // archive-only
{ "status": "archived", "expected_version": 3 } // also archive-only
{ "status": "archived", "name": "Renamed" } // NOT archive-only — gated
