Sending Messages
Complete guide to sending SMS and WhatsApp messages using the Sent API.
Overview
A Message in Sent represents a single communication sent to a contact through a specific channel (SMS or WhatsApp). This guide covers the core aspects of message sending.
The Message Lifecycle
Sending Methods
1. Send to Phone Number
The simplest approach - send directly to any phone number:
curl -X POST "https://api.sent.dm/v3/messages" \
-H "x-api-key: $SENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": ["+1234567890"],
"template": {
"id": "tmpl_1234567890",
"parameters": {
"customerName": "John Doe",
"orderNumber": "#12345"
}
}
}'import SentDm from '@sentdm/sentdm';
const client = new SentDm();
const response = await client.messages.send({
to: ['+1234567890'],
template: {
id: 'tmpl_1234567890',
parameters: {
customerName: 'John Doe',
orderNumber: '#12345'
}
}
});
console.log(`Message ID: ${response.data.recipients[0].message_id}`);from sent_dm import SentDm
client = SentDm()
response = client.messages.send(
to=["+1234567890"],
template={
"id": "tmpl_1234567890",
"parameters": {
"customerName": "John Doe",
"orderNumber": "#12345"
}
}
)
print(f"Message ID: {response.data.recipients[0].message_id}")import (
"context"
"github.com/sentdm/sent-dm-go"
"github.com/sentdm/sent-dm-go/option"
)
client := sentdm.NewClient()
response, err := client.Messages.Send(context.Background(), sentdm.MessageSendParams{
To: []string{"+1234567890"},
Template: sentdm.MessageSendParamsTemplate{
ID: sentdm.String("tmpl_1234567890"),
Parameters: map[string]interface{}{
"customerName": "John Doe",
"orderNumber": "#12345",
},
},
})import dm.sent.client.SentDmClient;
import dm.sent.client.okhttp.SentDmOkHttpClient;
import dm.sent.core.JsonValue;
import dm.sent.models.messages.MessageSendParams;
SentDmClient client = SentDmOkHttpClient.fromEnv();
MessageSendParams params = MessageSendParams.builder()
.addTo("+1234567890")
.template(MessageSendParams.Template.builder()
.id("tmpl_1234567890")
.parameters(MessageSendParams.Template.Parameters.builder()
.putAdditionalProperty("customerName", JsonValue.from("John Doe"))
.putAdditionalProperty("orderNumber", JsonValue.from("#12345"))
.build())
.build())
.build();
var response = client.messages().send(params);
System.out.println("Sent: " + response.data().recipients().get(0).messageId());using Sentdm;
using Sentdm.Models.Messages;
using System.Collections.Generic;
SentDmClient client = new();
MessageSendParams parameters = new()
{
To = new List<string> { "+1234567890" },
Template = new MessageSendParamsTemplate
{
Id = "tmpl_1234567890",
Parameters = new Dictionary<string, string>
{
{ "customerName", "John Doe" },
{ "orderNumber", "#12345" }
}
}
};
var response = await client.Messages.SendAsync(parameters);
Console.WriteLine($"Sent: {response.Data.Recipients[0].MessageId}");<?php
require_once 'vendor/autoload.php';
use SentDM\Client;
$client = new Client($_ENV['SENT_DM_API_KEY']);
$result = $client->messages->send(
to: ['+1234567890'],
template: [
'id' => 'tmpl_1234567890',
'parameters' => [
'customerName' => 'John Doe',
'orderNumber' => '#12345'
]
]
);
var_dump($result->data->recipients[0]->message_id);require "sentdm"
sent_dm = Sentdm::Client.new
result = sent_dm.messages.send(
to: ["+1234567890"],
template: {
id: "tmpl_1234567890",
parameters: {
customerName: "John Doe",
orderNumber: "#12345"
}
}
)
puts result.data.recipients[0].message_id2. Send to Multiple Recipients
Send the same message to multiple recipients in one request:
curl -X POST "https://api.sent.dm/v3/messages" \
-H "x-api-key: $SENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": ["+1234567890", "+1987654321", "+1555555555"],
"template": {
"id": "tmpl_1234567890",
"parameters": {
"announcement": "Our store is now open!"
}
}
}'const response = await client.messages.send({
to: ['+1234567890', '+1987654321', '+1555555555'],
template: {
id: 'tmpl_1234567890',
parameters: {
announcement: 'Our store is now open!'
}
}
});
console.log(`Sent to ${response.data.recipients.length} recipients`);response = client.messages.send(
to=["+1234567890", "+1987654321", "+1555555555"],
template={
"id": "tmpl_1234567890",
"parameters": {
"announcement": "Our store is now open!"
}
}
)
print(f"Sent to {len(response.data.recipients)} recipients")response, err := client.Messages.Send(context.Background(), sentdm.MessageSendParams{
To: []string{"+1234567890", "+1987654321", "+1555555555"},
Template: sentdm.MessageSendParamsTemplate{
ID: sentdm.String("tmpl_1234567890"),
Parameters: map[string]interface{}{
"announcement": "Our store is now open!",
},
},
})
fmt.Printf("Sent to %d recipients\n", len(response.Data.Recipients))import dm.sent.models.messages.MessageSendParams;
import dm.sent.core.JsonValue;
MessageSendParams params = MessageSendParams.builder()
.addTo("+1234567890")
.addTo("+1987654321")
.addTo("+1555555555")
.template(MessageSendParams.Template.builder()
.id("tmpl_1234567890")
.parameters(MessageSendParams.Template.Parameters.builder()
.putAdditionalProperty("announcement", JsonValue.from("Our store is now open!"))
.build())
.build())
.build();
var response = client.messages().send(params);
System.out.println("Sent to " + response.data().recipients().size() + " recipients");using Sentdm.Models.Messages;
MessageSendParams parameters = new()
{
To = new List<string> { "+1234567890", "+1987654321", "+1555555555" },
Template = new MessageSendParamsTemplate
{
Id = "tmpl_1234567890",
Parameters = new Dictionary<string, string>
{
{ "announcement", "Our store is now open!" }
}
}
};
var response = await client.Messages.SendAsync(parameters);
Console.WriteLine($"Sent to {response.Data.Recipients.Count} recipients");$result = $client->messages->send(
to: ['+1234567890', '+1987654321', '+1555555555'],
template: [
'id' => 'tmpl_1234567890',
'parameters' => [
'announcement' => 'Our store is now open!'
]
]
);
echo "Sent to " . count($result->data->recipients) . " recipients\n";result = sent_dm.messages.send(
to: ["+1234567890", "+1987654321", "+1555555555"],
template: {
id: "tmpl_1234567890",
parameters: {
announcement: "Our store is now open!"
}
}
)
puts "Sent to #{result.data.recipients.length} recipients"Rate Limiting: Batch requests count against rate limits per recipient. A request with 10 recipients counts as 10 messages for rate limiting purposes (200 requests/minute standard limit).
Channel Selection Strategies
Automatic Selection (Default)
Let Sent choose the optimal channel by omitting the channel field:
curl -X POST "https://api.sent.dm/v3/messages" \
-H "x-api-key: $SENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": ["+1234567890"],
"template": {
"id": "tmpl_1234567890"
}
}'const response = await client.messages.send({
to: ['+1234567890'],
template: {
id: 'tmpl_1234567890'
}
// No channel field - automatic selection
});response = client.messages.send(
to=["+1234567890"],
template={
"id": "tmpl_1234567890"
}
# No channel parameter - automatic selection
)response, err := client.Messages.Send(context.Background(), sentdm.MessageSendParams{
To: []string{"+1234567890"},
Template: sentdm.MessageSendParamsTemplate{
ID: sentdm.String("tmpl_1234567890"),
},
// No channel field - automatic selection
})MessageSendParams params = MessageSendParams.builder()
.addTo("+1234567890")
.template(MessageSendParams.Template.builder()
.id("tmpl_1234567890")
.build())
// No channel - automatic selection
.build();
var response = client.messages().send(params);MessageSendParams parameters = new()
{
To = new List<string> { "+1234567890" },
Template = new MessageSendParamsTemplate
{
Id = "tmpl_1234567890"
}
// No channel - automatic selection
};
var response = await client.Messages.SendAsync(parameters);$result = $client->messages->send(
to: ['+1234567890'],
template: [
'id' => 'tmpl_1234567890'
]
// No channel - automatic selection
);result = sent_dm.messages.send(
to: ["+1234567890"],
template: {
id: "tmpl_1234567890"
}
# No channel - automatic selection
)Sent considers:
- Contact's available channels
- Historical delivery success rates
- Cost optimization
- Regional preferences
Force Specific Channel
Override automatic selection by specifying the channel field:
SMS Only
curl -X POST "https://api.sent.dm/v3/messages" \
-H "x-api-key: $SENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": ["+1234567890"],
"template": {
"id": "tmpl_1234567890"
},
"channel": ["sms"]
}'const response = await client.messages.send({
to: ['+1234567890'],
template: {
id: 'tmpl_1234567890'
},
channel: ['sms']
});response = client.messages.send(
to=["+1234567890"],
template={
"id": "tmpl_1234567890"
},
channel=["sms"]
)response, err := client.Messages.Send(context.Background(), sentdm.MessageSendParams{
To: []string{"+1234567890"},
Channel: []string{"sms"},
Template: sentdm.MessageSendParamsTemplate{
ID: sentdm.String("tmpl_1234567890"),
},
})MessageSendParams params = MessageSendParams.builder()
.addTo("+1234567890")
.addChannel("sms")
.template(MessageSendParams.Template.builder()
.id("tmpl_1234567890")
.build())
.build();
var response = client.messages().send(params);MessageSendParams parameters = new()
{
To = new List<string> { "+1234567890" },
Channels = new List<string> { "sms" },
Template = new MessageSendParamsTemplate
{
Id = "tmpl_1234567890"
}
};
var response = await client.Messages.SendAsync(parameters);$result = $client->messages->send(
to: ['+1234567890'],
template: [
'id' => 'tmpl_1234567890'
],
channels: ['sms']
);result = sent_dm.messages.send(
to: ["+1234567890"],
template: {
id: "tmpl_1234567890"
},
channels: ["sms"]
)WhatsApp Only
If WhatsApp is not available for this contact, the message will fail with no fallback.
curl -X POST "https://api.sent.dm/v3/messages" \
-H "x-api-key: $SENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": ["+1234567890"],
"template": {
"id": "tmpl_1234567890"
},
"channel": ["whatsapp"]
}'const response = await client.messages.send({
to: ['+1234567890'],
template: {
id: 'tmpl_1234567890'
},
channel: ['whatsapp']
});response = client.messages.send(
to=["+1234567890"],
template={
"id": "tmpl_1234567890"
},
channel=["whatsapp"]
)response, err := client.Messages.Send(context.Background(), sentdm.MessageSendParams{
To: []string{"+1234567890"},
Channel: []string{"whatsapp"},
Template: sentdm.MessageSendParamsTemplate{
ID: sentdm.String("tmpl_1234567890"),
},
})MessageSendParams params = MessageSendParams.builder()
.addTo("+1234567890")
.addChannel("whatsapp")
.template(MessageSendParams.Template.builder()
.id("tmpl_1234567890")
.build())
.build();
var response = client.messages().send(params);MessageSendParams parameters = new()
{
To = new List<string> { "+1234567890" },
Channels = new List<string> { "whatsapp" },
Template = new MessageSendParamsTemplate
{
Id = "tmpl_1234567890"
}
};
var response = await client.Messages.SendAsync(parameters);$result = $client->messages->send(
to: ['+1234567890'],
template: [
'id' => 'tmpl_1234567890'
],
channels: ['whatsapp']
);result = sent_dm.messages.send(
to: ["+1234567890"],
template: {
id: "tmpl_1234567890"
},
channels: ["whatsapp"]
)WhatsApp with SMS Fallback
Sent will try WhatsApp first, then automatically fall back to SMS if needed.
curl -X POST "https://api.sent.dm/v3/messages" \
-H "x-api-key: $SENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": ["+1234567890"],
"template": {
"id": "tmpl_1234567890"
},
"channel": ["whatsapp", "sms"]
}'const response = await client.messages.send({
to: ['+1234567890'],
template: {
id: 'tmpl_1234567890'
},
channel: ['whatsapp', 'sms']
});response = client.messages.send(
to=["+1234567890"],
template={
"id": "tmpl_1234567890"
},
channel=["whatsapp", "sms"]
)response, err := client.Messages.Send(context.Background(), sentdm.MessageSendParams{
To: []string{"+1234567890"},
Channel: []string{"whatsapp", "sms"},
Template: sentdm.MessageSendParamsTemplate{
ID: sentdm.String("tmpl_1234567890"),
},
})MessageSendParams params = MessageSendParams.builder()
.addTo("+1234567890")
.addChannel("whatsapp")
.addChannel("sms")
.template(MessageSendParams.Template.builder()
.id("tmpl_1234567890")
.build())
.build();
var response = client.messages().send(params);MessageSendParams parameters = new()
{
To = new List<string> { "+1234567890" },
Channels = new List<string> { "whatsapp", "sms" },
Template = new MessageSendParamsTemplate
{
Id = "tmpl_1234567890"
}
};
var response = await client.Messages.SendAsync(parameters);$result = $client->messages->send(
to: ['+1234567890'],
template: [
'id' => 'tmpl_1234567890'
],
channels: ['whatsapp', 'sms']
);result = sent_dm.messages.send(
to: ["+1234567890"],
template: {
id: "tmpl_1234567890"
},
channels: ["whatsapp", "sms"]
)Response Overview
Success Response (202 Accepted)
{
"success": true,
"data": {
"status": "accepted",
"body": "Hi John Doe, your order #12345 has been confirmed.",
"template_id": "7ba7b820-9dad-11d1-80b4-00c04fd430c8",
"template_name": "order_confirmation",
"recipients": [
{
"message_id": "8ba7b830-9dad-11d1-80b4-00c04fd430c8",
"to": "+14155551234",
"channel": "sms"
}
]
},
"error": null,
"meta": {
"request_id": "req_7X9zKp2jDw",
"timestamp": "2026-03-04T11:28:25.2096416+00:00",
"version": "v3"
}
}Important: Store the message_id from each recipient object - you'll need it to track delivery status.
Error Response (4xx/5xx)
{
"success": false,
"data": null,
"error": {
"code": "BUSINESS_003",
"message": "Account balance is insufficient to send this message",
"details": null,
"doc_url": "https://docs.sent.dm/errors/BUSINESS_003"
},
"meta": {
"request_id": "req_7X9zKp2jDw",
"timestamp": "2026-03-04T11:28:25.2096416+00:00",
"version": "v3"
}
}Common error codes:
RESOURCE_002- Template not found or not approvedBUSINESS_003- Insufficient account balanceBUSINESS_002- Rate limit exceeded (200 req/min standard, 10 req/min sensitive)VALIDATION_002- Invalid phone number formatBUSINESS_005- Template not approved for sending
Next Steps
Response Handling
Detailed guide to handling responses and errors in all SDKs
Best Practices
Test mode, idempotency, error handling, and cost optimization
Batch Operations
High-volume sending and contact imports
Status Tracking
Track delivery status with webhooks and polling