Skip to content

SMS

The SMS module provides a provider-agnostic SMS/MMS sending abstraction with typed models, status tracking, effects composition, and built-in test doubles. Providers like Twilio and Bandwidth implement ISmsService.

Quick Start

using Deepstaging.Sms;

var result = await smsService.SendAsync(new SmsMessage
{
    From = new PhoneNumber("+15551234567"),
    To = new PhoneNumber("+15559876543"),
    Body = "Your verification code is 123456"
});

if (result.Status == SmsStatus.Accepted)
    Console.WriteLine($"Sent: {result.ProviderMessageId}");

Features

Feature Description
ISmsService Core abstraction — SendAsync, GetStatusAsync
SmsMessage Model with To, From (PhoneNumber), Body, MediaUrls (for MMS), StatusCallbackUrl, Metadata
SmsResult Result with SmsMessageId, ProviderMessageId, SmsStatus, error details
MMS support Include MediaUrls to send MMS instead of SMS
Status polling GetStatusAsync(SmsMessageId) to check delivery status
Webhook events SmsDelivered, SmsFailed, SmsReceived
Effects module SmsModule[EffectsModule(typeof(ISmsService))] for Eff<RT, T> composition
TypedIds PhoneNumber (E.164), SmsMessageId
Test double TestSmsService with call recording and seedable state
InMemory fallback InMemorySmsService registered by default via TryAddSingleton

Core Interface

public interface ISmsService
{
    Task<SmsResult> SendAsync(SmsMessage message, CancellationToken ct = default);
    Task<SmsResult> GetStatusAsync(SmsMessageId messageId, CancellationToken ct = default);
}

Effects Composition

from result in SmsModule.Sms.SendAsync<AppRuntime>(new SmsMessage
{
    From = new PhoneNumber("+15551234567"),
    To = new PhoneNumber("+15559876543"),
    Body = "Your order has shipped!"
})
select result;

For multi-step composition and error handling, see Effects Composition.

Models

SmsMessage

Property Type Description
To PhoneNumber Recipient (E.164 format)
From PhoneNumber Sender (E.164 format)
Body string Message text
MediaUrls IReadOnlyList<Uri>? Media URLs for MMS. Null/empty = plain SMS
StatusCallbackUrl string? Webhook URL for delivery callbacks
Metadata IReadOnlyDictionary<string, string>? Provider-specific pass-through

SmsStatus

Value Description
Accepted Provider accepted, waiting to send
Sending Being transmitted
Sent Sent to carrier network
Delivered Confirmed delivered to handset
Failed Delivery failed permanently
Undelivered Carrier rejected, number invalid

TypedId Profiles

SMS uses two custom TypedId profiles:

  • sms profile — BackingType.Guid, JSON converter (used by SmsMessageId)
  • phone profile — BackingType.String, JSON converter (used by PhoneNumber)

Templating

Use Scriban templates for SMS message bodies. Plain-text templates keep message copy out of handler code and make content easy to review and update.

{{- # Templates/Sms/VerificationCode.scriban-txt -}}
Your {{ app_name }} verification code is {{ code }}. It expires in {{ expires_minutes }} minutes.
{{- # Templates/Sms/ShippingUpdate.scriban-txt -}}
{{ customer_name }}, your order #{{ order_id }} has shipped!
{{- if tracking_url }}
Track it: {{ tracking_url }}
{{- end }}
var body = Template.RenderText("Sms/VerificationCode", new { AppName = "Acme", Code = "482901", ExpiresMinutes = 10 });

await smsService.SendAsync(new SmsMessage
{
    From = new PhoneNumber("+15551234567"),
    To = new PhoneNumber(recipient),
    Body = body
});

For syntax reference and best practices, see Scriban Templating.

Webhook Events

Event When
SmsDelivered Carrier confirmed delivery
SmsFailed Delivery failed (unreachable, invalid number, etc.)
SmsReceived Inbound SMS received on your number

Providers

Provider Package
Twilio Deepstaging.Twilio
Bandwidth Deepstaging.Bandwidth
InMemory (default) Built-in — InMemorySmsService
Test Built-in — TestSmsService

Sub-Pages

Page Description
Effects Composition Eff<RT, T> pipelines — send, poll status, cross-module composition
Generated Code What [EffectsModule] emits — capability interface, effect methods, DI
Testing TestSmsService — call recording, custom responses, exception injection

Source

src/Core/Deepstaging.Runtime/Sms/