Skip to main content

Sending Messages

Learn how to send messages through the Transformify API.

Overview

Messages are sent through channel-specific endpoints:

ChannelEndpointUse case
SMS TransactionalPOST /sms/campaigns + POST /sms/campaigns/{id}/messagesOTP, order notifications, alerts
SMS PromotionalPOST /sms/promotionalMarketing campaigns, bulk offers
SMS DirectPOST /sms/sendQuick sends without a template
Viber BulkPOST /viber/sendRich messages, marketing, notifications
Viber TransactionalPOST /viber/campaigns + POST /viber/campaigns/{id}/messagesEvent-triggered Viber notifications
Viber DirectPOST /viber/send-textQuick 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."
}
Async flow

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" }
}
]
}'
SMS Sender ID

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" }
]
}'
No template needed

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" }
]
}'
note

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
InputNormalizedValid
+3897012345638970123456
3897012345638970123456
070-123-456Invalid
123Invalid

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

  1. Recipient-level placeholders are used first (if provided)
  2. 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 │
└─────────┘ └─────────┘
StatusDescription
PendingMessage created, waiting to be processed
SendingMessage is being sent
SentMessage sent to the provider
DeliveredMessage delivered to recipient's device
SeenRecipient viewed the message (Viber only)
FailedMessage 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

ParameterTypeDescription
phoneNumberstringFilter by phone number
externalUserIdstringFilter by external user ID
startDatedateFilter messages created on or after this date
endDatedateFilter messages created on or before this date
pageintPage number (default: 1)
pageSizeintResults 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 CodeNameDescription
8USER_BLOCKEDRecipient has blocked your Viber service
9NOT_VIBER_USERPhone number is not registered on Viber
10NO_SUITABLE_DEVICENo 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

  1. Validate phone numbers - Clean and validate before sending
  2. Use external IDs - Track messages with your own identifiers
  3. Batch efficiently - Send many recipients per request for bulk sends
  4. Monitor delivery - Check message status for failed deliveries
  5. Handle errors - Implement retry logic for transient failures