Powerful Developer SMS API

Integrate SMS Communications in to anything and everything. Your own projects, or for your clients.

PureSMS API — built on Divergent Connect
> performance
~40ms API completion time, thousands of SMS in a single batch call
> interface
HTTPS/REST with first-class .NET and Node.js SDKs
> features
Single SMS, Bulk SMS, Scheduling, Inbound Replies, Delivery Receipts
> security
API Key auth + HMAC-signed webhooks

Full developer documentation, including request/response schemas, webhooks and SDK references.

Read the Developer Docs

Single 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.Connect

In 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/bulk

Body 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"
    }
}
StatusFinal?Description
QueuedNoMessage is queued for sending.
DispatchedNoMessage has been sent to the carrier.
DeliveredYesMessage was successfully delivered to the recipient.
FailedYesMessage delivery failed permanently. See errorCode.
ExpiredYesCarrier timed out before delivery (phone off, etc.).
RejectedYesThe carrier rejected the message.
CancelledYesMessage was cancelled before delivery.
DeletedYesMessage was deleted.
UnknownNoStatus 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.