Lyo.Sms.Twilio
1.0.13
dotnet add package Lyo.Sms.Twilio --version 1.0.13
NuGet\Install-Package Lyo.Sms.Twilio -Version 1.0.13
<PackageReference Include="Lyo.Sms.Twilio" Version="1.0.13" />
<PackageVersion Include="Lyo.Sms.Twilio" Version="1.0.13" />
<PackageReference Include="Lyo.Sms.Twilio" />
paket add Lyo.Sms.Twilio --version 1.0.13
#r "nuget: Lyo.Sms.Twilio, 1.0.13"
#:package Lyo.Sms.Twilio@1.0.13
#addin nuget:?package=Lyo.Sms.Twilio&version=1.0.13
#tool nuget:?package=Lyo.Sms.Twilio&version=1.0.13
Lyo.Sms.Twilio
Twilio SMS and MMS through Lyo.Sms. TwilioSmsService implements ISmsService.
Features
- Twilio. SMS and MMS through the Twilio API.
- Bulk. Bulk SMS with rate limiting.
- MMS. Up to 10 media attachments per message.
- Query.
GetMessagesAsyncwithSmsMessageQueryFilter. - Errors. Twilio-specific error codes on
TwilioSmsResult. - Logging. Logs through Microsoft.Extensions.Logging.
- Metrics. Optional metrics on SMS operations.
- DI.
AddTwilioSmsServiceandAddTwilioSmsServiceFromConfiguration. - Async. Methods take
CancellationToken. - Concurrency. Safe for concurrent use.
- Validation.
TwilioOptionsValidatorchecks required options. - Events.
MessageSending,MessageSent,BulkSending,BulkSent.
Examples
Configure Twilio options
{
"TwilioOptions": {
"AccountSid": "your_account_sid",
"AuthToken": "your_auth_token",
"DefaultFromPhoneNumber": "+1234567890",
"BulkSmsConcurrencyLimit": 10,
"MaxMessageBodyLength": 1600,
"MaxBulkSmsLimit": 1000,
"EnableMetrics": false
}
}
Configure 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)
};
Register services
// In ConfigureServices(context, services):
services.AddTwilioSmsServiceFromConfiguration(context.Configuration);
// Override the configuration section name (default: "TwilioOptions"):
// services.AddTwilioSmsServiceFromConfiguration(context.Configuration, "MyTwilio");
Send an 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 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 bulk messages
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}");
}
}
Query 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
Get a 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}");
}
Test 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}");
};
Subscribe to 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}");
}
Error handling
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)}");
}
}
}
Configure Twilio options
Using configuration file (appsettings.json)
Using code
Register services
Using configuration binding
AddTwilioSmsService(IConfiguration, string) is an alias kept for callers that prefer the shorter name.
Both register 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 automatically.
Send bulk messages
Using IEnumerable of builders
Using BulkSmsBuilder
Events
The Twilio SMS service raises events around send operations:
MessageSending
Fired before each message is sent, including during bulk operations:
MessageSent
Fired after each message is sent (success or failure):
BulkSending
Fired before a bulk send starts:
BulkSent
Fired after a bulk send completes:
Complete event example
Events fire even when operations fail, so you can track every attempt. Useful for monitoring, logging, and user notifications.
Twilio error codes
Twilio-specific error codes are included in 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
The library does not include built-in retry or timeout logic. Apply resilience at the application layer (e.g. using Lyo.Resilience with AddLyoResilienceHandler on the HttpClient, or by wrapping ISmsService calls) as needed.
Rate limiting
- Concurrent requests. Limited to 10 concurrent requests (configurable via
BulkSmsConcurrencyLimit). - Throttling.
SemaphoreSlimcaps concurrency. - Async. Methods take
CancellationToken. - Bulk limits. Maximum messages per bulk operation (configurable via
MaxBulkSmsLimit).
Thread safety
- All instance fields are readonly
- Bulk operations use thread-safe collections (
ConcurrentBag) - Rate limiting uses
SemaphoreSlimfor thread-safe concurrency control - The underlying Twilio SDK client is thread-safe
TwilioOptions properties
| 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 | Max concurrent bulk SMS requests |
MaxMessageBodyLength |
int |
No | 1600 | Max message body length in characters |
MaxBulkSmsLimit |
int |
No | 1000 | Max messages per bulk operation |
EnableMetrics |
bool |
No | false | Enable metrics collection |
Logging
The library 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
Phone numbers are masked in logs (last 4 digits only).
Metrics
Set 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 runs via
services.AddOptions<TwilioOptions>().ValidateOnStart()when usingAddTwilioSmsServiceFromConfiguration()/AddTwilioSmsService(IConfiguration, ...).
Dependencies
Generated from ProjectReference / PackageReference (same model as docs/Lyo.ProjectGraph.html).
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.Common(transitive, lyo)Lyo.Metrics(transitive, lyo)Lyo.Sms.Models(transitive, lyo)Microsoft.Extensions.DependencyInjection.Abstractions10.0.5(transitive, microsoft)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.Exceptions (>= 1.0.13)
- Lyo.Result (>= 1.0.13)
- Lyo.Sms (>= 1.0.13)
- 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.Exceptions (>= 1.0.13)
- Lyo.Result (>= 1.0.13)
- Lyo.Sms (>= 1.0.13)
- 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.