Viber
Send Viber messages to recipients. Supports all Viber message types including text, images, buttons, carousels, surveys, video, and files.
Three sending patterns are available:
- Bulk — Send to many recipients using a template (marketing, announcements)
- Transactional — Create a long-lived campaign, then add messages one at a time (notifications, alerts)
- Direct Send — Send simple text messages without a template
Send Viber Messages (Bulk)
Send Viber messages to one or more recipients using a template. Creates a campaign and all messages in a single request.
POST /api/v1/viber/send
Request Body
{
"templateId": "550e8400-e29b-41d4-a716-446655440000",
"recipients": [
{
"phoneNumber": "38970123456",
"externalUserId": "customer_001",
"placeholders": {
"name": "John",
"orderId": "ORD-12345"
}
},
{
"phoneNumber": "38970123457",
"externalUserId": "customer_002",
"placeholders": {
"name": "Jane",
"orderId": "ORD-12346"
}
}
],
"placeholders": {
"companyName": "Acme Corp"
}
}
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| templateId | UUID | Yes | ID of a Viber template |
| recipients | array | Yes | List of recipients |
| recipients[].phoneNumber | string | Yes | Phone number (optional + prefix, 10-15 digits; regex: ^\+?\d{10,15}$) |
| recipients[].externalUserId | string | No | Your identifier for tracking |
| recipients[].placeholders | object | No | Per-recipient template placeholders |
| placeholders | object | No | Default placeholders (fallback for recipients without their own) |
The template must be a Viber template. SMS templates will be rejected with a 400 error.
Response
{
"campaignId": "660e8400-e29b-41d4-a716-446655440001",
"messageCount": 50000,
"status": "Queuing",
"queuedAt": "2024-01-15T10:30:00Z",
"message": "Campaign accepted. 50000 recipients are being queued for insertion. Poll GET /api/v1/campaigns/{id} for status, then call POST /api/v1/campaigns/{id}/start when Ready."
}
Response Fields
| Field | Type | Description |
|---|---|---|
| campaignId | UUID | ID of the created campaign |
| messageCount | integer | Number of valid recipients accepted |
| status | string | Always Queuing on success — messages are being inserted asynchronously |
| queuedAt | datetime | When the request was accepted |
| message | string | Instructions including count of any skipped invalid numbers |
Async Flow
For large sends, the API returns 202 Accepted immediately while messages are prepared in the background. Follow this three-step flow:
Step 1 — Send (POST /api/v1/viber/send)
Returns 202 with status: "Queuing" and a campaignId.
Step 2 — Poll until Ready (GET /api/v1/campaigns/{id})
Poll the campaign until status changes from Queuing → Ready. This typically takes seconds to a few minutes depending on recipient count.
Step 3 — Start delivery (POST /api/v1/campaigns/{id}/start)
When the campaign is Ready, call this endpoint to begin message delivery. The status moves to InProgress.
POST /api/v1/viber/send → 202, campaignId, status: "Queuing"
GET /api/v1/campaigns/{id} → poll until status: "Ready"
POST /api/v1/campaigns/{id}/start → status: "InProgress", delivery begins
If the campaign shows status: "Failed", the message preparation encountered an error. Re-send the original request to create a new campaign.
Example
- cURL
- JavaScript
- Python
curl -X POST https://api.transformify.mk/api/v1/viber/send \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"templateId": "550e8400-e29b-41d4-a716-446655440000",
"recipients": [
{
"phoneNumber": "38970123456",
"externalUserId": "order_001",
"placeholders": { "name": "John", "orderId": "ORD-12345" }
},
{
"phoneNumber": "38970123457",
"externalUserId": "order_002",
"placeholders": { "name": "Jane", "orderId": "ORD-12346" }
}
],
"placeholders": { "companyName": "Acme Corp" }
}'
const response = await fetch('https://api.transformify.mk/api/v1/viber/send', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
templateId: '550e8400-e29b-41d4-a716-446655440000',
recipients: [
{
phoneNumber: '38970123456',
externalUserId: 'order_001',
placeholders: { name: 'John', orderId: 'ORD-12345' }
},
{
phoneNumber: '38970123457',
externalUserId: 'order_002',
placeholders: { name: 'Jane', orderId: 'ORD-12346' }
}
],
placeholders: { companyName: 'Acme Corp' }
})
});
const data = await response.json();
import requests
response = requests.post(
'https://api.transformify.mk/api/v1/viber/send',
headers={
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
json={
'templateId': '550e8400-e29b-41d4-a716-446655440000',
'recipients': [
{
'phoneNumber': '38970123456',
'externalUserId': 'order_001',
'placeholders': {'name': 'John', 'orderId': 'ORD-12345'}
},
{
'phoneNumber': '38970123457',
'externalUserId': 'order_002',
'placeholders': {'name': 'Jane', 'orderId': 'ORD-12346'}
}
],
'placeholders': {'companyName': 'Acme Corp'}
}
)
data = response.json()
SMS Fallback
If your Viber template has SMS fallback enabled (smsFallbackEnabled: true), messages that fail Viber delivery will automatically be resent via SMS using the fallback template.
See Templates > SMS Fallback for configuration details.
Create Transactional Campaign
Create a long-lived Viber campaign for transactional messages. The campaign stays in InProgress status and accepts messages indefinitely.
POST /api/v1/viber/campaigns
Request Body
{
"name": "Appointment Reminders",
"description": "Automated Viber notifications for appointments",
"templateId": "550e8400-e29b-41d4-a716-446655440000"
}
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Campaign name (max 200 chars) |
| description | string | No | Campaign description (max 1000 chars) |
| templateId | UUID | Yes | ID of a Viber template |
The template must be a Viber template. SMS templates will be rejected with a 400 error.
Response
{
"campaignId": "660e8400-e29b-41d4-a716-446655440001",
"name": "Appointment Reminders",
"status": "InProgress",
"createdAt": "2024-01-15T10:30:00Z"
}
Example
- cURL
- JavaScript
- Python
curl -X POST https://api.transformify.mk/api/v1/viber/campaigns \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Appointment Reminders",
"description": "Automated Viber notifications for appointments",
"templateId": "550e8400-e29b-41d4-a716-446655440000"
}'
const response = await fetch('https://api.transformify.mk/api/v1/viber/campaigns', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Appointment Reminders',
description: 'Automated Viber notifications for appointments',
templateId: '550e8400-e29b-41d4-a716-446655440000'
})
});
const campaign = await response.json();
import requests
response = requests.post(
'https://api.transformify.mk/api/v1/viber/campaigns',
headers={
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
json={
'name': 'Appointment Reminders',
'description': 'Automated Viber notifications for appointments',
'templateId': '550e8400-e29b-41d4-a716-446655440000'
}
)
campaign = response.json()
Add Message to Campaign
Add a single message to a transactional Viber campaign. The message is queued for immediate processing.
POST /api/v1/viber/campaigns/{id}/messages
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | UUID | Campaign ID |
Request Body
{
"phoneNumber": "38970123456",
"externalUserId": "appointment_789",
"placeholders": {
"name": "John",
"appointmentDate": "2024-01-20",
"appointmentTime": "14:00"
}
}
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| phoneNumber | string | Yes | Recipient phone number (optional + prefix, 10-15 digits) |
| externalUserId | string | No | Your identifier for tracking |
| placeholders | object | No | Template substitution values |
Response
{
"messageId": "770e8400-e29b-41d4-a716-446655440002",
"campaignId": "660e8400-e29b-41d4-a716-446655440001",
"phoneNumber": "38970123456",
"status": "Pending",
"queuedAt": "2024-01-15T10:31:00Z"
}
Example
- cURL
- JavaScript
- Python
curl -X POST https://api.transformify.mk/api/v1/viber/campaigns/660e8400-e29b-41d4-a716-446655440001/messages \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"phoneNumber": "38970123456",
"externalUserId": "appointment_789",
"placeholders": {
"name": "John",
"appointmentDate": "2024-01-20",
"appointmentTime": "14:00"
}
}'
const campaignId = '660e8400-e29b-41d4-a716-446655440001';
const response = await fetch(
`https://api.transformify.mk/api/v1/viber/campaigns/${campaignId}/messages`,
{
method: 'POST',
headers: {
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
phoneNumber: '38970123456',
externalUserId: 'appointment_789',
placeholders: {
name: 'John',
appointmentDate: '2024-01-20',
appointmentTime: '14:00'
}
})
}
);
const message = await response.json();
import requests
campaign_id = '660e8400-e29b-41d4-a716-446655440001'
response = requests.post(
f'https://api.transformify.mk/api/v1/viber/campaigns/{campaign_id}/messages',
headers={
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
json={
'phoneNumber': '38970123456',
'externalUserId': 'appointment_789',
'placeholders': {
'name': 'John',
'appointmentDate': '2024-01-20',
'appointmentTime': '14:00'
}
}
)
message = response.json()
Send Viber Text
Send simple text Viber messages without creating a template. Provide raw message content, a Viber Service ID, and a list of recipients.
POST /api/v1/viber/send-text
Request Body
{
"messageContent": "Your appointment is confirmed for tomorrow at 10:00 AM.",
"viberServiceId": 1,
"smsFallbackText": "Your appointment is confirmed for tomorrow at 10:00 AM.",
"smsFallbackSenderId": 1,
"recipients": [
{
"phoneNumber": "38970123456",
"externalUserId": "customer_001"
},
{
"phoneNumber": "38970123457"
}
]
}
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| messageContent | string | Yes | Viber message text (1-1,000 characters, no placeholders) |
| viberServiceId | integer | Yes | ID of an approved Viber Service (see GET /api/v1/viber-services) |
| smsFallbackText | string | No | SMS text sent when Viber fails (USER_BLOCKED, NOT_VIBER_USER, NO_SUITABLE_DEVICE). GSM-7 characters only. Max 1,600 chars. |
| smsFallbackSenderId | integer | Conditional | Approved SMS Sender ID used for the fallback SMS. Required when smsFallbackText is provided (see GET /api/v1/sms-senders). |
| recipients | array | Yes | List of recipients |
| recipients[].phoneNumber | string | Yes | Phone number (optional + prefix, 10-15 digits) |
| recipients[].externalUserId | string | No | Your identifier for tracking (max 100 chars) |
The Viber Service must belong to your company and have Approved status. Use GET /api/v1/viber-services to list your available services.
When smsFallbackText is provided, if a Viber delivery fails because the recipient is blocked, not a Viber user, or has no suitable device, an SMS is automatically sent with the fallback text. The SMS text must use GSM-7 compatible characters only (Latin letters, digits, standard punctuation).
smsFallbackSenderId is required whenever smsFallbackText is set — the fallback SMS is delivered from this sender. It must be an approved SMS Sender belonging to your company; otherwise the request is rejected with HTTP 400.
Response
{
"campaignId": "660e8400-e29b-41d4-a716-446655440001",
"messageCount": 2,
"skippedCount": 0,
"invalidPhoneNumbers": [],
"status": "Pending",
"queuedAt": "2024-01-15T10:30:00Z",
"message": "2 messages queued for delivery."
}
Response Fields
| Field | Type | Description |
|---|---|---|
| campaignId | UUID | Campaign identifier for tracking |
| messageCount | integer | Number of valid recipients accepted |
| skippedCount | integer | Number of invalid phone numbers skipped |
| invalidPhoneNumbers | array | List of skipped invalid phone numbers |
| status | string | Always Pending — messages are queued for immediate processing |
| queuedAt | datetime | When the request was accepted |
| message | string | Summary including count of any skipped numbers |
Example
- cURL
- JavaScript
- Python
curl -X POST https://api.transformify.mk/api/v1/viber/send-text \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"messageContent": "Your appointment is confirmed for tomorrow at 10:00 AM.",
"viberServiceId": 1,
"smsFallbackText": "Your appointment is confirmed for tomorrow at 10:00 AM.",
"smsFallbackSenderId": 1,
"recipients": [
{ "phoneNumber": "38970123456", "externalUserId": "customer_001" },
{ "phoneNumber": "38970123457" }
]
}'
const response = await fetch('https://api.transformify.mk/api/v1/viber/send-text', {
method: 'POST',
headers: {
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
messageContent: 'Your appointment is confirmed for tomorrow at 10:00 AM.',
viberServiceId: 1,
smsFallbackText: 'Your appointment is confirmed for tomorrow at 10:00 AM.',
smsFallbackSenderId: 1,
recipients: [
{ phoneNumber: '38970123456', externalUserId: 'customer_001' },
{ phoneNumber: '38970123457' }
]
})
});
const data = await response.json();
import requests
response = requests.post(
'https://api.transformify.mk/api/v1/viber/send-text',
headers={
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
},
json={
'messageContent': 'Your appointment is confirmed for tomorrow at 10:00 AM.',
'viberServiceId': 1,
'smsFallbackText': 'Your appointment is confirmed for tomorrow at 10:00 AM.',
'smsFallbackSenderId': 1,
'recipients': [
{'phoneNumber': '38970123456', 'externalUserId': 'customer_001'},
{'phoneNumber': '38970123457'}
]
}
)
data = response.json()
Errors
| Status | Error | Description |
|---|---|---|
| 400 | Invalid template channel type | Template is not a Viber template |
| 400 | No valid phone numbers | All recipient phone numbers are invalid |
| 400 | Too many recipients | Exceeds maximum recipients per request |
| 400 | Campaign is not active | Campaign status is not InProgress |
| 400 | Cannot add messages to bulk campaign | Campaign is not transactional |
| 400 | Campaign is not a Viber campaign | Campaign channel type is not Viber |
| 400 | Invalid Viber Service | Service ID not found, not approved, or wrong company (direct send) |
| 400 | SMS fallback text contains unsupported characters | smsFallbackText has non-GSM-7 characters (direct send) |
| 400 | SMS fallback sender required | smsFallbackText provided without smsFallbackSenderId (direct send) |
| 400 | Invalid SMS fallback sender | smsFallbackSenderId not found, not approved, or wrong company (direct send) |
| 401 | Unauthorized | Invalid API key |
| 404 | Not Found | Template or campaign not found |
| 429 | Too many pending messages | Pending message capacity exceeded |