feat(sms): MessagingProvider abstraction + BYO Twilio/Telnyx provider choice - #205
Merged
important-new merged 15 commits intoJun 26, 2026
Merged
Conversation
…nt adapter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…safe) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R6hYGDtjhEfMXwWDz4om21
…-dedicated) - server: expand TenantConfigPatchSchema smsMode enum to own|managed_shared|managed_dedicated (platform excluded from tenant input); PATCH handler rejects 'platform' on SaaS (zod enum gate + runtime guard), coerces any mode to 'own' on standalone - ui: replace two-button platform/own toggle with three-option radio selector in SmsDeliveryPanel (own=BYO, managed_shared=default/included, managed_dedicated=disabled paid-upgrade affordance); standalone shows only BYO notice unchanged - action: settings-communication save-sms-config passes through three valid modes (never submits 'platform'); legacy stored 'platform' value renders as managed_shared - tests: 5 new cases in sms-api.spec.ts covering SaaS rejection of platform, SaaS acceptance of own/managed_shared/managed_dedicated, standalone coercion to own - baseline: bump file-size-baseline for the two files that grew (reviewed decision) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R6hYGDtjhEfMXwWDz4om21
…ttings Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R6hYGDtjhEfMXwWDz4om21
1 task
…aseline The provider-aware automation refactor shifted line numbers, staling the line-keyed tenant-scoping baseline. Properly scope the three automationLogs status updates by tenant (inspection.tenantId) — a security improvement over baselining — and re-snapshot the baseline for the incidental line-shifts of pre-existing accepted queries in delivery.ts / api/sms.ts.
This was referenced Jun 26, 2026
important-new
added a commit
that referenced
this pull request
Jun 26, 2026
…re (#207) The Communication settings Delivery panels predated the multi-provider abstractions (#205 SMS Twilio/Telnyx, #206 email Resend/SendGrid/Postmark/ Mailgun) and still hardcoded Resend/Twilio in their mode labels, hints, and "configured" guardrails. With a non-default provider selected this produced wrong copy and a false "no Resend key — emails will fail" banner even when the chosen provider (e.g. SendGrid) was fully configured. - Email delivery: mode toggle "My own Resend" → "My own provider"; own-mode guardrail + status + sender-domain hint now reflect email_byo_provider (Resend/SendGrid/Postmark/Mailgun). The own-mode "configured" check keys off the selected provider's credentials (Mailgun needs key + domain), not just RESEND_API_KEY. Platform-mode path is unchanged. - SMS delivery: intro/self-host/effective-source copy generalized off the Twilio-only wording to the selected provider (Twilio/Telnyx); the toll-free compliance block is gated to Twilio (Telnyx has a different flow). - Extracted ownEmailProviderConfigured() to app/lib/email-provider-config.ts (pure, unit-tested) — the masked-secret "" = not-configured contract. Client-only; no server/schema change. Full local verify green (lint/type-check/db:check/unit 2256/web 611/build).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces a provider abstraction for SMS so self-hosters/tenants can bring their own provider (Twilio or Telnyx), and surfaces provider + credential selection in Settings → Communication. This is the open-source BYO + provider-interface scope of #196. (The Track L SMS engine — send, consent, opt-in/STOP/HELP, templates, metering — already shipped; this PR adds the abstraction layer + a second adapter.)
What's included
MessagingProviderinterface + thinfetchadapters:TwilioClient(extracted from the existing basic-auth path) and a newTelnyxProvider(Telnyx v2 Messages API). No provider SDK.resolveProvider(byoProvider, creds)selection; the live send path (test-send + automation) routes through it. Twilio behavior is byte-for-byte unchanged.tenant_configs.sms_byo_providercolumn +messaging_compliancetable (schema-first migration; cleanADD COLUMN, no table rebuild).GET /api/manager/sms/compliance) + a status pill in Settings.platformis reserved for first-party (rejected as a tenant choice; standalone forced to BYO).docs/sms-compliance.md).Out of scope
Platform-provisioned / managed sending (where the platform handles carrier onboarding on a tenant's behalf) is intentionally not in this PR — self-host + BYO is the supported path here. The
managed_*sender modes are selectable placeholders without a live send path.Deferred follow-up
Telnyx inbound STOP/HELP (Ed25519 webhook verification) — outbound ships now;
validateInboundSignatureis a documented stub.Verification
test:unit(2190) +test:web(593) + full type-check +db:checkall green.sms_byo_provider='telnyx'+ encrypted creds, reload reads back the choice.Refs #196
🤖 Generated with Claude Code