Lyo.Sms.Twilio 1.0.2

dotnet add package Lyo.Sms.Twilio --version 1.0.2
                    
NuGet\Install-Package Lyo.Sms.Twilio -Version 1.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Lyo.Sms.Twilio" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Lyo.Sms.Twilio" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Lyo.Sms.Twilio" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Lyo.Sms.Twilio --version 1.0.2
                    
#r "nuget: Lyo.Sms.Twilio, 1.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Lyo.Sms.Twilio@1.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Lyo.Sms.Twilio&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=Lyo.Sms.Twilio&version=1.0.2
                    
Install as a Cake Tool

Lyo.Sms.Twilio

A production-ready Twilio SMS/MMS service implementation for .NET, built on the extensible Lyo.Sms library.

Features

  • Twilio Integration - Full support for Twilio SMS and MMS messaging
  • Bulk Messaging - Efficient bulk SMS sending with rate limiting
  • MMS Support - Send multimedia messages with up to 10 media attachments
  • Message Querying - Query messages by various filter criteria
  • Error Handling - Comprehensive error handling with Twilio-specific error codes
  • Logging - Built-in logging support via Microsoft.Extensions.Logging
  • Metrics - Optional metrics collection for monitoring SMS operations
  • Dependency Injection - Full support for .NET dependency injection
  • Async/Await - Fully asynchronous API with cancellation token support
  • Thread-Safe - Thread-safe implementation for concurrent use
  • Validation - Automatic validation of required configuration options
  • Events - Events for message sending, message sent, bulk sending, and bulk sent

Examples

1. Configure Twilio Options

{
  "TwilioOptions": {
    "AccountSid": "your_account_sid",
    "AuthToken": "your_auth_token",
    "DefaultFromPhoneNumber": "+1234567890",
    "BulkSmsConcurrencyLimit": 10,
    "MaxMessageBodyLength": 1600,
    "MaxBulkSmsLimit": 1000,
    "EnableMetrics": false
  }
}

1. 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)
};

2. Register Services

// In ConfigureServices(context, services):
services.AddTwilioSmsServiceFromConfiguration(context.Configuration);
// Override the configuration section name (default: "TwilioOptions"):
// services.AddTwilioSmsServiceFromConfiguration(context.Configuration, "MyTwilio");

3. Use the Service

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}");
        }
    }
}

Using the Builder Pattern

var builder = SmsMessageBuilder
    .New()
    .SetTo("+1234567890")
    .SetFrom("+1987654321")
    .SetBody("Hello, World!");

var result = await _smsService.SendAsync(builder);

Sending MMS (Multimedia Messages)

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);

Sending 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}");
    }
}

Sending Bulk Messages (2)

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}");
    }
}

Querying 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

Getting 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}");
}

Testing Connection

var isConnected = await _smsService.TestConnectionAsync();
if (isConnected)
{
    Console.WriteLine("Connected to Twilio!");
}

Using Events

_smsService.MessageSending += (sender, args) =>
{
    var request = args.SmsRequest;
    Console.WriteLine($"Sending SMS to {request.To}: {request.Body}");
};

Using Events (2)

_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}");
    }
};

Using Events (3)

_smsService.BulkSending += (sender, args) =>
{
    Console.WriteLine($"Starting bulk send for {args.BulkSmsMessage.Count} messages");
};

Using Events (4)

_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}");
};

Using Events (5)

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)}");
        }
    }
}

1. Configure Twilio Options

Using Configuration File (appsettings.json)
Using Code

2. 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 (resilient policies layered via the application's IHttpClientFactory configuration apply automatically).

Sending Bulk Messages

Using IEnumerable of Builders

Using Events

The Twilio SMS service provides events for monitoring message operations:

MessageSending Event

Fired before each message is sent (including during bulk operations):

MessageSent Event

Fired after each message is sent (success or failure):

BulkSending Event

Fired before a bulk send operation starts:

BulkSent Event

Fired after a bulk send operation completes:

Complete Event Example

Note: Events fire even when operations fail, allowing you to track all SMS operations regardless of success or failure. This is useful for monitoring, logging, and user notifications.

Twilio-Specific Features — 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)
  • Automatic Throttling: Built-in semaphore-based throttling
  • Non-blocking: Uses async/await for efficient resource usage
  • Bulk Limits: Maximum number of messages per bulk operation (configurable via MaxBulkSmsLimit)

Thread Safety

  • All instance fields are readonly
  • Bulk operations use thread-safe collections (ConcurrentBag)
  • Rate limiting uses SemaphoreSlim for 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 for all 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 automatically masked in logs (only last 4 digits shown) for privacy.

Metrics

Optional metrics collection is available:

services.AddLyoMetrics();
services.AddTwilioSmsService(options =>
{
    options.EnableMetrics = true;
    // ... other options
});

Metrics tracked:

  • sms.twilio.send.duration
  • sms.twilio.send.success
  • sms.twilio.send.failure
  • sms.twilio.bulk.send.duration
  • sms.twilio.bulk.send.total
  • sms.twilio.bulk.send.success
  • sms.twilio.bulk.send.failure
  • sms.twilio.bulk.send.last_duration_ms
  • sms.twilio.api.get_message.duration
  • sms.twilio.api.get_messages.duration
  • sms.twilio.test_connection.duration

Validation

  • AccountSid is required
  • AuthToken is required
  • Validation runs via services.AddOptions<TwilioOptions>().ValidateOnStart() when using AddTwilioSmsServiceFromConfiguration() / 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.Http 10.0.5 — (direct, microsoft)
  • Microsoft.Extensions.Logging.Abstractions 10.0.5 — (direct, microsoft)
  • Microsoft.Extensions.Options.ConfigurationExtensions 10.0.5 — (direct, microsoft)
  • Twilio 7.14.9 — (direct, third-party)
  • Lyo.Common — (transitive, lyo)
  • Lyo.Metrics — (transitive, lyo)
  • Lyo.Sms.Models — (transitive, lyo)
  • Microsoft.Extensions.DependencyInjection.Abstractions 10.0.5 — (transitive, microsoft)
  • System.Memory 4.6.3 — (transitive, microsoft, netstandard2.0)
  • System.Text.Json 10.0.5 — (transitive, microsoft, netstandard2.0)
Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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.

Version Downloads Last Updated
1.0.2 0 8/19/2026
1.0.1 49 8/18/2026
1.0.0 63 8/16/2026