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
@@ -6,6 +6,7 @@ export const CommunicationAggregateResultSchema = z.object({ _count: z.object({
channel: z.number(),
direction: z.number(),
status: z.number(),
provider: z.number(),
body: z.number(),
callDuration: z.number(),
twilioSid: z.number(),
@@ -6,6 +6,7 @@ export const CommunicationCreateResultSchema = z.object({
channel: z.unknown(),
direction: z.unknown(),
status: z.unknown(),
provider: z.unknown(),
body: z.string().optional(),
callDuration: z.number().int().optional(),
twilioSid: z.string().optional(),
@@ -6,6 +6,7 @@ export const CommunicationDeleteResultSchema = z.nullable(z.object({
channel: z.unknown(),
direction: z.unknown(),
status: z.unknown(),
provider: z.unknown(),
body: z.string().optional(),
callDuration: z.number().int().optional(),
twilioSid: z.string().optional(),
@@ -6,6 +6,7 @@ export const CommunicationFindFirstResultSchema = z.nullable(z.object({
channel: z.unknown(),
direction: z.unknown(),
status: z.unknown(),
provider: z.unknown(),
body: z.string().optional(),
callDuration: z.number().int().optional(),
twilioSid: z.string().optional(),
@@ -7,6 +7,7 @@ export const CommunicationFindManyResultSchema = z.object({
channel: z.unknown(),
direction: z.unknown(),
status: z.unknown(),
provider: z.unknown(),
body: z.string().optional(),
callDuration: z.number().int().optional(),
twilioSid: z.string().optional(),
@@ -6,6 +6,7 @@ export const CommunicationFindUniqueResultSchema = z.nullable(z.object({
channel: z.unknown(),
direction: z.unknown(),
status: z.unknown(),
provider: z.unknown(),
body: z.string().optional(),
callDuration: z.number().int().optional(),
twilioSid: z.string().optional(),
@@ -14,6 +14,7 @@ export const CommunicationGroupByResultSchema = z.array(z.object({
channel: z.number(),
direction: z.number(),
status: z.number(),
provider: z.number(),
body: z.number(),
callDuration: z.number(),
twilioSid: z.number(),
@@ -6,6 +6,7 @@ export const CommunicationUpdateResultSchema = z.nullable(z.object({
channel: z.unknown(),
direction: z.unknown(),
status: z.unknown(),
provider: z.unknown(),
body: z.string().optional(),
callDuration: z.number().int().optional(),
twilioSid: z.string().optional(),
@@ -6,6 +6,7 @@ export const CommunicationUpsertResultSchema = z.object({
channel: z.unknown(),
direction: z.unknown(),
status: z.unknown(),
provider: z.unknown(),
body: z.string().optional(),
callDuration: z.number().int().optional(),
twilioSid: z.string().optional(),
@@ -0,0 +1,43 @@
import * as z from 'zod';
export const TelnyxSettingsAggregateResultSchema = z.object({ _count: z.object({
id: z.number(),
userId: z.number(),
apiKey: z.number(),
accountSid: z.number(),
phoneNumber: z.number(),
texmlApplicationId: z.number(),
messagingProfileId: z.number(),
webhookPublicKey: z.number(),
greetingMessage: z.number(),
user: z.number()
}).optional(),
_sum: z.object({
id: z.number().nullable(),
userId: z.number().nullable()
}).nullable().optional(),
_avg: z.object({
id: z.number().nullable(),
userId: z.number().nullable()
}).nullable().optional(),
_min: z.object({
id: z.number().int().nullable(),
userId: z.number().int().nullable(),
apiKey: z.string().nullable(),
accountSid: z.string().nullable(),
phoneNumber: z.string().nullable(),
texmlApplicationId: z.string().nullable(),
messagingProfileId: z.string().nullable(),
webhookPublicKey: z.string().nullable(),
greetingMessage: z.string().nullable()
}).nullable().optional(),
_max: z.object({
id: z.number().int().nullable(),
userId: z.number().int().nullable(),
apiKey: z.string().nullable(),
accountSid: z.string().nullable(),
phoneNumber: z.string().nullable(),
texmlApplicationId: z.string().nullable(),
messagingProfileId: z.string().nullable(),
webhookPublicKey: z.string().nullable(),
greetingMessage: z.string().nullable()
}).nullable().optional()});
@@ -0,0 +1,2 @@
import * as z from 'zod';
export const TelnyxSettingsCountResultSchema = z.number();
@@ -0,0 +1,4 @@
import * as z from 'zod';
export const TelnyxSettingsCreateManyResultSchema = z.object({
count: z.number()
});
@@ -0,0 +1,13 @@
import * as z from 'zod';
export const TelnyxSettingsCreateResultSchema = z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
accountSid: z.string().optional(),
phoneNumber: z.string(),
texmlApplicationId: z.string().optional(),
messagingProfileId: z.string().optional(),
webhookPublicKey: z.string().optional(),
greetingMessage: z.string().optional(),
user: z.unknown()
});
@@ -0,0 +1,4 @@
import * as z from 'zod';
export const TelnyxSettingsDeleteManyResultSchema = z.object({
count: z.number()
});
@@ -0,0 +1,13 @@
import * as z from 'zod';
export const TelnyxSettingsDeleteResultSchema = z.nullable(z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
accountSid: z.string().optional(),
phoneNumber: z.string(),
texmlApplicationId: z.string().optional(),
messagingProfileId: z.string().optional(),
webhookPublicKey: z.string().optional(),
greetingMessage: z.string().optional(),
user: z.unknown()
}));
@@ -0,0 +1,13 @@
import * as z from 'zod';
export const TelnyxSettingsFindFirstResultSchema = z.nullable(z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
accountSid: z.string().optional(),
phoneNumber: z.string(),
texmlApplicationId: z.string().optional(),
messagingProfileId: z.string().optional(),
webhookPublicKey: z.string().optional(),
greetingMessage: z.string().optional(),
user: z.unknown()
}));
@@ -0,0 +1,23 @@
import * as z from 'zod';
export const TelnyxSettingsFindManyResultSchema = z.object({
data: z.array(z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
accountSid: z.string().optional(),
phoneNumber: z.string(),
texmlApplicationId: z.string().optional(),
messagingProfileId: z.string().optional(),
webhookPublicKey: z.string().optional(),
greetingMessage: z.string().optional(),
user: z.unknown()
})),
pagination: z.object({
page: z.number().int().min(1),
pageSize: z.number().int().min(1),
total: z.number().int().min(0),
totalPages: z.number().int().min(0),
hasNext: z.boolean(),
hasPrev: z.boolean()
})
});
@@ -0,0 +1,13 @@
import * as z from 'zod';
export const TelnyxSettingsFindUniqueResultSchema = z.nullable(z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
accountSid: z.string().optional(),
phoneNumber: z.string(),
texmlApplicationId: z.string().optional(),
messagingProfileId: z.string().optional(),
webhookPublicKey: z.string().optional(),
greetingMessage: z.string().optional(),
user: z.unknown()
}));
@@ -0,0 +1,54 @@
import * as z from 'zod';
export const TelnyxSettingsGroupByResultSchema = z.array(z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
accountSid: z.string(),
phoneNumber: z.string(),
texmlApplicationId: z.string(),
messagingProfileId: z.string(),
webhookPublicKey: z.string(),
greetingMessage: z.string(),
_count: z.object({
id: z.number(),
userId: z.number(),
apiKey: z.number(),
accountSid: z.number(),
phoneNumber: z.number(),
texmlApplicationId: z.number(),
messagingProfileId: z.number(),
webhookPublicKey: z.number(),
greetingMessage: z.number(),
user: z.number()
}).optional(),
_sum: z.object({
id: z.number().nullable(),
userId: z.number().nullable()
}).nullable().optional(),
_avg: z.object({
id: z.number().nullable(),
userId: z.number().nullable()
}).nullable().optional(),
_min: z.object({
id: z.number().int().nullable(),
userId: z.number().int().nullable(),
apiKey: z.string().nullable(),
accountSid: z.string().nullable(),
phoneNumber: z.string().nullable(),
texmlApplicationId: z.string().nullable(),
messagingProfileId: z.string().nullable(),
webhookPublicKey: z.string().nullable(),
greetingMessage: z.string().nullable()
}).nullable().optional(),
_max: z.object({
id: z.number().int().nullable(),
userId: z.number().int().nullable(),
apiKey: z.string().nullable(),
accountSid: z.string().nullable(),
phoneNumber: z.string().nullable(),
texmlApplicationId: z.string().nullable(),
messagingProfileId: z.string().nullable(),
webhookPublicKey: z.string().nullable(),
greetingMessage: z.string().nullable()
}).nullable().optional()
}));
@@ -0,0 +1,4 @@
import * as z from 'zod';
export const TelnyxSettingsUpdateManyResultSchema = z.object({
count: z.number()
});
@@ -0,0 +1,13 @@
import * as z from 'zod';
export const TelnyxSettingsUpdateResultSchema = z.nullable(z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
accountSid: z.string().optional(),
phoneNumber: z.string(),
texmlApplicationId: z.string().optional(),
messagingProfileId: z.string().optional(),
webhookPublicKey: z.string().optional(),
greetingMessage: z.string().optional(),
user: z.unknown()
}));
@@ -0,0 +1,13 @@
import * as z from 'zod';
export const TelnyxSettingsUpsertResultSchema = z.object({
id: z.number().int(),
userId: z.number().int(),
apiKey: z.string(),
accountSid: z.string().optional(),
phoneNumber: z.string(),
texmlApplicationId: z.string().optional(),
messagingProfileId: z.string().optional(),
webhookPublicKey: z.string().optional(),
greetingMessage: z.string().optional(),
user: z.unknown()
});
@@ -25,6 +25,7 @@ export const UserAggregateResultSchema = z.object({ _count: z.object({
cloudFiles: z.number(),
communications: z.number(),
twilioSettings: z.number(),
telnyxSettings: z.number(),
aiSettings: z.number(),
officeHours: z.number(),
officeContact: z.number(),
@@ -25,6 +25,7 @@ export const UserCreateResultSchema = z.object({
cloudFiles: z.array(z.unknown()),
communications: z.array(z.unknown()),
twilioSettings: z.unknown().optional(),
telnyxSettings: z.unknown().optional(),
aiSettings: z.unknown().optional(),
officeHours: z.unknown().optional(),
officeContact: z.unknown().optional(),
@@ -25,6 +25,7 @@ export const UserDeleteResultSchema = z.nullable(z.object({
cloudFiles: z.array(z.unknown()),
communications: z.array(z.unknown()),
twilioSettings: z.unknown().optional(),
telnyxSettings: z.unknown().optional(),
aiSettings: z.unknown().optional(),
officeHours: z.unknown().optional(),
officeContact: z.unknown().optional(),
@@ -25,6 +25,7 @@ export const UserFindFirstResultSchema = z.nullable(z.object({
cloudFiles: z.array(z.unknown()),
communications: z.array(z.unknown()),
twilioSettings: z.unknown().optional(),
telnyxSettings: z.unknown().optional(),
aiSettings: z.unknown().optional(),
officeHours: z.unknown().optional(),
officeContact: z.unknown().optional(),
@@ -26,6 +26,7 @@ export const UserFindManyResultSchema = z.object({
cloudFiles: z.array(z.unknown()),
communications: z.array(z.unknown()),
twilioSettings: z.unknown().optional(),
telnyxSettings: z.unknown().optional(),
aiSettings: z.unknown().optional(),
officeHours: z.unknown().optional(),
officeContact: z.unknown().optional(),
@@ -25,6 +25,7 @@ export const UserFindUniqueResultSchema = z.nullable(z.object({
cloudFiles: z.array(z.unknown()),
communications: z.array(z.unknown()),
twilioSettings: z.unknown().optional(),
telnyxSettings: z.unknown().optional(),
aiSettings: z.unknown().optional(),
officeHours: z.unknown().optional(),
officeContact: z.unknown().optional(),
@@ -36,6 +36,7 @@ export const UserGroupByResultSchema = z.array(z.object({
cloudFiles: z.number(),
communications: z.number(),
twilioSettings: z.number(),
telnyxSettings: z.number(),
aiSettings: z.number(),
officeHours: z.number(),
officeContact: z.number(),
@@ -25,6 +25,7 @@ export const UserUpdateResultSchema = z.nullable(z.object({
cloudFiles: z.array(z.unknown()),
communications: z.array(z.unknown()),
twilioSettings: z.unknown().optional(),
telnyxSettings: z.unknown().optional(),
aiSettings: z.unknown().optional(),
officeHours: z.unknown().optional(),
officeContact: z.unknown().optional(),
@@ -25,6 +25,7 @@ export const UserUpsertResultSchema = z.object({
cloudFiles: z.array(z.unknown()),
communications: z.array(z.unknown()),
twilioSettings: z.unknown().optional(),
telnyxSettings: z.unknown().optional(),
aiSettings: z.unknown().optional(),
officeHours: z.unknown().optional(),
officeContact: z.unknown().optional(),
@@ -336,6 +336,19 @@ export { TwilioSettingsDeleteManyResultSchema } from './TwilioSettingsDeleteMany
export { TwilioSettingsAggregateResultSchema } from './TwilioSettingsAggregateResult.schema';
export { TwilioSettingsGroupByResultSchema } from './TwilioSettingsGroupByResult.schema';
export { TwilioSettingsCountResultSchema } from './TwilioSettingsCountResult.schema';
export { TelnyxSettingsFindUniqueResultSchema } from './TelnyxSettingsFindUniqueResult.schema';
export { TelnyxSettingsFindFirstResultSchema } from './TelnyxSettingsFindFirstResult.schema';
export { TelnyxSettingsFindManyResultSchema } from './TelnyxSettingsFindManyResult.schema';
export { TelnyxSettingsCreateResultSchema } from './TelnyxSettingsCreateResult.schema';
export { TelnyxSettingsCreateManyResultSchema } from './TelnyxSettingsCreateManyResult.schema';
export { TelnyxSettingsUpdateResultSchema } from './TelnyxSettingsUpdateResult.schema';
export { TelnyxSettingsUpdateManyResultSchema } from './TelnyxSettingsUpdateManyResult.schema';
export { TelnyxSettingsUpsertResultSchema } from './TelnyxSettingsUpsertResult.schema';
export { TelnyxSettingsDeleteResultSchema } from './TelnyxSettingsDeleteResult.schema';
export { TelnyxSettingsDeleteManyResultSchema } from './TelnyxSettingsDeleteManyResult.schema';
export { TelnyxSettingsAggregateResultSchema } from './TelnyxSettingsAggregateResult.schema';
export { TelnyxSettingsGroupByResultSchema } from './TelnyxSettingsGroupByResult.schema';
export { TelnyxSettingsCountResultSchema } from './TelnyxSettingsCountResult.schema';
export { AiSettingsFindUniqueResultSchema } from './AiSettingsFindUniqueResult.schema';
export { AiSettingsFindFirstResultSchema } from './AiSettingsFindFirstResult.schema';
export { AiSettingsFindManyResultSchema } from './AiSettingsFindManyResult.schema';