feat: migrate SMS/voice to Telnyx (TeXML), keep browser dial pad on Twilio

Twilio now handles only the in-browser dial pad (Access Token + browser-Device
outbound TwiML); Telnyx takes over SMS, inbound voicemail, the AI voice
conversation loop, and staff-triggered outbound calls via TeXML. Adds
TelnyxSettings model, Communication.provider discriminator, new comms/routes
modules, and a real Telnyx Settings page.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-03 15:39:43 -04:00
co-authored by Claude Sonnet 5
parent b899659e13
commit 79ac6aaa31
463 changed files with 7823 additions and 3397 deletions
@@ -44,6 +44,7 @@ model User {
cloudFiles CloudFile[]
communications Communication[]
twilioSettings TwilioSettings?
telnyxSettings TelnyxSettings?
aiSettings AiSettings?
officeHours OfficeHours?
officeContact OfficeContact?
@@ -562,6 +563,11 @@ enum CommunicationStatus {
no_answer
}
enum CommunicationProvider {
twilio
telnyx
}
model Communication {
id Int @id @default(autoincrement())
patientId Int
@@ -570,9 +576,13 @@ model Communication {
channel CommunicationChannel
direction CommunicationDirection
status CommunicationStatus
// Which platform produced twilioSid — twilio for dial-pad-originated calls,
// telnyx for everything else (SMS, inbound/AI voice, staff-triggered outbound calls).
provider CommunicationProvider @default(twilio)
body String?
callDuration Int?
// Provider SID — Twilio MessageSid/CallSid, or Telnyx message/call id. See `provider`.
twilioSid String?
createdAt DateTime @default(now())
@@ -606,6 +616,8 @@ model TwilioSettings {
accountSid String
authToken String
phoneNumber String
// Vestigial — inbound voice moved to Telnyx (TelnyxSettings.greetingMessage). No longer
// read/written; kept to avoid a destructive migration for a cosmetic cleanup.
greetingMessage String?
templates Json?
@@ -614,6 +626,24 @@ model TwilioSettings {
@@map("twilio_settings")
}
model TelnyxSettings {
id Int @id @default(autoincrement())
userId Int @unique
apiKey String
// Telnyx account SID (Mission Control Portal), required to place outbound
// TeXML calls via POST /texml/Accounts/{accountSid}/Calls — separate from apiKey.
accountSid String?
phoneNumber String
texmlApplicationId String?
messagingProfileId String?
webhookPublicKey String?
greetingMessage String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("telnyx_settings")
}
model AiSettings {
id Int @id @default(autoincrement())
userId Int @unique