Skip to content

FRD: SMS Provider

Document Summary

FieldDetails
Feature NameSMS Provider Library
StatusDraft
OwnerDavid Holmes
ContributorsEngineering
Target Releasev2.0.0 (P2)
Related LinksRoadmap item #72, ADR-027 (Default Tech Stack), ADR-014 (Open Source First)
Last Updated2026-06-02
Open Source Librariestwilio
DocumentationTwilio Node Docs · SMS API Reference · Messaging Quickstart

This FRD ships @dmwd-io/sms as an optional package that re-exports twilio as the platform standard for SMS messaging via Twilio. The value is standards and drift prevention, not a custom abstraction. Platform conventions (env var names, defaults) are documented on top of the library’s own API.

Introduction

Overview

Per ADR-014 (Open Source First), we designate twilio as the community library for SMS messaging and re-export it through @dmwd-io/sms without building a custom provider interface on top. The package documents the platform env var conventions — TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_FROM_NUMBER — so all services initialize the client consistently. No custom XProvider interface is built; the twilio library’s own API is the interface.

Goals

  • Designate twilio as the platform standard for SMS messaging per ADR-014.
  • Re-export twilio through @dmwd-io/sms so all services import from one canonical location.
  • Document TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_FROM_NUMBER as the platform env var conventions.
  • Include OTP and alert message templates with variable interpolation.

Non-Goals

  • Building a custom SmsProvider interface or adapter layer — the library’s own API is the interface (per ADR-014).
  • Implementing a production-ready AWS SNS adapter or other vendor adapters.
  • Managing phone number provisioning or carrier lookup.
  • Building an admin UI for SMS templates.
  • Handling MMS or rich media messages.

Scope

In Scope

AreaDescription
Thin re-export@dmwd-io/sms re-exports twilio as the platform standard
Env var conventionsDocuments TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_FROM_NUMBER
OTP templateTemplate function producing a 6-digit OTP message with expiry
Alert templateTemplate function producing a plain-text alert notification
Template interpolationString interpolation utility for custom templates
Unit testsCoverage of template functions and re-export surface
DocumentationStorybook MDX docs with usage examples

Out of Scope

AreaReason
Custom provider interface or adapter layerADR-014: library API is the interface
Vendor-specific adapters beyond TwilioTwilio is the designated platform standard
Phone number validationHandled by form-validation utilities
Delivery webhooks / inbound SMSRequires server infrastructure not in scope for a library
Message queue integrationConsumer responsibility
Internationalization of templatesDeferred to i18n effort

Users and Pain Points

User Groups

UserDescriptionNeeds
Application developersEngineers building features that send SMS (OTP, alerts)A stable, testable interface they can program against without coupling to a vendor SDK
QA engineersTeam members verifying SMS-dependent flowsA mock adapter that lets them assert on sent messages without real SMS delivery
Platform engineersEngineers wiring production adaptersA clear contract so adapters are consistent and swappable

Pain Points

UserPain PointImpact
Application developersDirect Twilio/SNS SDK calls scattered across services, each with different error handlingVendor lock-in; inconsistent error handling; hard to test
QA engineersNo way to verify SMS content in automated tests without a real providerTests are slow, flaky, or skip SMS verification entirely
Platform engineersNo shared schema for delivery results; each service defines its ownMonitoring and alerting logic is duplicated and inconsistent

Definitions

TermDefinition
ProviderAn object that implements the SmsProvider interface and sends messages through a specific channel (Twilio, SNS, mock)
Delivery resultA typed object describing the outcome of a send attempt: delivered, failed, pending, or rate-limited
OTPOne-Time Password: a short-lived numeric code sent via SMS for identity verification
TemplateA function that accepts variables and returns a formatted message string
Mock adapterAn in-memory SmsProvider implementation that records messages for test assertions

Current State

Existing Behavior

There is no shared SMS abstraction. Individual services call vendor SDKs directly, each with ad-hoc error handling and result parsing.

Current Limitations

  • No contract interface exists; each service defines its own send function.
  • No typed delivery result schema; services parse vendor-specific responses inline.
  • No mock adapter; integration tests either skip SMS or use real providers with test credentials.
  • OTP message text is hardcoded in multiple places with inconsistent wording.

Existing Workarounds

  • Developers copy-paste Twilio initialization code between services.
  • Tests stub fetch or the vendor SDK at the HTTP level, which is brittle and vendor-coupled.

Proposed Solution

Summary

Ship @dmwd-io/sms as a thin re-export of twilio, designating it as the platform standard per ADR-014. No custom interface is built — the library’s API is the interface. The package adds only platform conventions on top: env var names and template utilities.

import { Twilio } from '@dmwd-io/sms';
const client = new Twilio(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN
);
await client.messages.create({
from: process.env.TWILIO_FROM_NUMBER,
to: '+15551234567',
body: createOtpMessage('123456', 10),
});

Platform env var conventions:

  • TWILIO_ACCOUNT_SID — Twilio account SID
  • TWILIO_AUTH_TOKEN — Twilio auth token
  • TWILIO_FROM_NUMBER — sender phone number in E.164 format

Key Capabilities

  • @dmwd-io/sms re-exports all exports from twilio verbatim.
  • createOtpMessage(code, expiryMinutes) and createAlertMessage(title, body) template functions.
  • interpolate(template, variables) utility for custom templates.
  • No custom provider interface or adapter layer.

User Experience

Not applicable (library, no UI).

Developer Experience

Developers import from @dmwd-io/sms instead of twilio directly, initialize the client with the documented env vars, and use template utilities for consistent message wording. The import path is the only convention enforced — the full twilio API is available.


Requirements

IDRequirementPriorityNotes
FR-001The library must re-export twilio from @dmwd-io/smsMustThin re-export per ADR-014
FR-002The library must document TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_FROM_NUMBER as platform env varsMustStandards enforcement
FR-003The library must provide OTP and alert template functionsMustVariable interpolation
FR-004The library should provide a generic interpolate utilityShouldFor custom templates
FR-005The library could ship a ConsoleSmsProvider for local dev loggingCouldLogs to stdout

Priority Definitions

PriorityMeaning
MustRequired for this feature to ship.
ShouldImportant, but can be deferred if needed.
CouldNice to have. Not required for initial release.

Functional Requirements

IDRequirementUser BenefitPriority
FUNC-001@dmwd-io/sms re-exports all named exports from twilioCanonical import path for all SMS usageMust
FUNC-002Platform env var names are documented in the package README and Storybook docsConsistent client initialization across servicesMust
FUNC-003createOtpMessage(code, expiryMinutes) returns a string containing the code and expiryConsistent OTP wording across servicesMust
FUNC-004createAlertMessage(title, body) returns a formatted alert stringConsistent alert formatMust
FUNC-005interpolate(template, vars) returns a string with variables substitutedCustom template support without string concatenationShould

Non-Functional Requirements

IDRequirementCategoryPriority
NFR-001The library must have twilio as a peer dependency, not a bundled dependencyMaintainabilityMust
NFR-002All public types must be exported from the package entry pointMaintainabilityMust
NFR-003The library must not log or persist phone numbers by defaultSecurityMust
NFR-004The library must support tree-shaking so consumers can import only what they needPerformanceShould

API / Interface Requirements

Public API

NameTypeDescriptionRequired
Twilioclass (re-export)Twilio SDK client — see twilio docsYes
createOtpMessagefunction(code: string, expiryMinutes: number) => stringYes
createAlertMessagefunction(title: string, body: string) => stringYes
interpolatefunction(template: string, vars: Record<string, string>) => stringNo

Example Usage

import { Twilio, createOtpMessage } from '@dmwd-io/sms';
const client = new Twilio(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN
);
const body = createOtpMessage('123456', 10);
await client.messages.create({
from: process.env.TWILIO_FROM_NUMBER,
to: '+15551234567',
body,
});

API Notes

  • All twilio exports are available via @dmwd-io/sms — no subset or wrapper.
  • Phone number format validation is the caller’s responsibility.
  • twilio must be installed by the consuming package; it is a peer dependency.

Accessibility Requirements

IDRequirementNotes
A11Y-001Not directly applicable; this is a headless libraryNo UI surface

Checklist

  • Keyboard support is defined. (N/A — no UI)
  • Focus behavior is defined. (N/A — no UI)
  • Screen reader behavior is defined. (N/A — no UI)
  • Color contrast requirements are met. (N/A — no UI)
  • Reduced motion behavior is considered. (N/A — no UI)
  • Semantic HTML expectations are documented. (N/A — no UI)
  • ARIA usage is defined only where needed. (N/A — no UI)

Content and Documentation Requirements

IDRequirementLocationPriority
DOC-001API reference with all exported types and functionsStorybook MDXMust
DOC-002Usage guide showing env var setup and client initializationStorybook MDXMust
DOC-003Template function usage examplesStorybook MDXMust
DOC-004Template customization guideStorybook MDXShould
DOC-005Migration guide for services currently calling twilio directlyStorybook MDXShould

Documentation Should Include

  • Overview of the ADR-014 rationale (re-export, not custom interface)
  • When to use the library vs. direct SDK calls
  • Installation and peer dependency setup
  • Env var names and where to source them
  • Basic usage with createOtpMessage
  • Advanced usage: custom templates, batch sending
  • API reference for all exports
  • Common mistakes (e.g., not validating phone numbers before send)

Dependencies

DependencyTypeOwnerStatusNotes
TypeScript 5.xEngineeringDavid HolmesReadyBuild toolchain
VitestEngineeringDavid HolmesReadyTest runner
twilioPeerTwilioReadyPlatform standard per ADR-014

Risks and Tradeoffs

Risk / TradeoffImpactMitigation
Twilio API changes break consumersMajor version bumps in twilio may require updates across servicesPin twilio peer dependency range; communicate breaking changes in changelog
Template functions may be too rigidDifferent products may need different OTP wordingShip interpolate utility so consumers can build custom templates
No retry logic in the contractCallers must implement their own retryKeep the library focused on re-export and templates; retry is an application concern
Consumers bypass @dmwd-io/sms and import twilio directlyDrift re-emergesLint rule enforcing @dmwd-io/sms as the import path (future work)

Open Questions

IDQuestionOwnerStatusResolution
Q-001Should send accept an E.164 type or a plain string for phone numbers?David HolmesOpen
Q-002Should we ship a ConsoleSmsProvider for local dev in the initial release or defer?David HolmesOpen
Q-003Should an ESLint rule enforce @dmwd-io/sms over direct twilio imports?David HolmesOpen

Acceptance Criteria

IDCriteriaRelated Requirement
AC-001import { Twilio } from '@dmwd-io/sms' resolves correctlyFR-001
AC-002TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_FROM_NUMBER are documented in the package README and StorybookFR-002
AC-003createOtpMessage("123456", 10) returns a string containing both the code and the expiryFUNC-003
AC-004createAlertMessage("Title", "Body") returns a formatted string with both argumentsFUNC-004
AC-005All public types are re-exported from the package indexNFR-002
AC-006Unit tests pass with full coverage of the template functionsFR-003
AC-007Storybook MDX docs render without errors and include usage examplesDOC-001

LLM Handoff Instructions

Expected LLM Behavior

  • Follow the requirements and acceptance criteria in this document.
  • Do not expand scope beyond the In Scope section.
  • Respect the Out of Scope section.
  • Ask for clarification only when a requirement cannot be safely interpreted.
  • Prefer existing design system patterns over inventing new ones.
  • Preserve public API compatibility unless this document says otherwise.
  • Update documentation and Storybook examples when behavior changes.
  • Add or update tests that map to the acceptance criteria.

Implementation steps

  1. Create the packages/sms/ directory if it does not exist.
  2. Add package.json with twilio as a peer dependency and @dmwd-io/sms as the package name.
  3. Create src/index.ts that contains export * from 'twilio' plus the template utilities.
  4. Document env var names (TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_FROM_NUMBER) in the package README and Storybook MDX.
  5. Run pnpm typecheck to confirm no type errors.

LLM Should Not

  • Build a custom SmsProvider interface or adapter layer — the twilio library’s API is the interface per ADR-014.
  • Connect to real SMS providers during tests.
  • Add new dependencies without justification.
  • Change unrelated components.
  • Include phone numbers or PII in test fixtures (use obviously fake numbers like +15550001234).
  • Implement retry or queue logic.

Decision Log

DateDecisionReasonOwner
2026-05-26Use discriminated union for delivery results instead of status codesType narrowing makes caller error handling saferDavid Holmes
2026-05-26Zero runtime dependenciesLibrary must be lightweight and vendor-neutralDavid Holmes
2026-05-26Ship mock adapter as part of the library, not a separate packageReduces friction for testing; mock is smallDavid Holmes
2026-06-02Reframed per ADR-014 (Open Source First): adopt twilio as thin re-export rather than building a custom provider interface. Library API is the interface.ADR-014 eliminates unnecessary abstraction over well-maintained community librariesDavid Holmes

Document History

DateAuthorChange
2026-05-26David HolmesInitial draft
2026-06-02David HolmesReframed per ADR-014: ship as thin re-export of twilio, drop custom interface.