Templates
Create, read, update, and delete message templates.
Create Viber Template
POST /api/v1/templates/viber
Request Body
{
"name": "Welcome Message",
"description": "Sent to new customers",
"messageType": 1,
"templateContent": "Hello {{name}}! Welcome to our service.",
"viberServiceId": 12345,
"typeSpecificConfig": null
}
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Template name |
| description | string | No | Template description (max 500 chars) |
| messageType | integer | Yes | Viber message type (1-8) |
| templateContent | string | Yes | Message text with placeholders |
| viberServiceId | integer | Yes | Viber service ID |
| typeSpecificConfig | string | Depends | Type-specific configuration as a JSON string (use JSON.stringify or json.dumps) |
| smsFallbackEnabled | boolean | No | Enable SMS fallback for failed Viber messages (default: false) |
| smsFallbackTemplateContent | string | No | SMS message content for fallback (max 1600 chars, required if smsFallbackEnabled is true) |
| smsFallbackSenderId | integer | Conditional | Approved SMS Sender ID used for the fallback SMS. Required when smsFallbackEnabled is true (see GET /api/v1/sms-senders). |
When smsFallbackEnabled is true, failed Viber messages will automatically be sent via SMS. This triggers when Viber returns error codes for blocked users, non-Viber users, or incompatible devices. Both smsFallbackTemplateContent and smsFallbackSenderId are required when fallback is enabled. See SMS Fallback for details.
Message Types
| Type | Value | typeSpecificConfig |
|---|---|---|
| SimpleText | 1 | Not required |
| ImageOnly | 2 | { "imageUrl": "https://..." } |
| ButtonPlusText | 3 | { "buttonText": "...", "buttonUrl": "https://..." } |
| ImagePlusText | 4 | { "imageUrl": "...", "buttonText": "...", "buttonUrl": "..." } |
| Carousel | 5 | { "items": [...] } |
| Survey | 6 | { "options": [...] } |
| Video | 7 | { "videoUrl": "...", "thumbnailUrl": "...", "buttonText": "...", "buttonUrl": "...", "fileSize": ..., "duration": ... } |
| File | 8 | { "fileUrl": "...", "fileName": "...", "fileType": "..." } |
Response
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Welcome Message",
"description": "Sent to new customers",
"messageType": 1,
"channelType": "Viber",
"templateContent": "Hello {{name}}! Welcome to our service.",
"typeSpecificConfig": null,
"viberServiceId": 12345,
"smsFallbackEnabled": false,
"smsFallbackTemplateContent": null,
"smsFallbackSenderId": null,
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
Example: SimpleText Template (messageType: 1)
- cURL
- JavaScript
- Python
curl -X POST https://api.transformify.mk/api/v1/templates/viber \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Order Confirmation",
"description": "Sent when customer order is ready",
"messageType": 1,
"templateContent": "Hello {{name}}, your order {{orderId}} is ready!",
"viberServiceId": 12345
}'
const response = await fetch('https://api.transformify.mk/api/v1/templates/viber', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Order Confirmation',
description: 'Sent when customer order is ready',
messageType: 1,
templateContent: 'Hello {{name}}, your order {{orderId}} is ready!',
viberServiceId: 12345
})
});
const template = await response.json();
import requests
response = requests.post(
'https://api.transformify.mk/api/v1/templates/viber',
headers={
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
json={
'name': 'Order Confirmation',
'description': 'Sent when customer order is ready',
'messageType': 1,
'templateContent': 'Hello {{name}}, your order {{orderId}} is ready!',
'viberServiceId': 12345
}
)
template = response.json()
Example: ImageOnly Template (messageType: 2)
- cURL
- JavaScript
- Python
curl -X POST https://api.transformify.mk/api/v1/templates/viber \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Product Image",
"messageType": 2,
"viberServiceId": 12345,
"typeSpecificConfig": "{\"imageUrl\":\"https://cdn.example.com/products/product.jpg\"}"
}'
const response = await fetch('https://api.transformify.mk/api/v1/templates/viber', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Product Image',
messageType: 2,
viberServiceId: 12345,
typeSpecificConfig: JSON.stringify({
imageUrl: 'https://cdn.example.com/products/product.jpg'
})
})
});
const template = await response.json();
import requests
import json
response = requests.post(
'https://api.transformify.mk/api/v1/templates/viber',
headers={
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
json={
'name': 'Product Image',
'messageType': 2,
'viberServiceId': 12345,
'typeSpecificConfig': json.dumps({
'imageUrl': 'https://cdn.example.com/products/product.jpg'
})
}
)
template = response.json()
Note:
templateContentis optional for ImageOnly messages.
Example: ButtonPlusText Template (messageType: 3)
- cURL
- JavaScript
- Python
curl -X POST https://api.transformify.mk/api/v1/templates/viber \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Order Tracking",
"messageType": 3,
"templateContent": "Your order {{orderId}} has shipped!",
"viberServiceId": 12345,
"typeSpecificConfig": "{\"buttonText\":\"Track Order\",\"buttonUrl\":\"https://track.example.com/orders\"}"
}'
const response = await fetch('https://api.transformify.mk/api/v1/templates/viber', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Order Tracking',
messageType: 3,
templateContent: 'Your order {{orderId}} has shipped!',
viberServiceId: 12345,
typeSpecificConfig: JSON.stringify({
buttonText: 'Track Order',
buttonUrl: 'https://track.example.com/orders'
})
})
});
const template = await response.json();
import requests
import json
response = requests.post(
'https://api.transformify.mk/api/v1/templates/viber',
headers={
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
json={
'name': 'Order Tracking',
'messageType': 3,
'templateContent': 'Your order {{orderId}} has shipped!',
'viberServiceId': 12345,
'typeSpecificConfig': json.dumps({
'buttonText': 'Track Order',
'buttonUrl': 'https://track.example.com/orders'
})
}
)
template = response.json()
Example: ImagePlusText Template (messageType: 4)
- cURL
- JavaScript
- Python
curl -X POST https://api.transformify.mk/api/v1/templates/viber \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Product Promotion",
"messageType": 4,
"templateContent": "Check out {{productName}}! {{discount}}% off!",
"viberServiceId": 12345,
"typeSpecificConfig": "{\"imageUrl\":\"https://cdn.example.com/product.jpg\",\"buttonText\":\"Buy Now\",\"buttonUrl\":\"https://shop.example.com/product\"}"
}'
const response = await fetch('https://api.transformify.mk/api/v1/templates/viber', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Product Promotion',
messageType: 4,
templateContent: 'Check out {{productName}}! {{discount}}% off!',
viberServiceId: 12345,
typeSpecificConfig: JSON.stringify({
imageUrl: 'https://cdn.example.com/product.jpg',
buttonText: 'Buy Now',
buttonUrl: 'https://shop.example.com/product'
})
})
});
const template = await response.json();
import requests
import json
response = requests.post(
'https://api.transformify.mk/api/v1/templates/viber',
headers={
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
json={
'name': 'Product Promotion',
'messageType': 4,
'templateContent': 'Check out {{productName}}! {{discount}}% off!',
'viberServiceId': 12345,
'typeSpecificConfig': json.dumps({
'imageUrl': 'https://cdn.example.com/product.jpg',
'buttonText': 'Buy Now',
'buttonUrl': 'https://shop.example.com/product'
})
}
)
template = response.json()
Example: Carousel Template (messageType: 5)
- cURL
- JavaScript
- Python
curl -X POST https://api.transformify.mk/api/v1/templates/viber \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Product Showcase",
"messageType": 5,
"templateContent": "Check out our featured products!",
"viberServiceId": 12345,
"typeSpecificConfig": "{\"items\":[{\"title\":\"Wireless Headphones\",\"imageUrl\":\"https://cdn.example.com/headphones.jpg\",\"buttonText\":\"Buy\",\"buttonUrl\":\"https://shop.example.com/headphones\",\"secondaryButtonText\":\"Details\",\"secondaryButtonUrl\":\"https://shop.example.com/headphones/details\"},{\"title\":\"Smart Watch\",\"imageUrl\":\"https://cdn.example.com/watch.jpg\",\"buttonText\":\"Buy\",\"buttonUrl\":\"https://shop.example.com/watch\"}]}"
}'
const response = await fetch('https://api.transformify.mk/api/v1/templates/viber', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Product Showcase',
messageType: 5,
templateContent: 'Check out our featured products!',
viberServiceId: 12345,
typeSpecificConfig: JSON.stringify({
items: [
{
title: 'Wireless Headphones',
imageUrl: 'https://cdn.example.com/headphones.jpg',
buttonText: 'Buy',
buttonUrl: 'https://shop.example.com/headphones',
secondaryButtonText: 'Details',
secondaryButtonUrl: 'https://shop.example.com/headphones/details'
},
{
title: 'Smart Watch',
imageUrl: 'https://cdn.example.com/watch.jpg',
buttonText: 'Buy',
buttonUrl: 'https://shop.example.com/watch'
}
]
})
})
});
const template = await response.json();
import requests
import json
response = requests.post(
'https://api.transformify.mk/api/v1/templates/viber',
headers={
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
json={
'name': 'Product Showcase',
'messageType': 5,
'templateContent': 'Check out our featured products!',
'viberServiceId': 12345,
'typeSpecificConfig': json.dumps({
'items': [
{
'title': 'Wireless Headphones',
'imageUrl': 'https://cdn.example.com/headphones.jpg',
'buttonText': 'Buy',
'buttonUrl': 'https://shop.example.com/headphones',
'secondaryButtonText': 'Details',
'secondaryButtonUrl': 'https://shop.example.com/headphones/details'
},
{
'title': 'Smart Watch',
'imageUrl': 'https://cdn.example.com/watch.jpg',
'buttonText': 'Buy',
'buttonUrl': 'https://shop.example.com/watch'
}
]
})
}
)
template = response.json()
Note: Carousel items support optional
secondaryButtonText/secondaryButtonUrl(max 12 chars).templateContentis optional for Carousel messages (max 1000 chars).
Example: Survey Template (messageType: 6)
- cURL
- JavaScript
- Python
curl -X POST https://api.transformify.mk/api/v1/templates/viber \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Customer Satisfaction",
"messageType": 6,
"templateContent": "How satisfied are you with your purchase, {{name}}?",
"viberServiceId": 12345,
"typeSpecificConfig": "{\"options\":[{\"optionText\":\"Very Satisfied\",\"optionValue\":\"5\"},{\"optionText\":\"Satisfied\",\"optionValue\":\"4\"},{\"optionText\":\"Neutral\",\"optionValue\":\"3\"},{\"optionText\":\"Dissatisfied\",\"optionValue\":\"2\"}]}"
}'
const response = await fetch('https://api.transformify.mk/api/v1/templates/viber', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Customer Satisfaction',
messageType: 6,
templateContent: 'How satisfied are you with your purchase, {{name}}?',
viberServiceId: 12345,
typeSpecificConfig: JSON.stringify({
options: [
{ optionText: 'Very Satisfied', optionValue: '5' },
{ optionText: 'Satisfied', optionValue: '4' },
{ optionText: 'Neutral', optionValue: '3' },
{ optionText: 'Dissatisfied', optionValue: '2' }
]
})
})
});
const template = await response.json();
import requests
import json
response = requests.post(
'https://api.transformify.mk/api/v1/templates/viber',
headers={
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
json={
'name': 'Customer Satisfaction',
'messageType': 6,
'templateContent': 'How satisfied are you with your purchase, {{name}}?',
'viberServiceId': 12345,
'typeSpecificConfig': json.dumps({
'options': [
{'optionText': 'Very Satisfied', 'optionValue': '5'},
{'optionText': 'Satisfied', 'optionValue': '4'},
{'optionText': 'Neutral', 'optionValue': '3'},
{'optionText': 'Dissatisfied', 'optionValue': '2'}
]
})
}
)
template = response.json()
Example: Video Template (messageType: 7)
- cURL
- JavaScript
- Python
curl -X POST https://api.transformify.mk/api/v1/templates/viber \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Product Demo",
"messageType": 7,
"templateContent": "Check out this video about {{productName}}!",
"viberServiceId": 12345,
"typeSpecificConfig": "{\"videoUrl\":\"https://cdn.example.com/videos/demo.mp4\",\"thumbnailUrl\":\"https://cdn.example.com/videos/demo-thumb.jpg\",\"buttonText\":\"Watch\",\"buttonUrl\":\"https://example.com/videos\"}"
}'
const response = await fetch('https://api.transformify.mk/api/v1/templates/viber', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Product Demo',
messageType: 7,
templateContent: 'Check out this video about {{productName}}!',
viberServiceId: 12345,
typeSpecificConfig: JSON.stringify({
videoUrl: 'https://cdn.example.com/videos/demo.mp4',
thumbnailUrl: 'https://cdn.example.com/videos/demo-thumb.jpg',
buttonText: 'Watch',
buttonUrl: 'https://example.com/videos'
})
})
});
const template = await response.json();
import requests
import json
response = requests.post(
'https://api.transformify.mk/api/v1/templates/viber',
headers={
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
json={
'name': 'Product Demo',
'messageType': 7,
'templateContent': 'Check out this video about {{productName}}!',
'viberServiceId': 12345,
'typeSpecificConfig': json.dumps({
'videoUrl': 'https://cdn.example.com/videos/demo.mp4',
'thumbnailUrl': 'https://cdn.example.com/videos/demo-thumb.jpg',
'buttonText': 'Watch',
'buttonUrl': 'https://example.com/videos'
})
}
)
template = response.json()
Note:
buttonText/buttonUrlare optional.fileSizeanddurationare automatically populated during template creation via HTTP HEAD request.templateContentis optional.
Example: File Template (messageType: 8)
- cURL
- JavaScript
- Python
curl -X POST https://api.transformify.mk/api/v1/templates/viber \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Invoice Delivery",
"messageType": 8,
"templateContent": "Your invoice for order {{orderId}} is ready.",
"viberServiceId": 12345,
"typeSpecificConfig": "{\"fileUrl\":\"https://cdn.example.com/invoices/{{invoiceId}}.pdf\",\"fileName\":\"Invoice.pdf\",\"fileType\":\"pdf\"}"
}'
const response = await fetch('https://api.transformify.mk/api/v1/templates/viber', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Invoice Delivery',
messageType: 8,
templateContent: 'Your invoice for order {{orderId}} is ready.',
viberServiceId: 12345,
typeSpecificConfig: JSON.stringify({
fileUrl: 'https://cdn.example.com/invoices/{{invoiceId}}.pdf',
fileName: 'Invoice.pdf',
fileType: 'pdf'
})
})
});
const template = await response.json();
import requests
import json
response = requests.post(
'https://api.transformify.mk/api/v1/templates/viber',
headers={
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
json={
'name': 'Invoice Delivery',
'messageType': 8,
'templateContent': 'Your invoice for order {{orderId}} is ready.',
'viberServiceId': 12345,
'typeSpecificConfig': json.dumps({
'fileUrl': 'https://cdn.example.com/invoices/{{invoiceId}}.pdf',
'fileName': 'Invoice.pdf',
'fileType': 'pdf'
})
}
)
template = response.json()
Note: Placeholders are supported in
templateContentandfileUrl.templateContentis optional.fileSizeis automatically populated via HTTP HEAD request during template creation.Supported file types: pdf, doc, docx, xls, xlsx, ppt, pptx, txt, csv, rtf, zip, rar
Create SMS Template
POST /api/v1/templates/sms
Request Body
{
"name": "OTP Code",
"description": "Verification code",
"templateContent": "Your code is {{code}}. Valid for {{expiry}} minutes.",
"smsSenderId": 42
}
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Template name |
| description | string | No | Template description (max 500 chars) |
| templateContent | string | Yes | Message text (max 1600 chars) |
| smsSenderId | integer | No | SMS Sender ID (must be approved and belong to your company). Use GET /api/v1/sms-senders to list available senders. |
When smsSenderId is set, campaigns created from this template will use the specified sender. You can retrieve your approved SMS senders via the GET /api/v1/sms-senders endpoint.
Response
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "OTP Code",
"description": "Verification code",
"messageType": 1,
"channelType": "Smpp",
"templateContent": "Your code is {{code}}. Valid for {{expiry}} minutes.",
"smsSenderId": 42,
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
List Templates
GET /api/v1/templates
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| page | integer | 1 | Page number |
| pageSize | integer | 20 | Items per page (max 100) |
Response
{
"templates": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Welcome Message",
"description": "Sent to new customers",
"messageType": 1,
"channelType": "Viber",
"templateContent": "Hello {{name}}!",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
],
"totalCount": 1,
"page": 1,
"pageSize": 20,
"totalPages": 1
}
Get Template
GET /api/v1/templates/{id}
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | UUID | Template ID |
Response
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Welcome Message",
"description": "Sent to new customers",
"messageType": 1,
"channelType": "Viber",
"templateContent": "Hello {{name}}!",
"typeSpecificConfig": null,
"viberServiceId": 12345,
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
Update Template
PUT /api/v1/templates/{id}
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | UUID | Template ID |
Request Body
Same as create request.
The channelType of a template is preserved on update regardless of the request body. A Viber template remains a Viber template, and an SMS template remains an SMS template.
Response
Updated template object.
Delete Template
DELETE /api/v1/templates/{id}
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | UUID | Template ID |
Response
204 No Content
SMS Fallback
SMS fallback allows Viber templates to automatically send an SMS when Viber delivery fails.
Enabling SMS Fallback
Add smsFallbackEnabled, smsFallbackTemplateContent, and smsFallbackSenderId to your Viber template:
{
"name": "Order Notification with Fallback",
"messageType": 1,
"templateContent": "Hello {{name}}! Your order {{orderId}} is ready.",
"viberServiceId": 12345,
"smsFallbackEnabled": true,
"smsFallbackTemplateContent": "Hi {{name}}, your order {{orderId}} is ready. Reply STOP to opt out.",
"smsFallbackSenderId": 42
}
smsFallbackSenderId must be an approved SMS Sender belonging to your company (see GET /api/v1/sms-senders). Campaigns created from this template — via POST /api/v1/viber/send, POST /api/v1/viber/campaigns, or the transactional add-message endpoint — carry this sender so the fallback SMS can be delivered. Without it the fallback SMS cannot be sent.
When Fallback Triggers
SMS fallback is triggered when Viber returns these error codes:
| Code | Error | Description |
|---|---|---|
| 8 | USER_BLOCKED | User has blocked the Viber service |
| 9 | NOT_VIBER_USER | Recipient is not registered on Viber |
| 10 | NO_SUITABLE_DEVICE | No compatible device for Viber |
Best Practices
- Keep SMS content under 160 characters for a single SMS segment
- Maximum SMS length is 1600 characters (approximately 10 segments)
- Use the same
{{placeholder}}syntax as your Viber template - SMS fallback only applies to Viber templates (not SMS templates)
- A valid, approved
smsFallbackSenderIdis mandatory whenever fallback is enabled
Errors
| Status | Error | Description |
|---|---|---|
| 400 | Bad Request | Invalid template configuration |
| 400 | Bad Request | Media URL not accessible |
| 400 | Bad Request | SMS fallback content required when enabled |
| 400 | Bad Request | SMS fallback sender required when enabled |
| 400 | Bad Request | SMS fallback sender does not belong to your company |
| 401 | Unauthorized | Invalid API key |
| 404 | Not Found | Template not found |