Lyo.Sms.Twilio
3.0.0
dotnet add package Lyo.Sms.Twilio --version 3.0.0
NuGet\Install-Package Lyo.Sms.Twilio -Version 3.0.0
<PackageReference Include="Lyo.Sms.Twilio" Version="3.0.0" />
<PackageVersion Include="Lyo.Sms.Twilio" Version="3.0.0" />
<PackageReference Include="Lyo.Sms.Twilio" />
paket add Lyo.Sms.Twilio --version 3.0.0
#r "nuget: Lyo.Sms.Twilio, 3.0.0"
#:package Lyo.Sms.Twilio@3.0.0
#addin nuget:?package=Lyo.Sms.Twilio&version=3.0.0
#tool nuget:?package=Lyo.Sms.Twilio&version=3.0.0
Lyo.Sms.Twilio
Twilio SMS and MMS via Lyo.Sms. TwilioSmsService implements ISmsService.
Features
- Twilio. SMS and MMS via the Twilio API.
- Bulk. Batch SMS with rate limiting.
- MMS. As many as 10 media attachments per message.
- Query.
GetMessagesAsyncfiltered bySmsMessageQueryFilter. - Errors. Twilio-specific codes on
TwilioSmsResult. - Logging. Writes through Microsoft.Extensions.Logging.
- Metrics. Optional instrumentation for SMS operations.
- DI.
AddTwilioSmsServiceplusAddTwilioSmsServiceFromConfiguration. - Async. Every method accepts a
CancellationToken. - Concurrency. Safe to use from multiple threads.
- Validation.
TwilioOptionsValidatorverifies required options. - Events.
MessageSending,MessageSent,BulkSending,BulkSent.
Examples
Set Twilio options
{
"TwilioOptions": {
"AccountSid": "your_account_sid",
"AuthToken": "your_auth_token",
"DefaultFromPhoneNumber": "+1234567890",
"BulkSmsConcurrencyLimit": 10,
"MaxMessageBodyLength": 1600,
"MaxBulkSmsLimit": 1000,
"EnableMetrics": false
}
}
Set Twilio options (2)
var options = new TwilioOptions
{
AccountSid = "your_account_sid",
AuthToken = "your_auth_token",
DefaultFromPhoneNumber = "+1234567890",
BulkSmsConcurrencyLimit = 10, // Max concurrent bulk SMS requests (default: 10)
MaxMessageBodyLength = 1600, // Max message body length in characters (default: 1600)
MaxBulkSmsLimit = 1000 // Max messages per bulk operation (default: 1000)
};
Add services
// In ConfigureServices(context, services):
services.AddTwilioSmsServiceFromConfiguration(context.Configuration);
// Override the configuration section name (default: "TwilioOptions"):
// services.AddTwilioSmsServiceFromConfiguration(context.Configuration, "MyTwilio");
Send one SMS
public class MyService
{
private readonly ISmsService _smsService;
public MyService(ISmsService smsService)
{
_smsService = smsService;
}
public async Task SendSmsAsync()
{
// Simple send
var result = await _smsService.SendSmsAsync(
to: "+1234567890",
body: "Hello, World!",
from: "+1987654321"
);
if (result.IsSuccess)
{
Console.WriteLine($"Message sent! ID: {result.MessageId}");
}
else
{
Console.WriteLine($"Failed: {result.ErrorMessage}");
}
}
}
SmsMessageBuilder
var builder = SmsMessageBuilder
.New()
.SetTo("+1234567890")
.SetFrom("+1987654321")
.SetBody("Hello, World!");
var result = await _smsService.SendAsync(builder);
Send an MMS
var builder = SmsMessageBuilder
.New()
.SetTo("+1234567890")
.SetFrom("+1987654321")
.SetBody("Check out this image!")
.AddMediaUrl(new Uri("https://example.com/image.jpg"));
var result = await _smsService.SendAsync(builder);
Send a batch
var messages = new[]
{
SmsMessageBuilder.New().SetTo("+1111111111").SetBody("Message 1"),
SmsMessageBuilder.New().SetTo("+2222222222").SetBody("Message 2"),
SmsMessageBuilder.New().SetTo("+3333333333").SetBody("Message 3")
};
var results = await _smsService.SendBulkAsync(messages);
foreach (var result in results)
{
if (result.IsSuccess)
{
Console.WriteLine($"Sent to {result.To}: {result.MessageId}");
}
}
BulkSmsBuilder
var bulkBuilder = BulkSmsBuilder
.New()
.SetDefaultFrom("+1987654321") // Optional: set default sender for all messages
.SetMaxLimit(100) // Optional: limit number of messages
.Add("+1111111111", "Message 1")
.Add("+2222222222", "Message 2")
.Add("+3333333333", "Message 3", "+19998887777"); // Override sender for specific message
var bulkResult = await _smsService.SendBulkAsync(bulkBuilder);
Console.WriteLine($"Total: {bulkResult.TotalCount}");
Console.WriteLine($"Success: {bulkResult.SuccessCount}");
Console.WriteLine($"Failed: {bulkResult.FailureCount}");
Console.WriteLine($"Elapsed: {bulkResult.ElapsedTime}");
if (bulkResult.IsCompleteSuccess)
{
Console.WriteLine("All messages sent successfully!");
}
foreach (var result in bulkResult.Results)
{
if (result.IsSuccess)
{
Console.WriteLine($"Sent to {result.To}: {result.MessageId}");
}
else
{
Console.WriteLine($"Failed to send to {result.To}: {result.ErrorMessage}");
}
}
List messages
var filter = new SmsMessageQueryFilter
{
From = "+1987654321",
DateSentAfter = DateTime.UtcNow.AddDays(-7),
PageSize = 50
};
var result = await _smsService.GetMessagesAsync(filter);
foreach (var message in result.Items)
{
Console.WriteLine($"{message.DateSent}: {message.Body}");
}
// Cursor-based pagination: use result.NextCursor as DateSentBefore for next page when result.HasMore
Fetch one message by id
var message = await _smsService.GetMessageByIdAsync("SM1234567890abcdef");
if (message.IsSuccess)
{
Console.WriteLine($"Status: {message.Status}");
Console.WriteLine($"Body: {message.Body}");
Console.WriteLine($"Price: {((TwilioSmsResult)message).Price} {((TwilioSmsResult)message).PriceUnit}");
}
Check the connection
var isConnected = await _smsService.TestConnectionAsync();
if (isConnected)
{
Console.WriteLine("Connected to Twilio!");
}
MessageSending
_smsService.MessageSending += (sender, args) =>
{
var request = args.SmsRequest;
Console.WriteLine($"Sending SMS to {request.To}: {request.Body}");
};
MessageSent
_smsService.MessageSent += (sender, args) =>
{
var result = args.SmsResult;
if (result.IsSuccess)
{
Console.WriteLine($"SMS sent successfully: {result.MessageId}");
if (result is TwilioSmsResult twilioResult)
{
Console.WriteLine($" Status: {twilioResult.Status}");
Console.WriteLine($" Price: {twilioResult.Price} {twilioResult.PriceUnit}");
}
}
else
{
Console.WriteLine($"SMS failed: {result.ErrorMessage}");
}
};
BulkSending
_smsService.BulkSending += (sender, args) =>
{
Console.WriteLine($"Starting bulk send for {args.BulkSmsMessage.Count} messages");
};
BulkSent
_smsService.BulkSent += (sender, args) =>
{
var bulkResult = args.BulkSmsResult;
Console.WriteLine($"Bulk send completed:");
Console.WriteLine($" Total: {bulkResult.TotalCount}");
Console.WriteLine($" Success: {bulkResult.SuccessCount}");
Console.WriteLine($" Failure: {bulkResult.FailureCount}");
Console.WriteLine($" Elapsed: {bulkResult.ElapsedTime}");
};
Listen for events
public class SmsNotificationService
{
private readonly ISmsService _smsService;
public SmsNotificationService(ISmsService smsService)
{
_smsService = smsService;
SubscribeToEvents();
}
private void SubscribeToEvents()
{
_smsService.MessageSending += OnMessageSending;
_smsService.MessageSent += OnMessageSent;
_smsService.BulkSending += OnBulkSending;
_smsService.BulkSent += OnBulkSent;
}
private void OnMessageSending(object? sender, SmsSendingEventArgs args)
{
Console.WriteLine($"Preparing to send SMS to {args.SmsRequest.To}");
}
private void OnMessageSent(object? sender, SmsSentEventArgs args)
{
if (args.SmsResult.IsSuccess)
{
Console.WriteLine($" SMS sent: {args.SmsResult.MessageId}");
if (args.SmsResult is TwilioSmsResult twilioResult)
{
Console.WriteLine($" Twilio Status: {twilioResult.Status}");
Console.WriteLine($" Cost: {twilioResult.Price} {twilioResult.PriceUnit}");
}
}
else
{
Console.WriteLine($" SMS failed: {args.SmsResult.ErrorMessage}");
}
}
private void OnBulkSending(object? sender, SmsBulkSendingEventArgs args)
{
Console.WriteLine($"Starting bulk SMS operation: {args.BulkSmsMessage.Count} messages");
}
private void OnBulkSent(object? sender, BulkSmsSentEventArgs args)
{
var bulkResult = args.BulkSmsResult;
Console.WriteLine($"Bulk SMS completed: {bulkResult.SuccessCount}/{bulkResult.TotalCount} successful in {bulkResult.ElapsedTime.TotalSeconds:F2}s");
if (bulkResult.FailureCount > 0)
{
Console.WriteLine($" Failures: {bulkResult.FailureCount}");
foreach (var r in bulkResult.FailedResults)
Console.WriteLine($" - {r.Data?.To}: {string.Join("; ", r.Errors?.Select(e => e.Message) ?? [])}");
}
}
}
TwilioSmsResult
var result = await _smsService.SendSmsAsync("+1234567890", "Hello");
if (result is TwilioSmsResult twilioResult)
{
Console.WriteLine($"Message SID: {twilioResult.MessageId}");
Console.WriteLine($"Status: {twilioResult.Status}");
Console.WriteLine($"Segments: {twilioResult.NumSegments}");
Console.WriteLine($"Price: {twilioResult.Price} {twilioResult.PriceUnit}");
Console.WriteLine($"Account SID: {twilioResult.AccountSid}");
}
Handling errors
var result = await _smsService.SendSmsAsync("+1234567890", "Hello");
if (!result.IsSuccess)
{
Console.WriteLine($"Error: {result.ErrorMessage}");
Console.WriteLine($"Error Code: {result.ErrorCode}");
if (result.Exception != null)
{
Console.WriteLine($"Exception: {result.Exception.Message}");
// Handle specific exception types
if (result.Exception is InvalidFormatException formatEx)
{
Console.WriteLine($"Invalid Value: {formatEx.InvalidValue}");
Console.WriteLine($"Valid Formats: {string.Join(", ", formatEx.ValidFormats)}");
}
}
}
Set Twilio options
From a configuration file (appsettings.json)
From code
Add services
From configuration binding
AddTwilioSmsService(IConfiguration, string) is an alias kept for callers that prefer the shorter name.
Both wire the same singletons: TwilioOptions, TwilioOptionsValidator, TwilioSmsService, plus
the cross-typed ISmsService and ISmsService<TwilioSmsResult> interfaces backed by the same instance.
On net6.0+ targets an IHttpClient keyed "lyo-twilio-sms" is also registered so Twilio reuses the
shared IHttpClientFactory pool. Resilience policies on the application's IHttpClientFactory
configuration apply on their own.
Send a batch
From an IEnumerable of builders
From BulkSmsBuilder
Events
The Twilio SMS service raises events around send operations:
MessageSending
Raised before each message is sent, including during bulk operations:
MessageSent
Raised after each message is sent (success or failure):
BulkSending
Raised before a bulk send starts:
BulkSent
Raised after a bulk send completes:
Complete event example
Events still fire when operations fail, so you can track every attempt. Useful for monitoring, logging, and user notifications.
Twilio failure codes
Twilio-specific error codes land on the result via TwilioSmsResult.TwilioErrorCode:
if (!result.IsSuccess && result is TwilioSmsResult twilioResult)
{
if (twilioResult.TwilioErrorCode.HasValue)
{
Console.WriteLine($"Twilio Error Code: {twilioResult.TwilioErrorCode}");
// Common error codes:
// 20003 - Unreachable destination handset
// 20429 - Too Many Requests (rate limit)
// 30001 - Queue overflow
// 30008 - Unknown destination handset
}
// The Errors collection (inherited from Result<SmsRequest>) carries human-readable messages and codes
var firstError = twilioResult.Errors?.FirstOrDefault();
Console.WriteLine($"Error: {firstError?.Message} ({firstError?.Code})");
}
Resilience
Built-in retry or timeout logic is not included. Apply resilience at the application layer (e.g. using Lyo.Resilience with
AddLyoResilienceHandler on the HttpClient, or by wrapping ISmsService calls) as needed.
Throughput limits
- Concurrent requests. Capped at 10 concurrent requests (configurable via
BulkSmsConcurrencyLimit). - Throttling. A
SemaphoreSlimcaps concurrency. - Async. Every method accepts a
CancellationToken. - Bulk limits. Maximum messages per bulk operation (set via
MaxBulkSmsLimit).
Concurrent use
- Every instance field is readonly
- Bulk runs use thread-safe collections (
ConcurrentBag) - Rate limiting uses
SemaphoreSlimso concurrency stays thread-safe - The Twilio SDK client underneath is thread-safe
TwilioOptions fields
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
AccountSid |
string |
Yes | - | Your Twilio Account SID |
AuthToken |
string |
Yes | - | Your Twilio Auth Token |
DefaultFromPhoneNumber |
string? |
No | - | Default sender phone number |
BulkSmsConcurrencyLimit |
int |
No | 10 | Maximum concurrent bulk SMS requests |
MaxMessageBodyLength |
int |
No | 1600 | Maximum message body length in characters |
MaxBulkSmsLimit |
int |
No | 1000 | Maximum messages in one bulk operation |
EnableMetrics |
bool |
No | false | Turn on metrics collection |
Logging
Logging uses Microsoft.Extensions.Logging:
services.AddLogging(builder =>
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Information);
});
Log levels:
- Information. Successful operations, message details
- Warning. Retries, long messages, connection issues
- Error. Failures, exceptions
Logs mask phone numbers (last 4 digits only).
Metrics
Turn on EnableMetrics to collect:
services.AddLyoMetrics();
services.AddTwilioSmsService(options =>
{
options.EnableMetrics = true;
// ... other options
});
Metrics tracked:
sms.twilio.send.durationsms.twilio.send.successsms.twilio.send.failuresms.twilio.bulk.send.durationsms.twilio.bulk.send.totalsms.twilio.bulk.send.successsms.twilio.bulk.send.failuresms.twilio.bulk.send.last_duration_mssms.twilio.api.get_message.durationsms.twilio.api.get_messages.durationsms.twilio.test_connection.duration
Validation
AccountSidis requiredAuthTokenis required- Validation is wired via
services.AddOptions<TwilioOptions>().ValidateOnStart()when usingAddTwilioSmsServiceFromConfiguration()/AddTwilioSmsService(IConfiguration, ...).
Dependencies
Generated from ProjectReference / PackageReference (same model as docs/Lyo.ProjectGraph.html).
Lyo.Common.Core(direct, lyo)Lyo.Exceptions(direct, lyo)Lyo.Result(direct, lyo)Lyo.Sms(direct, lyo)Microsoft.Extensions.Http10.0.5(direct, microsoft)Microsoft.Extensions.Logging.Abstractions10.0.5(direct, microsoft)Microsoft.Extensions.Options.ConfigurationExtensions10.0.5(direct, microsoft)Twilio7.14.9(direct, third-party)Lyo.Metrics(transitive, lyo)Lyo.Sms.Models(transitive, lyo)Microsoft.Bcl.AsyncInterfaces10.0.5(transitive, microsoft, netstandard2.0)System.Memory4.6.3(transitive, microsoft, netstandard2.0)System.Text.Json10.0.5(transitive, microsoft, netstandard2.0)
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Lyo.Common.Core (>= 3.0.0)
- Lyo.Exceptions (>= 3.0.0)
- Lyo.Result (>= 3.0.0)
- Lyo.Sms (>= 3.0.0)
- Microsoft.Extensions.Http (>= 10.0.5)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.5)
- Twilio (>= 7.14.9)
-
net10.0
- Lyo.Common.Core (>= 3.0.0)
- Lyo.Exceptions (>= 3.0.0)
- Lyo.Result (>= 3.0.0)
- Lyo.Sms (>= 3.0.0)
- Microsoft.Extensions.Http (>= 10.0.5)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.5)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.5)
- Twilio (>= 7.14.9)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Lyo.Sms.Twilio:
| Package | Downloads |
|---|---|
|
Lyo.Sms.Twilio.Postgres
PostgreSQL implementation of Lyo.Sms.Twilio using Entity Framework Core. |
GitHub repositories
This package is not used by any popular GitHub repositories.