Sending Messages
Learn how to send messages through the Transformify API.
Overview
Messages are sent through channel-specific endpoints:
| Channel | Endpoint | Use case |
|---|---|---|
| SMS Transactional | POST /sms/campaigns + POST /sms/campaigns/{id}/messages | OTP, order notifications, alerts |
| SMS Promotional | POST /sms/promotional | Marketing campaigns, bulk offers |
| SMS Direct | POST /sms/send | Quick sends without a template |
| Viber Bulk | POST /viber/send | Rich messages, marketing, notifications |
| Viber Transactional | POST /viber/campaigns + POST /viber/campaigns/{id}/messages | Event-triggered Viber notifications |
| Viber Direct | POST /viber/send-text | Quick text sends without a template |
SMS — Promotional (Bulk)
Use bulk sending for marketing campaigns or batch notifications via SMS.
Basic Example
curl -X POST https://api.transformify.mk/api/v1/sms/promotional \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"templateId": "550e8400-e29b-41d4-a716-446655440000",
"recipients": [
{
"phoneNumber": "38970111111",
"placeholders": { "name": "John", "promoCode": "JOHN20" }
},
{
"phoneNumber": "38970222222",
"placeholders": { "name": "Jane", "promoCode": "JANE20" }
},
{
"phoneNumber": "38970333333",
"placeholders": { "name": "Bob", "promoCode": "BOB20" }
}
]
}'
Response
{
"campaignId": "660e8400-e29b-41d4-a716-446655440001",
"messageCount": 3,
"skippedCount": 0,
"invalidPhoneNumbers": [],
"status": "Queuing",
"queuedAt": "2024-01-15T10:30:00Z",
"message": "Campaign accepted. 3 recipients are being queued for insertion. Poll GET /api/v1/campaigns/{id} for status, then call POST /api/v1/campaigns/{id}/start when Ready."
}
Bulk sends return immediately with status: "Queuing". Poll GET /api/v1/campaigns/{id} until status is Ready, then call POST /api/v1/campaigns/{id}/start to begin delivery.
With Fallback Placeholders
Use request-level placeholders as defaults for recipients without their own:
{
"templateId": "template-id",
"recipients": [
{
"phoneNumber": "38970111111",
"placeholders": { "name": "John", "discount": "25%" }
},
{ "phoneNumber": "38970222222" }
],
"placeholders": {
"name": "Valued Customer",
"discount": "10%"
}
}
In this example:
- First recipient gets "John" and "25%"
- Second recipient gets "Valued Customer" and "10%" (from request-level fallback)
Viber — Bulk Sending
Use /viber/send for Viber messages. Works the same as SMS promotional — send to multiple recipients in a single request.
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": "38970111111",
"placeholders": { "name": "John" }
},
{
"phoneNumber": "38970222222",
"placeholders": { "name": "Jane" }
}
]
}'
When you create an SMS template with a smsSenderId, that sender is automatically used for all campaigns created from the template — both transactional and promotional. Use GET /api/v1/sms-senders to list your approved senders.
SMS — Transactional
Use transactional SMS for real-time messages triggered by events (OTP codes, order updates, alerts).
Step 1: Create a Campaign (once)
curl -X POST https://api.transformify.mk/api/v1/sms/campaigns \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Order Notifications",
"templateId": "template-id"
}'
Save the campaignId from the response.
Step 2: Add Messages (on each event)
curl -X POST https://api.transformify.mk/api/v1/sms/campaigns/{campaignId}/messages \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"phoneNumber": "38970123456",
"externalUserId": "order_12345",
"placeholders": {
"orderId": "ORD-12345",
"status": "Shipped"
}
}'
Viber — Transactional
Use transactional Viber for real-time messages triggered by events (appointment reminders, delivery updates, alerts).
Step 1: Create a Campaign (once)
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",
"templateId": "viber-template-id"
}'
Save the campaignId from the response.
Step 2: Add Messages (on each event)
curl -X POST https://api.transformify.mk/api/v1/viber/campaigns/{campaignId}/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"
}
}'
SMS — Direct Send
Send SMS messages without creating a template or managing campaigns. Just provide the message content, an SMS Sender ID, and a list of recipients.
curl -X POST https://api.transformify.mk/api/v1/sms/send \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"messageContent": "Your order #12345 has been shipped!",
"smsSenderId": 1,
"recipients": [
{ "phoneNumber": "38970123456", "externalUserId": "customer_001" },
{ "phoneNumber": "38970123457" }
]
}'
Direct send endpoints do not use templates or placeholders. The messageContent is sent as-is to all recipients.
Viber — Direct Send
Send simple text Viber messages without creating a template or managing campaigns. Just provide the message content, a Viber Service ID, and a list of recipients.
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,
"recipients": [
{ "phoneNumber": "38970123456", "externalUserId": "customer_001" },
{ "phoneNumber": "38970123457" }
]
}'
Only simple text messages are supported via direct send. For rich Viber messages (images, buttons, carousels, etc.), use a template with POST /viber/send.
Phone Number Format
Phone numbers should be:
- 10-15 digits
- Optional
+prefix (will be stripped) - No spaces, dashes, or parentheses
| Input | Normalized | Valid |
|---|---|---|
+38970123456 | 38970123456 | ✓ |
38970123456 | 38970123456 | ✓ |
070-123-456 | Invalid | ✗ |
123 | Invalid | ✗ |
Placeholders
Placeholders use {{name}} syntax and are replaced with values from the placeholders object.
Template:
Hello {{name}}, your order #{{orderId}} is ready!
Placeholders:
{
"name": "John",
"orderId": "12345"
}
Result:
Hello John, your order #12345 is ready!
Placeholder Precedence
- Recipient-level placeholders are used first (if provided)
- Request-level placeholders are used as fallback (if recipient has none)
See the Bulk Sending examples above for per-recipient placeholder usage.
Message Status Flow
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌───────────┐
│ Pending │───▶│ Sending │───▶│ Sent │───▶│ Delivered │
└─────────┘ └─────────┘ └─────────┘ └───────────┘
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│ Failed │ │ Seen │
└─────────┘ └─────────┘
| Status | Description |
|---|---|
| Pending | Message created, waiting to be processed |
| Sending | Message is being sent |
| Sent | Message sent to the provider |
| Delivered | Message delivered to recipient's device |
| Seen | Recipient viewed the message (Viber only) |
| Failed | Message failed to send |
Checking Message Status
Query messages by phone number or external user ID:
curl -X GET "https://api.transformify.mk/api/v1/messages?phoneNumber=38970123456" \
-H "X-API-Key: your-api-key"
Or by external user ID:
curl -X GET "https://api.transformify.mk/api/v1/messages?externalUserId=order_12345" \
-H "X-API-Key: your-api-key"
You can also filter by date range:
curl -X GET "https://api.transformify.mk/api/v1/messages?phoneNumber=38970123456&startDate=2024-01-01&endDate=2024-01-31" \
-H "X-API-Key: your-api-key"
Response
{
"messages": [
{
"id": "msg-id-1",
"campaignId": "660e8400-e29b-41d4-a716-446655440001",
"phoneNumber": "38970123456",
"status": "Delivered",
"details": "Message delivered successfully",
"externalUserId": "order_12345",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:31:00Z"
}
],
"totalCount": 1,
"page": 1,
"pageSize": 20,
"totalPages": 1
}
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| phoneNumber | string | Filter by phone number |
| externalUserId | string | Filter by external user ID |
| startDate | date | Filter messages created on or after this date |
| endDate | date | Filter messages created on or before this date |
| page | int | Page number (default: 1) |
| pageSize | int | Results per page (default: 20) |
SMS Fallback
Viber templates can be configured with SMS fallback. When Viber delivery fails with specific error codes, the system automatically sends the message via SMS instead.
SMS fallback triggers on the following Viber error codes:
| Error Code | Name | Description |
|---|---|---|
| 8 | USER_BLOCKED | Recipient has blocked your Viber service |
| 9 | NOT_VIBER_USER | Phone number is not registered on Viber |
| 10 | NO_SUITABLE_DEVICE | No compatible device (e.g., Viber not installed) |
See Viber Templates - SMS Fallback for configuration details.
Error Handling
Invalid Phone Numbers
Invalid phone numbers are skipped but don't fail the request. The response includes the full list:
{
"campaignId": "...",
"messageCount": 8,
"skippedCount": 2,
"invalidPhoneNumbers": ["abc", "123"],
"status": "Queuing",
"message": "Campaign accepted. 8 recipients are being queued for insertion. 2 invalid phone numbers were skipped."
}
Template Not Found
{
"error": "Template not found or access denied"
}
Wrong Channel Template
{
"error": "Invalid template channel type",
"message": "SMS endpoints require an SMPP template. This template is configured for Viber"
}
Best Practices
- Validate phone numbers - Clean and validate before sending
- Use external IDs - Track messages with your own identifiers
- Batch efficiently - Send many recipients per request for bulk sends
- Monitor delivery - Check message status for failed deliveries
- Handle errors - Implement retry logic for transient failures