Files
skills/docs/communication/messaging/notification-channel/src/channels/sms-channel.md
2026-05-29 08:20:56 +01:00

2.5 KiB

sms-channel — SMS delivery channel

Purpose

sms-channel.ts ships the SmsChannel DeliveryChannel implementation. Like the email channel, it is provider-neutral: a caller-supplied sendFn(message) performs the actual API call into Twilio, MessageBird, Vonage, Sinch, or any in-house SMSC. The channel keeps the responsibility surface tiny — recipient validation (E.164 + prefix), payload shaping, and error swallowing — so the router can stay decoupled from any specific carrier.

Features

  • Provider-neutral via injectable sendFn.
  • canSend rejects recipients that do not start with + (E.164 prefix); avoids hitting upstream providers with formats they will reject.
  • Propagates metadata to the provider message for correlation IDs and segmentation.
  • Injectable Clock for deterministic timestamp fields in tests.
  • Returns DeliveryResult even on provider failure — never throws to the router.

Usage: Basic

import { SmsChannel } from "@suaveplan/notification-channel";

const channel = new SmsChannel({
    from: "+15559999999",
    sendFn: async (msg) => ({
        messageId: await twilio.messages.create({
            from: "+15559999999",
            to: msg.to,
            body: msg.body,
        }).sid,
    }),
});

Usage: Advanced

import { SmsChannel } from "@suaveplan/notification-channel";

const channel = new SmsChannel({
    from: "+15559999999",
    sendFn: async (msg) => {
        const { sid } = await provider.send({
            from: "+15559999999",
            to: msg.to,
            body: msg.body,
            ...(msg.metadata?.["statusCallback"] !== undefined
                ? { statusCallback: msg.metadata["statusCallback"] }
                : {}),
        });
        return { messageId: sid };
    },
    clock: () => testNow,
});

API Reference

  • SmsChannel(config: SmsChannelConfig) — constructor.
  • SmsChannelConfig — { from, sendFn, clock? }.
  • SmsMessage — { to, body, metadata? } passed to sendFn.
  • canSend(n): boolean, send(n): Promise<DeliveryResult> — DeliveryChannel impl.

Implementation Notes

The + prefix check is intentionally conservative — it does not validate the full E.164 country/area format because providers do that anyway and over-validating client-side breaks edge cases like short-code dispatch. Tests cover both Error and non-Error throws inside sendFn to lock in the String(err) coercion. The injected clock only affects the DeliveryResult.timestamp field; provider-recorded send time remains authoritative.