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.';
}Prefer a proper SDK?
Richard Leishman of Webforward created an SDK to make this easier in PHP environments.
composer require mrl22/puresms-sdk$sms = new PureSms(apiKey: 'your-api-key', defaultSender: 'YourSender');
$sms->send('+447700900123', 'Hello world, from PureSMS!');Please note we cannot offer support for this library, but it looks great!
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, so 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.