Skip to main content

Context

SMS is the channel with the widest reach in Latin America and the most operational friction of any we support. It differs from every other adapter in three ways that make it its own delivery: It needs a new vendor, which means an ADR before the first import (DEC-E1). It costs real money per message, so a runaway loop is a bill rather than a graph. And sender registration is per country — alphanumeric sender IDs, short codes and long codes each have their own rules per market, and sending without registration means silent non-delivery rather than an error. The channel row and the port have existed since day one (FS-MSG-0001). Only the adapter is missing, which is exactly what DEC-E1 intended.

Scope (normative)

  • The SMS adapter implementing NotificationChannelPort.
  • messaging.sms_sender_registrations: per tenant, per country.
  • Per-message cost tracking, feeding metering.
  • Segment counting and encoding awareness (GSM-7 versus UCS-2).
  • Delivery receipts mapped into the standard status vocabulary.
  • A hard per-tenant spend cap, independent of the message quota.

Non-scope (normative)

  • Inbound SMS and two-way conversation. Different product.
  • WhatsApp — F2, and a different channel entirely despite sharing a phone number.
  • Choosing the provider. That is the ADR this feature is blocked on.

Behaviour (normative)

  1. Blocked until the provider ADR exists (DEC-E1). No SMS vendor is imported before it.
  2. Sending to a country where the tenant has no verified sender registration is rejected with a typed error, not attempted. An unregistered send is silently dropped by carriers, which is the worst failure mode: it looks like it worked.
  3. Phone numbers are normalized to E.164 before sending, and an invalid number fails terminally and suppresses.
  4. Message length is computed with encoding awareness: a single accented character switches the message to UCS-2 and halves the segment size. The console shows segment count before sending, because a tenant who thinks they sent one message and paid for three will not think it was fair.
  5. Cost per message is recorded per send and fed to metering. SMS is the one channel where a bug costs money in real time.
  6. A hard per-tenant spend cap applies, independent of the message quota, and it stops sending rather than billing an overage.
  7. Delivery receipts map into the standard status vocabulary. Where the provider offers no receipt, the send stops at sent and the limitation is documented rather than faked as delivered.
  8. SMS is available for the transactional rail only in F1b. Marketing SMS carries per-country consent requirements stricter than email, and it waits for that to be specified.

Data (normative)

API (normative)

Events (normative)

None beyond the standard send status events.

Acceptance criteria (normative)

  1. No SMS vendor is imported until its ADR is accepted — enforced by the lockfile allowlist.
  2. Sending to an unregistered country is rejected with a typed error and no provider call.
  3. Numbers are normalized to E.164; an invalid one fails terminally and suppresses.
  4. Segment count is correct for GSM-7 and for UCS-2, proven with fixtures including an accented character.
  5. Cost is recorded per send and appears in the usage snapshot.
  6. The spend cap stops sending, and does not bill an overage.
  7. A provider without delivery receipts leaves the send at sent, never a fabricated delivered.
  8. Negative: no marketing-category message is dispatched on SMS in F1b.

Execution

Blocked on the provider ADR. Once accepted: an adapter file plus a catalog row, with no change to the dispatcher — which is the point of the port.

Open questions

Changelog

Delivery record

Not implemented yet.