Skip to content

.NET

The Divergent.Connect package sends and manages SMS and email with the v2 API. It supports .NET 8, 9 and 10, and .NET Standard 2.0.

Terminal window
dotnet add package Divergent.Connect --prerelease

Version 2.0 and later is for the v2 API. The 0.5.x versions are for the v1 API.

To receive webhooks in ASP.NET Core, add Divergent.Connect.AspNetCore too.

Register the client in Program.cs:

using Divergent.Connect.DependencyInjection;
builder.AddDivergentConnect();

It reads its settings from the Divergent:Connect section of your configuration, for example in appsettings.json:

{
"Divergent": {
"Connect": {
"ApiKey": "ck_...",
"DefaultSmsSender": "YourBrand",
"DefaultEmailSender": "hello@yourcompany.com"
}
}
}

Keep your API key out of source control, for example in user secrets or an environment variable named Divergent__Connect__ApiKey.

SettingDescription
ApiKeyYour v2 API key. Required
DefaultSmsSenderUsed when an SMS or batch doesn’t set Sender
DefaultEmailSenderUsed when an email doesn’t set Sender
ApiKeyId, SigningSecretSigns every request, for keys that need signed requests
ApiUrlDefaults to https://api.connect.ms/

You can also change settings in code with builder.AddDivergentConnect(o => ...), or use services.AddDivergentConnect(configuration) without a host builder.

Then inject ConnectClient, or just ConnectSms or ConnectEmail:

public class OrderNotifier(ConnectSms sms)
{
public Task ShippedAsync(string phone) =>
sms.SendAsync(new SendSmsRequest { Recipient = phone, Content = "Your order has shipped." });
}
var connect = new ConnectClientBuilder()
.WithApiKey(apiKey)
.WithDefaultSmsSender("YourBrand")
.Build();

The builder also has WithDefaultEmailSender, WithSigning, WithSigningKey, WithApiUrl, WithHttpClient and WithLoggerFactory. Create one client and reuse it.

using Divergent.Connect.Models;
var sms = await connect.Sms.SendAsync(new SendSmsRequest
{
Recipient = "+447700900123",
Content = "Hi Sam, your order has shipped.",
});
Console.WriteLine($"{sms.Id} {sms.Status}");

The request and the returned message are the same as the API’s. See Send SMS and Track SMS.

MethodAPI
Sms.SendAsyncPOST /v2/sms
Sms.GetAsyncGET /v2/sms/{id}
Sms.ListAsync, Sms.ListAllAsyncGET /v2/sms
Sms.CancelAsyncDELETE /v2/sms/{id}
Sms.CreateBatchAsync, Sms.GetBatchAsync, Sms.CancelBatchAsyncBatches
await connect.Email.SendAsync(new SendEmailRequest
{
Recipient = "sam@example.com",
Subject = "Your order has shipped",
Content = new() { Html = "<p>Hi Sam, your order is on its way.</p>" },
Attachments = [EmailAttachment.FromBytes("receipt.pdf", "application/pdf", pdfBytes)],
});

See Send Email. Email has SendAsync, GetAsync, ListAsync, ListAllAsync and CancelAsync, like Sms.

ListAsync returns one page, with Data and NextCursor. Set Cursor to NextCursor for the next page. ListAllAsync fetches every page for you:

await foreach (var message in connect.Sms.ListAllAsync(new ListSmsOptions { Status = SmsMessageStatus.Failed }))
Console.WriteLine($"{message.Id} {message.Reason?.Code}");

Every POST has an Idempotency-Key, so a retry never sends twice. The client makes one up, or you can pass your own:

await connect.Sms.SendAsync(request, idempotencyKey: "order-991-shipped");

Network errors, 5xx and 429 responses are retried twice, waiting for Retry-After when there is one.

Any other error throws a ConnectApiException:

try
{
await connect.Sms.SendAsync(request);
}
catch (ConnectApiException e)
{
Console.WriteLine($"{e.Status} {e.Code} {e.Detail} {e.RequestId}");
}

Errors lists validation problems by field. Quote RequestId when you contact support.

To receive events, see ASP.NET Core webhooks.