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:
smsprofile —BackingType.Guid, JSON converter (used bySmsMessageId)phoneprofile —BackingType.String, JSON converter (used byPhoneNumber)
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/