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
                    
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="3.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Lyo.Sms.Twilio" Version="3.0.0" />
                    
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 3.0.0
                    
#r "nuget: Lyo.Sms.Twilio, 3.0.0"
                    
#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@3.0.0
                    
#: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=3.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Lyo.Sms.Twilio&version=3.0.0
                    
Install as a Cake Tool

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. GetMessagesAsync filtered by SmsMessageQueryFilter.
  • Errors. Twilio-specific codes on TwilioSmsResult.
  • Logging. Writes through Microsoft.Extensions.Logging.
  • Metrics. Optional instrumentation for SMS operations.
  • DI. AddTwilioSmsService plus AddTwilioSmsServiceFromConfiguration.
  • Async. Every method accepts a CancellationToken.
  • Concurrency. Safe to use from multiple threads.
  • Validation. TwilioOptionsValidator verifies 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 SemaphoreSlim caps 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 SemaphoreSlim so 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.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 is wired via services.AddOptions<TwilioOptions>().ValidateOnStart() when using AddTwilioSmsServiceFromConfiguration() / 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.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.Metrics (transitive, lyo)
  • Lyo.Sms.Models (transitive, lyo)
  • Microsoft.Bcl.AsyncInterfaces 10.0.5 (transitive, microsoft, netstandard2.0)
  • 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
3.0.0 131 10/23/2026
2.0.0 165 9/9/2026
1.0.13 146 8/25/2026
1.0.11 140 8/23/2026
1.0.9 161 8/22/2026
1.0.6 158 8/20/2026
1.0.4 152 8/20/2026
1.0.3 140 8/19/2026
1.0.2 146 8/19/2026
1.0.1 150 8/18/2026
1.0.0 143 8/16/2026