Powerful Developer SMS API
Integrate SMS Communications in to anything and everything. Your own projects, or for your clients.
~40ms API completion time, thousands of SMS in a single batch call
HTTPS/REST with first-class .NET and Node.js SDKs
Single SMS, Bulk SMS, Scheduling, Inbound Replies, Delivery Receipts
API Key auth + HMAC-signed webhooks
Full developer documentation, including request/response schemas, webhooks and SDK references.
Read the Developer DocsSingle SMS
Low volume? No worries. You can send single SMSes, almost instantly.
Bulk SMS
Sending a tonne? Guess what, you can do that too, almost instantly.
Receive Replies
Want responses in your app? We got you. We'll POST them to you.
C# / .NET Example
Do you speak our native language? We have a NuGet package for you.
dotnet add package Divergent.ConnectIn a .NET 8+ application, register the client during host startup:
var builder = WebApplication.CreateBuilder(args);
builder.AddDivergentConnect();
var app = builder.Build();Then drop your API key in appsettings.json:
{
"Divergent": {
"Connect": {
"ApiKey": "your-api-key",
"DefaultSmsSender": "YourSender"
}
}
}Now inject IConnectSms wherever you need to send a message:
using Divergent.Connect;
public class WelcomeService(IConnectSms smsClient)
{
public async Task SendWelcomeSms(string phoneNumber)
{
await smsClient.SendSmsAsync(new ConnectSmsMessage
{
To = phoneNumber,
Content = "Hello world, from PureSMS!"
});
}
}Need to schedule, set a client reference, or force Unicode? It's all on the message:
await smsClient.SendSmsAsync(new ConnectSmsMessage
{
To = "+447700900123",
Content = "Your appointment is tomorrow at 10am",
DateSendAtUtc = DateTime.UtcNow.AddHours(1),
ClientReference = "appointment-reminder-123",
UnicodeMode = UnicodeMode.Allow
});Not on the host builder? You can build a client manually:
var smsClient = new ConnectClientBuilder()
.WithApiKey("your-api-key")
.WithDefaultSmsSender("YourSender")
.BuildSmsClient();cURL / HTTP Example
If you'd rather hit the API directly — fire a POST at /sms/send with your API key in the X-Api-Key header.
curl -X POST https://connect-api.divergent.cloud/sms/send \
-H "X-Api-Key: { API_KEY }" \
--json '{
"sender": "{ SENDER_NAME }",
"recipient": "{ RECIPIENT_NUMBER }",
"content": "Hello world, from PureSMS!"
}'Sending in bulk? Same API key, different endpoint:
POST https://connect-api.divergent.cloud/sms/send/bulkBody shape: { "messages": [{ "sender": "...", "recipient": "...", "content": "..." }, ...] }. The response gives you back a batchId and messageCount.
PHP Example
Integrating into WordPress, or just a PHP dev? Here's the same call in cURL form.
$apiKey = "My API Key";
$outboundSms = array(
'sender' => 'Somebody',
'recipient' => '447700900123',
'content' => 'Hello this is my first SMS'
);
$curl = curl_init('https://connect-api.divergent.cloud/sms/send');
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($outboundSms));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'X-Api-Key: ' . $apiKey,
'Content-Type: application/json'));
$result = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if ($status == 200) {
echo 'SMS accepted.';
}
else {
echo 'SMS failed.';
}Webhooks — Delivery Receipts & Inbound SMS
Webhooks are configured at the workspace level under Settings → Webhooks. Add an endpoint URL, optionally set a signing secret, and pick which event types to subscribe to.
Every webhook arrives as an HTTP POST with the same envelope:
{
"id": "evt_abc123",
"timestamp": "2026-01-15T10:30:00Z",
"workspaceId": "ws_xyz789",
"eventType": 1,
"data": { ... }
}eventType tells you what's inside data:
- 1 — Delivery Receipt: the status of an outbound message changed.
- 2 — Inbound SMS: a reply (or any message) was received on one of your virtual numbers.
Delivery Receipt payload
{
"id": "evt_dr_123456",
"timestamp": "2026-01-15T10:30:00Z",
"workspaceId": "ws_xyz789",
"eventType": 1,
"data": {
"messageId": "12345678",
"clientReference": "order-confirmation-456",
"deliveryStatus": "Delivered",
"errorCode": null,
"processedAt": "2026-01-15T10:29:55Z",
"deliveredAt": "2026-01-15T10:30:00Z"
}
}| Status | Final? | Description |
|---|---|---|
| Queued | No | Message is queued for sending. |
| Dispatched | No | Message has been sent to the carrier. |
| Delivered | Yes | Message was successfully delivered to the recipient. |
| Failed | Yes | Message delivery failed permanently. See errorCode. |
| Expired | Yes | Carrier timed out before delivery (phone off, etc.). |
| Rejected | Yes | The carrier rejected the message. |
| Cancelled | Yes | Message was cancelled before delivery. |
| Deleted | Yes | Message was deleted. |
| Unknown | No | Status could not be determined. |
Inbound SMS payload
{
"id": "evt_in_789012",
"timestamp": "2026-01-15T14:22:30Z",
"workspaceId": "ws_xyz789",
"eventType": 2,
"data": {
"messageId": "inb_987654",
"inboundNumber": "+447700900100",
"sender": "+447700900123",
"body": "Yes, please confirm my appointment",
"receivedAt": "2026-01-15T14:22:30Z"
}
}Inbound webhooks need a virtual number — pick one up in the dashboard.
Responding
Return any 2xx status as soon as you can — within 45 seconds. We retry on failure (immediate, then 5m, 15m, 1h, 4h, 8h, 12h) and auto-disable the endpoint after five consecutive failures, so wire up a healthcheck.
Verifying signatures
If you set a signing secret, every request includes X-Webhook-Signature (base64 HMAC-SHA256) and X-Webhook-Timestamp (Unix seconds). The signed string is {timestamp}.{rawJsonBody}:
using System.Security.Cryptography;
using System.Text;
public bool ValidateWebhookSignature(
string payload,
string signature,
string timestamp,
string secret)
{
var signatureData = $"{timestamp}.{payload}";
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(signatureData));
var expectedSignature = Convert.ToBase64String(hash);
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(signature),
Encoding.UTF8.GetBytes(expectedSignature));
}Always use a constant-time comparison (CryptographicOperations.FixedTimeEquals in .NET, hash_equals in PHP) to prevent timing attacks.