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_id

2. 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 approved
  • BUSINESS_003 - Insufficient account balance
  • BUSINESS_002 - Rate limit exceeded (200 req/min standard, 10 req/min sensitive)
  • VALIDATION_002 - Invalid phone number format
  • BUSINESS_005 - Template not approved for sending

Next Steps


On this page