elevenlabs-webhook-nodejs 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +27 -0
- package/.dockerignore +6 -0
- package/.env.example +43 -0
- package/BEFORE-PRODUCTION.md +32 -0
- package/Dockerfile +23 -0
- package/SYSTEM_OVERVIEW.md +942 -0
- package/SYSTEM_STATUS.md +332 -0
- package/backup_script/main.py +146 -0
- package/baileys/.dockerignore +7 -0
- package/baileys/Dockerfile +13 -0
- package/baileys/README.md +412 -0
- package/baileys/index.js +499 -0
- package/baileys/package-lock.json +2532 -0
- package/baileys/package.json +25 -0
- package/baileys/server.js +96 -0
- package/baileys/src/config.js +55 -0
- package/baileys/src/middleware/api-key.js +16 -0
- package/baileys/src/routes/accounts.js +51 -0
- package/baileys/src/routes/chats.js +103 -0
- package/baileys/src/routes/webhooks.js +34 -0
- package/baileys/src/services/account-store.js +259 -0
- package/baileys/src/services/webhook-dispatcher.js +161 -0
- package/baileys/src/services/worker-manager.js +597 -0
- package/baileys/src/utils/jid.js +79 -0
- package/baileys/src/utils/logger.js +16 -0
- package/baileys/src/utils/use-mongodb-auth-state.js +122 -0
- package/baileys/worker.js +721 -0
- package/dist/app.d.ts +1 -0
- package/dist/app.js +84 -0
- package/dist/app.js.map +1 -0
- package/dist/config/agentPrompts.json +19 -0
- package/dist/config/database.d.ts +1 -0
- package/dist/config/database.js +26 -0
- package/dist/config/database.js.map +1 -0
- package/dist/config/env.d.ts +41 -0
- package/dist/config/env.js +81 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.js +73 -0
- package/dist/config/index.js.map +1 -0
- package/dist/controllers/webhook.controller.d.ts +10 -0
- package/dist/controllers/webhook.controller.js +128 -0
- package/dist/controllers/webhook.controller.js.map +1 -0
- package/dist/errors/index.d.ts +4 -0
- package/dist/errors/index.js +13 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/hooks/webhookValidator.d.ts +2 -0
- package/dist/hooks/webhookValidator.js +47 -0
- package/dist/hooks/webhookValidator.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/001_session-architecture.d.ts +3 -0
- package/dist/migrations/001_session-architecture.js +119 -0
- package/dist/migrations/001_session-architecture.js.map +1 -0
- package/dist/migrations/002_agent-ref.d.ts +3 -0
- package/dist/migrations/002_agent-ref.js +55 -0
- package/dist/migrations/002_agent-ref.js.map +1 -0
- package/dist/migrations/003_shift-schedule.d.ts +3 -0
- package/dist/migrations/003_shift-schedule.js +48 -0
- package/dist/migrations/003_shift-schedule.js.map +1 -0
- package/dist/migrations/004_invert-shift-to-clinic-hours.d.ts +3 -0
- package/dist/migrations/004_invert-shift-to-clinic-hours.js +27 -0
- package/dist/migrations/004_invert-shift-to-clinic-hours.js.map +1 -0
- package/dist/migrations/005_composite-baileys-chat-ids.d.ts +3 -0
- package/dist/migrations/005_composite-baileys-chat-ids.js +47 -0
- package/dist/migrations/005_composite-baileys-chat-ids.js.map +1 -0
- package/dist/migrations/migration.model.d.ts +18 -0
- package/dist/migrations/migration.model.js +45 -0
- package/dist/migrations/migration.model.js.map +1 -0
- package/dist/migrations/migration.routes.d.ts +2 -0
- package/dist/migrations/migration.routes.js +37 -0
- package/dist/migrations/migration.routes.js.map +1 -0
- package/dist/migrations/migration.runner.d.ts +19 -0
- package/dist/migrations/migration.runner.js +74 -0
- package/dist/migrations/migration.runner.js.map +1 -0
- package/dist/models/ConversationClaim.d.ts +13 -0
- package/dist/models/ConversationClaim.js +44 -0
- package/dist/models/ConversationClaim.js.map +1 -0
- package/dist/models/ConversationEvaluation.d.ts +23 -0
- package/dist/models/ConversationEvaluation.js +92 -0
- package/dist/models/ConversationEvaluation.js.map +1 -0
- package/dist/models/Summary.d.ts +37 -0
- package/dist/models/Summary.js +78 -0
- package/dist/models/Summary.js.map +1 -0
- package/dist/models/Transcription.d.ts +34 -0
- package/dist/models/Transcription.js +71 -0
- package/dist/models/Transcription.js.map +1 -0
- package/dist/models/WhatsAppRecipient.d.ts +23 -0
- package/dist/models/WhatsAppRecipient.js +53 -0
- package/dist/models/WhatsAppRecipient.js.map +1 -0
- package/dist/modules/admin/admin.routes.d.ts +2 -0
- package/dist/modules/admin/admin.routes.js +1966 -0
- package/dist/modules/admin/admin.routes.js.map +1 -0
- package/dist/modules/admin/elevenlabs-test-chat.routes.d.ts +2 -0
- package/dist/modules/admin/elevenlabs-test-chat.routes.js +158 -0
- package/dist/modules/admin/elevenlabs-test-chat.routes.js.map +1 -0
- package/dist/modules/admin/whatsapp-test-chat.routes.d.ts +2 -0
- package/dist/modules/admin/whatsapp-test-chat.routes.js +204 -0
- package/dist/modules/admin/whatsapp-test-chat.routes.js.map +1 -0
- package/dist/modules/agents/agent.model.d.ts +38 -0
- package/dist/modules/agents/agent.model.js +92 -0
- package/dist/modules/agents/agent.model.js.map +1 -0
- package/dist/modules/agents/agent.routes.d.ts +2 -0
- package/dist/modules/agents/agent.routes.js +61 -0
- package/dist/modules/agents/agent.routes.js.map +1 -0
- package/dist/modules/appointment-validation/appointment-validation.model.d.ts +76 -0
- package/dist/modules/appointment-validation/appointment-validation.model.js +118 -0
- package/dist/modules/appointment-validation/appointment-validation.model.js.map +1 -0
- package/dist/modules/appointment-validation/appointment-validation.routes.d.ts +2 -0
- package/dist/modules/appointment-validation/appointment-validation.routes.js +202 -0
- package/dist/modules/appointment-validation/appointment-validation.routes.js.map +1 -0
- package/dist/modules/appointment-validation/appointment-validation.service.d.ts +53 -0
- package/dist/modules/appointment-validation/appointment-validation.service.js +827 -0
- package/dist/modules/appointment-validation/appointment-validation.service.js.map +1 -0
- package/dist/modules/auth/auth.model.d.ts +17 -0
- package/dist/modules/auth/auth.model.js +64 -0
- package/dist/modules/auth/auth.model.js.map +1 -0
- package/dist/modules/auth/auth.routes.d.ts +2 -0
- package/dist/modules/auth/auth.routes.js +202 -0
- package/dist/modules/auth/auth.routes.js.map +1 -0
- package/dist/modules/auth/auth.service.d.ts +28 -0
- package/dist/modules/auth/auth.service.js +183 -0
- package/dist/modules/auth/auth.service.js.map +1 -0
- package/dist/modules/auth/refresh-token.model.d.ts +13 -0
- package/dist/modules/auth/refresh-token.model.js +52 -0
- package/dist/modules/auth/refresh-token.model.js.map +1 -0
- package/dist/modules/billing/billing-alert.model.d.ts +16 -0
- package/dist/modules/billing/billing-alert.model.js +47 -0
- package/dist/modules/billing/billing-alert.model.js.map +1 -0
- package/dist/modules/billing/billing-period-snapshot.model.d.ts +35 -0
- package/dist/modules/billing/billing-period-snapshot.model.js +68 -0
- package/dist/modules/billing/billing-period-snapshot.model.js.map +1 -0
- package/dist/modules/billing/billing.model.d.ts +18 -0
- package/dist/modules/billing/billing.model.js +62 -0
- package/dist/modules/billing/billing.model.js.map +1 -0
- package/dist/modules/billing/billing.routes.d.ts +2 -0
- package/dist/modules/billing/billing.routes.js +63 -0
- package/dist/modules/billing/billing.routes.js.map +1 -0
- package/dist/modules/billing/billing.service.d.ts +69 -0
- package/dist/modules/billing/billing.service.js +498 -0
- package/dist/modules/billing/billing.service.js.map +1 -0
- package/dist/modules/billing/payment.model.d.ts +24 -0
- package/dist/modules/billing/payment.model.js +57 -0
- package/dist/modules/billing/payment.model.js.map +1 -0
- package/dist/modules/calls/call.model.d.ts +41 -0
- package/dist/modules/calls/call.model.js +97 -0
- package/dist/modules/calls/call.model.js.map +1 -0
- package/dist/modules/calls/call.routes.d.ts +2 -0
- package/dist/modules/calls/call.routes.js +103 -0
- package/dist/modules/calls/call.routes.js.map +1 -0
- package/dist/modules/campaigns/campaign.model.d.ts +45 -0
- package/dist/modules/campaigns/campaign.model.js +98 -0
- package/dist/modules/campaigns/campaign.model.js.map +1 -0
- package/dist/modules/campaigns/campaign.routes.d.ts +2 -0
- package/dist/modules/campaigns/campaign.routes.js +323 -0
- package/dist/modules/campaigns/campaign.routes.js.map +1 -0
- package/dist/modules/campaigns/campaign.service.d.ts +11 -0
- package/dist/modules/campaigns/campaign.service.js +86 -0
- package/dist/modules/campaigns/campaign.service.js.map +1 -0
- package/dist/modules/google-calendar/google-calendar.routes.d.ts +2 -0
- package/dist/modules/google-calendar/google-calendar.routes.js +32 -0
- package/dist/modules/google-calendar/google-calendar.routes.js.map +1 -0
- package/dist/modules/inbound-call/inbound-call-config.model.d.ts +20 -0
- package/dist/modules/inbound-call/inbound-call-config.model.js +68 -0
- package/dist/modules/inbound-call/inbound-call-config.model.js.map +1 -0
- package/dist/modules/inbound-call/inbound-call.routes.d.ts +2 -0
- package/dist/modules/inbound-call/inbound-call.routes.js +243 -0
- package/dist/modules/inbound-call/inbound-call.routes.js.map +1 -0
- package/dist/modules/leads/lead.model.d.ts +24 -0
- package/dist/modules/leads/lead.model.js +54 -0
- package/dist/modules/leads/lead.model.js.map +1 -0
- package/dist/modules/leads/lead.routes.d.ts +2 -0
- package/dist/modules/leads/lead.routes.js +201 -0
- package/dist/modules/leads/lead.routes.js.map +1 -0
- package/dist/modules/plans/plan.model.d.ts +25 -0
- package/dist/modules/plans/plan.model.js +59 -0
- package/dist/modules/plans/plan.model.js.map +1 -0
- package/dist/modules/surveys/survey.model.d.ts +30 -0
- package/dist/modules/surveys/survey.model.js +75 -0
- package/dist/modules/surveys/survey.model.js.map +1 -0
- package/dist/modules/surveys/survey.routes.d.ts +2 -0
- package/dist/modules/surveys/survey.routes.js +153 -0
- package/dist/modules/surveys/survey.routes.js.map +1 -0
- package/dist/modules/tenants/tenant.model.d.ts +86 -0
- package/dist/modules/tenants/tenant.model.js +127 -0
- package/dist/modules/tenants/tenant.model.js.map +1 -0
- package/dist/modules/tenants/tenant.routes.d.ts +2 -0
- package/dist/modules/tenants/tenant.routes.js +65 -0
- package/dist/modules/tenants/tenant.routes.js.map +1 -0
- package/dist/modules/users/user.routes.d.ts +2 -0
- package/dist/modules/users/user.routes.js +106 -0
- package/dist/modules/users/user.routes.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs-tool.routes.d.ts +20 -0
- package/dist/modules/webhooks/elevenlabs-tool.routes.js +85 -0
- package/dist/modules/webhooks/elevenlabs-tool.routes.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs-tool.service.d.ts +11 -0
- package/dist/modules/webhooks/elevenlabs-tool.service.js +360 -0
- package/dist/modules/webhooks/elevenlabs-tool.service.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs.routes.d.ts +2 -0
- package/dist/modules/webhooks/elevenlabs.routes.js +34 -0
- package/dist/modules/webhooks/elevenlabs.routes.js.map +1 -0
- package/dist/modules/webhooks/elevenlabs.service.d.ts +6 -0
- package/dist/modules/webhooks/elevenlabs.service.js +512 -0
- package/dist/modules/webhooks/elevenlabs.service.js.map +1 -0
- package/dist/modules/webhooks/unipile.routes.d.ts +2 -0
- package/dist/modules/webhooks/unipile.routes.js +780 -0
- package/dist/modules/webhooks/unipile.routes.js.map +1 -0
- package/dist/modules/whatsapp/appointment.model.d.ts +27 -0
- package/dist/modules/whatsapp/appointment.model.js +58 -0
- package/dist/modules/whatsapp/appointment.model.js.map +1 -0
- package/dist/modules/whatsapp/operator-request.model.d.ts +29 -0
- package/dist/modules/whatsapp/operator-request.model.js +65 -0
- package/dist/modules/whatsapp/operator-request.model.js.map +1 -0
- package/dist/modules/whatsapp/stt-usage.model.d.ts +16 -0
- package/dist/modules/whatsapp/stt-usage.model.js +53 -0
- package/dist/modules/whatsapp/stt-usage.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-chat.model.d.ts +23 -0
- package/dist/modules/whatsapp/whatsapp-chat.model.js +55 -0
- package/dist/modules/whatsapp/whatsapp-chat.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-contact-profile.model.d.ts +23 -0
- package/dist/modules/whatsapp/whatsapp-contact-profile.model.js +54 -0
- package/dist/modules/whatsapp/whatsapp-contact-profile.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-message.model.d.ts +30 -0
- package/dist/modules/whatsapp/whatsapp-message.model.js +52 -0
- package/dist/modules/whatsapp/whatsapp-message.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp-session.model.d.ts +33 -0
- package/dist/modules/whatsapp/whatsapp-session.model.js +65 -0
- package/dist/modules/whatsapp/whatsapp-session.model.js.map +1 -0
- package/dist/modules/whatsapp/whatsapp.routes.d.ts +2 -0
- package/dist/modules/whatsapp/whatsapp.routes.js +1237 -0
- package/dist/modules/whatsapp/whatsapp.routes.js.map +1 -0
- package/dist/plugins/cors.d.ts +3 -0
- package/dist/plugins/cors.js +11 -0
- package/dist/plugins/cors.js.map +1 -0
- package/dist/plugins/jwt.d.ts +3 -0
- package/dist/plugins/jwt.js +22 -0
- package/dist/plugins/jwt.js.map +1 -0
- package/dist/plugins/rawBody.d.ts +3 -0
- package/dist/plugins/rawBody.js +16 -0
- package/dist/plugins/rawBody.js.map +1 -0
- package/dist/plugins/rbac.d.ts +5 -0
- package/dist/plugins/rbac.js +29 -0
- package/dist/plugins/rbac.js.map +1 -0
- package/dist/routes/admin.routes.d.ts +2 -0
- package/dist/routes/admin.routes.js +169 -0
- package/dist/routes/admin.routes.js.map +1 -0
- package/dist/routes/health.routes.d.ts +2 -0
- package/dist/routes/health.routes.js +16 -0
- package/dist/routes/health.routes.js.map +1 -0
- package/dist/routes/webhook.routes.d.ts +2 -0
- package/dist/routes/webhook.routes.js +17 -0
- package/dist/routes/webhook.routes.js.map +1 -0
- package/dist/services/ai/base.ai.d.ts +19 -0
- package/dist/services/ai/base.ai.js +120 -0
- package/dist/services/ai/base.ai.js.map +1 -0
- package/dist/services/ai/gemini.service.d.ts +11 -0
- package/dist/services/ai/gemini.service.js +43 -0
- package/dist/services/ai/gemini.service.js.map +1 -0
- package/dist/services/ai/index.d.ts +2 -0
- package/dist/services/ai/index.js +26 -0
- package/dist/services/ai/index.js.map +1 -0
- package/dist/services/ai/openai.service.d.ts +11 -0
- package/dist/services/ai/openai.service.js +50 -0
- package/dist/services/ai/openai.service.js.map +1 -0
- package/dist/services/elevenlabs.service.d.ts +52 -0
- package/dist/services/elevenlabs.service.js +447 -0
- package/dist/services/elevenlabs.service.js.map +1 -0
- package/dist/services/google-calendar.service.d.ts +60 -0
- package/dist/services/google-calendar.service.js +494 -0
- package/dist/services/google-calendar.service.js.map +1 -0
- package/dist/services/inbound-call-schedule.service.d.ts +11 -0
- package/dist/services/inbound-call-schedule.service.js +162 -0
- package/dist/services/inbound-call-schedule.service.js.map +1 -0
- package/dist/services/netgsm.service.d.ts +41 -0
- package/dist/services/netgsm.service.js +89 -0
- package/dist/services/netgsm.service.js.map +1 -0
- package/dist/services/unipile.service.d.ts +41 -0
- package/dist/services/unipile.service.js +149 -0
- package/dist/services/unipile.service.js.map +1 -0
- package/dist/services/whatsapp-agent.service.d.ts +139 -0
- package/dist/services/whatsapp-agent.service.js +2055 -0
- package/dist/services/whatsapp-agent.service.js.map +1 -0
- package/dist/services/whatsapp.service.d.ts +26 -0
- package/dist/services/whatsapp.service.js +206 -0
- package/dist/services/whatsapp.service.js.map +1 -0
- package/dist/templates/index.d.ts +39 -0
- package/dist/templates/index.js +35 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/receptionist.d.ts +2 -0
- package/dist/templates/receptionist.js +39 -0
- package/dist/templates/receptionist.js.map +1 -0
- package/dist/templates/survey.d.ts +2 -0
- package/dist/templates/survey.js +41 -0
- package/dist/templates/survey.js.map +1 -0
- package/dist/types/index.d.ts +173 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.js +105 -0
- package/dist/utils/logger.js.map +1 -0
- package/docker-compose.nestjs.yml +89 -0
- package/docker-compose.yml +78 -0
- package/docs/AI_AGENT_ENHANCEMENT_PLAN.md +164 -0
- package/docs/API.md +1193 -0
- package/docs/API_ENDPOINTS.md +344 -0
- package/docs/ARCHITECTURE.md +305 -0
- package/docs/AUTH_API.md +252 -0
- package/docs/BILLING_SMS_ALERTS.md +94 -0
- package/docs/CHAT_ASSIGNMENT_SYSTEM.md +118 -0
- package/docs/CLIENT_TOOLS_AND_FEATURES.md +337 -0
- package/docs/ELEVENLABS_WEBHOOK_TOOLS.md +644 -0
- package/docs/FRONTEND_CHECKLIST.md +227 -0
- package/docs/IMPLEMENTATION_STATUS.md +470 -0
- package/docs/MIGRATION_GUIDE.md +96 -0
- package/docs/MISSINGS_REPORT.md +507 -0
- package/docs/NESTJS_MIGRATION_REFERENCE.md +5136 -0
- package/docs/PROJECT_DESCRIPTION.md +1038 -0
- package/docs/SCALING.md +148 -0
- package/docs/SESSION_SUMMARY_2026_03_17.md +135 -0
- package/docs/WHATSAPP_AGENT.md +1086 -0
- package/docs/architecture/00-SYSTEM-OVERVIEW.md +318 -0
- package/docs/architecture/01-DATABASE-SCHEMA.md +2564 -0
- package/docs/architecture/02-AUTHENTICATION.md +1040 -0
- package/docs/architecture/03-MULTI-CLINIC.md +742 -0
- package/docs/architecture/04-WHATSAPP-AGENT.md +608 -0
- package/docs/architecture/05-OPERATOR-WORKFLOW.md +444 -0
- package/docs/architecture/06-BAILEYS-MICROSERVICE.md +616 -0
- package/docs/architecture/07-APPOINTMENTS.md +849 -0
- package/docs/architecture/08-VOICE-CALLS.md +470 -0
- package/docs/architecture/09-OUTBOUND-CAMPAIGNS.md +542 -0
- package/docs/architecture/10-CLIENT-TOOLS.md +665 -0
- package/docs/architecture/11-BILLING.md +458 -0
- package/docs/architecture/12-SECURITY.md +216 -0
- package/docs/architecture/13-LOGGING-AUDIT.md +549 -0
- package/docs/architecture/14-META-BUSINESS-API.md +454 -0
- package/docs/architecture/15-AI-MODULE.md +479 -0
- package/docs/architecture/16-BACKGROUND-JOBS.md +469 -0
- package/docs/architecture/17-REALTIME-LIVECHAT.md +447 -0
- package/docs/architecture/18-FILE-STORAGE.md +410 -0
- package/docs/architecture/19-PATIENTS.md +1034 -0
- package/docs/architecture/20-TREATMENTS-AND-PLANS.md +774 -0
- package/docs/architecture/21-BEFORE-AFTER-PHOTOS.md +519 -0
- package/docs/database.md +456 -0
- package/docs/ornek-randevu-onay.csv +3 -0
- package/ecosystem.config.js +16 -0
- package/elevenlabs-convai-api-reference.md +1171 -0
- package/frontend/.dockerignore +2 -0
- package/frontend/Dockerfile +24 -0
- package/frontend/README.md +75 -0
- package/frontend/components.json +25 -0
- package/frontend/eslint.config.js +23 -0
- package/frontend/index.html +13 -0
- package/frontend/nginx.conf +37 -0
- package/frontend/package-lock.json +8709 -0
- package/frontend/package.json +71 -0
- package/frontend/public/favicon.svg +1 -0
- package/frontend/public/icons.svg +24 -0
- package/frontend/src/App.tsx +125 -0
- package/frontend/src/components/error-boundary.tsx +50 -0
- package/frontend/src/components/shared/activity-timeline.tsx +66 -0
- package/frontend/src/components/shared/appointment-calendar.css +80 -0
- package/frontend/src/components/shared/appointment-calendar.tsx +245 -0
- package/frontend/src/components/shared/chat-bubble.tsx +72 -0
- package/frontend/src/components/shared/combobox.tsx +119 -0
- package/frontend/src/components/shared/confirm-dialog.tsx +57 -0
- package/frontend/src/components/shared/data-table-pagination.tsx +97 -0
- package/frontend/src/components/shared/data-table-toolbar.tsx +39 -0
- package/frontend/src/components/shared/empty-state.tsx +27 -0
- package/frontend/src/components/shared/file-upload.tsx +140 -0
- package/frontend/src/components/shared/index.ts +20 -0
- package/frontend/src/components/shared/language-switcher.tsx +29 -0
- package/frontend/src/components/shared/notification-dropdown.tsx +115 -0
- package/frontend/src/components/shared/page-header.tsx +23 -0
- package/frontend/src/components/shared/stat-card.tsx +37 -0
- package/frontend/src/components/shared/status-badge.tsx +70 -0
- package/frontend/src/components/shared/status-dot.tsx +43 -0
- package/frontend/src/components/ui/alert-dialog.tsx +187 -0
- package/frontend/src/components/ui/alert.tsx +76 -0
- package/frontend/src/components/ui/avatar.tsx +109 -0
- package/frontend/src/components/ui/badge.tsx +52 -0
- package/frontend/src/components/ui/breadcrumb.tsx +125 -0
- package/frontend/src/components/ui/button.tsx +60 -0
- package/frontend/src/components/ui/calendar.tsx +219 -0
- package/frontend/src/components/ui/card.tsx +103 -0
- package/frontend/src/components/ui/chart.tsx +371 -0
- package/frontend/src/components/ui/checkbox.tsx +29 -0
- package/frontend/src/components/ui/collapsible.tsx +19 -0
- package/frontend/src/components/ui/command.tsx +194 -0
- package/frontend/src/components/ui/dialog.tsx +158 -0
- package/frontend/src/components/ui/dropdown-menu.tsx +268 -0
- package/frontend/src/components/ui/input-group.tsx +156 -0
- package/frontend/src/components/ui/input.tsx +20 -0
- package/frontend/src/components/ui/label.tsx +20 -0
- package/frontend/src/components/ui/pagination.tsx +130 -0
- package/frontend/src/components/ui/popover.tsx +90 -0
- package/frontend/src/components/ui/progress.tsx +83 -0
- package/frontend/src/components/ui/radio-group.tsx +36 -0
- package/frontend/src/components/ui/scroll-area.tsx +54 -0
- package/frontend/src/components/ui/select.tsx +199 -0
- package/frontend/src/components/ui/separator.tsx +23 -0
- package/frontend/src/components/ui/sheet.tsx +138 -0
- package/frontend/src/components/ui/sidebar.tsx +723 -0
- package/frontend/src/components/ui/skeleton.tsx +13 -0
- package/frontend/src/components/ui/sonner.tsx +47 -0
- package/frontend/src/components/ui/switch.tsx +30 -0
- package/frontend/src/components/ui/table.tsx +114 -0
- package/frontend/src/components/ui/tabs.tsx +82 -0
- package/frontend/src/components/ui/textarea.tsx +18 -0
- package/frontend/src/components/ui/toggle-group.tsx +89 -0
- package/frontend/src/components/ui/toggle.tsx +43 -0
- package/frontend/src/components/ui/tooltip.tsx +64 -0
- package/frontend/src/hooks/use-mobile.ts +19 -0
- package/frontend/src/i18n/index.ts +20 -0
- package/frontend/src/i18n/locales/en.json +786 -0
- package/frontend/src/i18n/locales/tr.json +786 -0
- package/frontend/src/index.css +134 -0
- package/frontend/src/layouts/admin-layout.tsx +111 -0
- package/frontend/src/layouts/app-header.tsx +130 -0
- package/frontend/src/layouts/app-layout.tsx +19 -0
- package/frontend/src/layouts/app-sidebar.tsx +212 -0
- package/frontend/src/layouts/auth-guard.tsx +31 -0
- package/frontend/src/layouts/mobile-sidebar.tsx +120 -0
- package/frontend/src/lib/api.ts +68 -0
- package/frontend/src/lib/hooks/use-appointments.ts +224 -0
- package/frontend/src/lib/hooks/use-availability-blocks.ts +83 -0
- package/frontend/src/lib/hooks/use-chats.ts +236 -0
- package/frontend/src/lib/hooks/use-clinic-detail.ts +171 -0
- package/frontend/src/lib/hooks/use-clinics.ts +37 -0
- package/frontend/src/lib/hooks/use-doctor-calendar.ts +80 -0
- package/frontend/src/lib/hooks/use-examinations.ts +89 -0
- package/frontend/src/lib/hooks/use-medical-history.ts +52 -0
- package/frontend/src/lib/hooks/use-patients.ts +296 -0
- package/frontend/src/lib/hooks/use-photo-sets.ts +118 -0
- package/frontend/src/lib/hooks/use-treatment-plans.ts +152 -0
- package/frontend/src/lib/hooks/use-treatments.ts +125 -0
- package/frontend/src/lib/hooks/use-users.ts +172 -0
- package/frontend/src/lib/utils.ts +6 -0
- package/frontend/src/main.tsx +17 -0
- package/frontend/src/pages/admin/agents/detail.tsx +774 -0
- package/frontend/src/pages/admin/agents/import.tsx +280 -0
- package/frontend/src/pages/admin/agents/index.tsx +245 -0
- package/frontend/src/pages/admin/ai-playground.tsx +543 -0
- package/frontend/src/pages/appointments/create-appointment-dialog.tsx +390 -0
- package/frontend/src/pages/appointments/index.tsx +860 -0
- package/frontend/src/pages/audit/index.tsx +24 -0
- package/frontend/src/pages/auth/login.tsx +194 -0
- package/frontend/src/pages/billing/index.tsx +24 -0
- package/frontend/src/pages/calendar/index.tsx +704 -0
- package/frontend/src/pages/calendar-connections/index.tsx +295 -0
- package/frontend/src/pages/calls/index.tsx +24 -0
- package/frontend/src/pages/campaigns/index.tsx +24 -0
- package/frontend/src/pages/chats/index.tsx +981 -0
- package/frontend/src/pages/clinics/index.tsx +224 -0
- package/frontend/src/pages/clinics/settings.tsx +412 -0
- package/frontend/src/pages/components-showcase.tsx +773 -0
- package/frontend/src/pages/connections/index.tsx +328 -0
- package/frontend/src/pages/dashboard.tsx +50 -0
- package/frontend/src/pages/leads/index.tsx +24 -0
- package/frontend/src/pages/my-schedule/index.tsx +496 -0
- package/frontend/src/pages/patients/create-patient-dialog.tsx +358 -0
- package/frontend/src/pages/patients/detail.tsx +1195 -0
- package/frontend/src/pages/patients/edit-patient-dialog.tsx +387 -0
- package/frontend/src/pages/patients/examinations-tab.tsx +460 -0
- package/frontend/src/pages/patients/index.tsx +381 -0
- package/frontend/src/pages/patients/medical-history-dialog.tsx +207 -0
- package/frontend/src/pages/patients/photo-sets-tab.tsx +616 -0
- package/frontend/src/pages/patients/timeline-tab.tsx +164 -0
- package/frontend/src/pages/patients/treatment-plans-tab.tsx +598 -0
- package/frontend/src/pages/phone-numbers/detail.tsx +427 -0
- package/frontend/src/pages/phone-numbers/index.tsx +455 -0
- package/frontend/src/pages/platform/index.tsx +454 -0
- package/frontend/src/pages/settings/index.tsx +126 -0
- package/frontend/src/pages/treatments/index.tsx +487 -0
- package/frontend/src/pages/users/doctor-profile.tsx +672 -0
- package/frontend/src/pages/users/edit.tsx +329 -0
- package/frontend/src/pages/users/index.tsx +407 -0
- package/frontend/src/pages/validation/index.tsx +24 -0
- package/frontend/src/stores/auth.store.ts +108 -0
- package/frontend/src/stores/ui.store.ts +41 -0
- package/frontend/tsconfig.app.json +32 -0
- package/frontend/tsconfig.json +13 -0
- package/frontend/tsconfig.node.json +26 -0
- package/frontend/vite.config.ts +29 -0
- package/nestjs/.dockerignore +5 -0
- package/nestjs/.env.docker +64 -0
- package/nestjs/.prettierrc +4 -0
- package/nestjs/Dockerfile +36 -0
- package/nestjs/README.md +98 -0
- package/nestjs/eslint.config.mjs +35 -0
- package/nestjs/nest-cli.json +8 -0
- package/nestjs/package-lock.json +13390 -0
- package/nestjs/package.json +114 -0
- package/nestjs/prisma/migrations/20260409161536_add_message_metadata_fields/migration.sql +1746 -0
- package/nestjs/prisma/migrations/20260410140436_add_agent_ai_fields/migration.sql +36 -0
- package/nestjs/prisma/migrations/20260410175519_add_agent_clinic_assignments/migration.sql +21 -0
- package/nestjs/prisma/migrations/20260412094344_make_agent_tenant_optional/migration.sql +2 -0
- package/nestjs/prisma/migrations/20260412110008_add_admin_chat_sessions/migration.sql +47 -0
- package/nestjs/prisma/migrations/migration_lock.toml +3 -0
- package/nestjs/prisma/schema.prisma +1843 -0
- package/nestjs/prisma/seed.ts +375 -0
- package/nestjs/prisma.config.ts +14 -0
- package/nestjs/src/admin/admin.controller.ts +27 -0
- package/nestjs/src/admin/admin.module.ts +16 -0
- package/nestjs/src/admin/admin.service.ts +91 -0
- package/nestjs/src/admin/ai-chat-session.service.ts +454 -0
- package/nestjs/src/admin/ai-test.controller.ts +191 -0
- package/nestjs/src/admin/superadmin.controller.ts +106 -0
- package/nestjs/src/agents/agents.controller.ts +262 -0
- package/nestjs/src/agents/agents.module.ts +13 -0
- package/nestjs/src/agents/agents.service.ts +733 -0
- package/nestjs/src/agents/dto/create-agent.dto.ts +99 -0
- package/nestjs/src/agents/dto/index.ts +2 -0
- package/nestjs/src/agents/dto/update-agent.dto.ts +148 -0
- package/nestjs/src/app.module.ts +115 -0
- package/nestjs/src/appointment-validation/appointment-validation.controller.ts +194 -0
- package/nestjs/src/appointment-validation/appointment-validation.module.ts +16 -0
- package/nestjs/src/appointment-validation/appointment-validation.service.ts +450 -0
- package/nestjs/src/appointment-validation/dto/create-batch.dto.ts +105 -0
- package/nestjs/src/appointment-validation/dto/index.ts +1 -0
- package/nestjs/src/appointment-validation/processors/validation-dispatch.processor.ts +26 -0
- package/nestjs/src/appointment-validation/processors/validation-sync.processor.ts +23 -0
- package/nestjs/src/appointments/appointments.controller.ts +268 -0
- package/nestjs/src/appointments/appointments.module.ts +13 -0
- package/nestjs/src/appointments/appointments.service.ts +773 -0
- package/nestjs/src/appointments/dto/create-appointment.dto.ts +72 -0
- package/nestjs/src/appointments/dto/index.ts +4 -0
- package/nestjs/src/appointments/dto/query-appointments.dto.ts +60 -0
- package/nestjs/src/appointments/dto/update-appointment.dto.ts +43 -0
- package/nestjs/src/appointments/dto/update-status.dto.ts +18 -0
- package/nestjs/src/appointments/processors/reminder.processor.ts +243 -0
- package/nestjs/src/audit/audit.controller.ts +84 -0
- package/nestjs/src/audit/audit.decorator.ts +20 -0
- package/nestjs/src/audit/audit.interceptor.ts +67 -0
- package/nestjs/src/audit/audit.interfaces.ts +15 -0
- package/nestjs/src/audit/audit.module.ts +12 -0
- package/nestjs/src/audit/audit.service.ts +177 -0
- package/nestjs/src/auth/auth.controller.ts +116 -0
- package/nestjs/src/auth/auth.module.ts +25 -0
- package/nestjs/src/auth/auth.service.ts +612 -0
- package/nestjs/src/auth/dto/change-password.dto.ts +13 -0
- package/nestjs/src/auth/dto/forgot-password.dto.ts +8 -0
- package/nestjs/src/auth/dto/index.ts +6 -0
- package/nestjs/src/auth/dto/login.dto.ts +13 -0
- package/nestjs/src/auth/dto/refresh.dto.ts +8 -0
- package/nestjs/src/auth/dto/register.dto.ts +28 -0
- package/nestjs/src/auth/dto/reset-password.dto.ts +13 -0
- package/nestjs/src/auth/permissions.config.ts +85 -0
- package/nestjs/src/availability-blocks/availability-blocks.controller.ts +83 -0
- package/nestjs/src/availability-blocks/availability-blocks.module.ts +10 -0
- package/nestjs/src/availability-blocks/availability-blocks.service.ts +202 -0
- package/nestjs/src/billing/billing.controller.ts +104 -0
- package/nestjs/src/billing/billing.module.ts +12 -0
- package/nestjs/src/billing/billing.service.ts +398 -0
- package/nestjs/src/billing/dto/index.ts +2 -0
- package/nestjs/src/billing/dto/query-billing.dto.ts +29 -0
- package/nestjs/src/billing/dto/record-payment.dto.ts +60 -0
- package/nestjs/src/billing/processors/alerts.processor.ts +216 -0
- package/nestjs/src/billing/processors/snapshot.processor.ts +181 -0
- package/nestjs/src/calls/calls.controller.ts +92 -0
- package/nestjs/src/calls/calls.module.ts +10 -0
- package/nestjs/src/calls/calls.service.ts +359 -0
- package/nestjs/src/calls/dto/index.ts +1 -0
- package/nestjs/src/calls/dto/query-calls.dto.ts +46 -0
- package/nestjs/src/clinics/clinics.controller.ts +226 -0
- package/nestjs/src/clinics/clinics.module.ts +11 -0
- package/nestjs/src/clinics/clinics.service.ts +203 -0
- package/nestjs/src/clinics/dto/create-clinic.dto.ts +40 -0
- package/nestjs/src/clinics/dto/create-meta-connection.dto.ts +44 -0
- package/nestjs/src/clinics/dto/index.ts +4 -0
- package/nestjs/src/clinics/dto/update-clinic.dto.ts +133 -0
- package/nestjs/src/clinics/dto/update-meta-connection.dto.ts +22 -0
- package/nestjs/src/clinics/meta-connections.service.ts +210 -0
- package/nestjs/src/common/decorators/accessible-clinics.decorator.ts +9 -0
- package/nestjs/src/common/decorators/current-user.decorator.ts +10 -0
- package/nestjs/src/common/decorators/features.decorator.ts +7 -0
- package/nestjs/src/common/decorators/index.ts +4 -0
- package/nestjs/src/common/decorators/permissions.decorator.ts +13 -0
- package/nestjs/src/common/gateways/events.gateway.ts +157 -0
- package/nestjs/src/common/guards/clinic-access.guard.ts +40 -0
- package/nestjs/src/common/guards/features.guard.ts +38 -0
- package/nestjs/src/common/guards/index.ts +5 -0
- package/nestjs/src/common/guards/jwt-auth.guard.ts +54 -0
- package/nestjs/src/common/guards/permissions.guard.ts +50 -0
- package/nestjs/src/common/guards/superadmin.guard.ts +35 -0
- package/nestjs/src/common/interceptors/request-context.interceptor.ts +32 -0
- package/nestjs/src/common/interceptors/superadmin-tenant.interceptor.ts +42 -0
- package/nestjs/src/common/interfaces/jwt-payload.interface.ts +11 -0
- package/nestjs/src/config/config.module.ts +13 -0
- package/nestjs/src/database/database.module.ts +9 -0
- package/nestjs/src/database/prisma.service.ts +20 -0
- package/nestjs/src/database/tenant-context.ts +27 -0
- package/nestjs/src/google-calendar/google-calendar.controller.ts +259 -0
- package/nestjs/src/google-calendar/google-calendar.module.ts +10 -0
- package/nestjs/src/google-calendar/google-calendar.service.ts +811 -0
- package/nestjs/src/integrations/ai/ai.interface.ts +74 -0
- package/nestjs/src/integrations/ai/ai.module.ts +11 -0
- package/nestjs/src/integrations/ai/ai.service.ts +148 -0
- package/nestjs/src/integrations/ai/providers/anthropic.provider.ts +146 -0
- package/nestjs/src/integrations/ai/providers/openai.provider.ts +158 -0
- package/nestjs/src/integrations/elevenlabs/elevenlabs.module.ts +8 -0
- package/nestjs/src/integrations/elevenlabs/elevenlabs.service.ts +226 -0
- package/nestjs/src/integrations/encryption/encryption.module.ts +9 -0
- package/nestjs/src/integrations/encryption/encryption.service.ts +31 -0
- package/nestjs/src/integrations/image/image.module.ts +9 -0
- package/nestjs/src/integrations/image/image.service.ts +61 -0
- package/nestjs/src/integrations/meta-business/meta-business.module.ts +10 -0
- package/nestjs/src/integrations/meta-business/meta-instagram.service.ts +94 -0
- package/nestjs/src/integrations/meta-business/meta-webhook.service.ts +52 -0
- package/nestjs/src/integrations/meta-business/meta-whatsapp.service.ts +254 -0
- package/nestjs/src/integrations/minio/minio.module.ts +9 -0
- package/nestjs/src/integrations/minio/minio.service.ts +88 -0
- package/nestjs/src/integrations/netgsm/netgsm.module.ts +8 -0
- package/nestjs/src/integrations/netgsm/netgsm.service.ts +17 -0
- package/nestjs/src/integrations/tektippay/tektippay.module.ts +8 -0
- package/nestjs/src/integrations/tektippay/tektippay.service.ts +23 -0
- package/nestjs/src/leads/dto/index.ts +2 -0
- package/nestjs/src/leads/dto/query-leads.dto.ts +50 -0
- package/nestjs/src/leads/dto/update-lead.dto.ts +26 -0
- package/nestjs/src/leads/leads.controller.ts +184 -0
- package/nestjs/src/leads/leads.module.ts +10 -0
- package/nestjs/src/leads/leads.service.ts +375 -0
- package/nestjs/src/logging/logging.controller.ts +82 -0
- package/nestjs/src/logging/logging.module.ts +11 -0
- package/nestjs/src/logging/logging.service.ts +180 -0
- package/nestjs/src/main.ts +86 -0
- package/nestjs/src/outbound-campaigns/dto/create-campaign.dto.ts +47 -0
- package/nestjs/src/outbound-campaigns/dto/index.ts +3 -0
- package/nestjs/src/outbound-campaigns/dto/update-campaign.dto.ts +31 -0
- package/nestjs/src/outbound-campaigns/dto/upload-entries.dto.ts +35 -0
- package/nestjs/src/outbound-campaigns/outbound-campaigns.controller.ts +307 -0
- package/nestjs/src/outbound-campaigns/outbound-campaigns.module.ts +13 -0
- package/nestjs/src/outbound-campaigns/outbound-campaigns.service.ts +471 -0
- package/nestjs/src/outbound-campaigns/processors/campaign-dispatch.processor.ts +366 -0
- package/nestjs/src/patients/documents.service.ts +231 -0
- package/nestjs/src/patients/dto/create-examination.dto.ts +34 -0
- package/nestjs/src/patients/dto/create-note.dto.ts +14 -0
- package/nestjs/src/patients/dto/create-patient.dto.ts +86 -0
- package/nestjs/src/patients/dto/create-photo-set.dto.ts +32 -0
- package/nestjs/src/patients/dto/index.ts +10 -0
- package/nestjs/src/patients/dto/update-examination.dto.ts +29 -0
- package/nestjs/src/patients/dto/update-medical-history.dto.ts +47 -0
- package/nestjs/src/patients/dto/update-patient.dto.ts +87 -0
- package/nestjs/src/patients/dto/update-photo-set.dto.ts +28 -0
- package/nestjs/src/patients/dto/upload-document.dto.ts +31 -0
- package/nestjs/src/patients/dto/upload-photo.dto.ts +27 -0
- package/nestjs/src/patients/examinations.service.ts +271 -0
- package/nestjs/src/patients/medical-history.service.ts +149 -0
- package/nestjs/src/patients/notes.service.ts +172 -0
- package/nestjs/src/patients/patients.controller.ts +485 -0
- package/nestjs/src/patients/patients.module.ts +22 -0
- package/nestjs/src/patients/patients.service.ts +412 -0
- package/nestjs/src/patients/photo-sets.service.ts +389 -0
- package/nestjs/src/phone-numbers/dto/create-phone-number.dto.ts +57 -0
- package/nestjs/src/phone-numbers/dto/index.ts +3 -0
- package/nestjs/src/phone-numbers/dto/update-phone-number.dto.ts +38 -0
- package/nestjs/src/phone-numbers/dto/update-schedule.dto.ts +39 -0
- package/nestjs/src/phone-numbers/phone-numbers.controller.ts +125 -0
- package/nestjs/src/phone-numbers/phone-numbers.module.ts +10 -0
- package/nestjs/src/phone-numbers/phone-numbers.service.ts +209 -0
- package/nestjs/src/plans/dto/create-plan.dto.ts +70 -0
- package/nestjs/src/plans/dto/index.ts +2 -0
- package/nestjs/src/plans/dto/update-plan.dto.ts +80 -0
- package/nestjs/src/plans/plans.controller.ts +50 -0
- package/nestjs/src/plans/plans.module.ts +10 -0
- package/nestjs/src/plans/plans.service.ts +115 -0
- package/nestjs/src/platform/dto/create-tenant.dto.ts +36 -0
- package/nestjs/src/platform/dto/index.ts +2 -0
- package/nestjs/src/platform/dto/update-tenant-platform.dto.ts +44 -0
- package/nestjs/src/platform/platform.controller.ts +79 -0
- package/nestjs/src/platform/platform.module.ts +10 -0
- package/nestjs/src/platform/platform.service.ts +301 -0
- package/nestjs/src/queue/queue.module.ts +56 -0
- package/nestjs/src/redis/redis.module.ts +20 -0
- package/nestjs/src/tenants/dto/index.ts +1 -0
- package/nestjs/src/tenants/dto/update-tenant.dto.ts +15 -0
- package/nestjs/src/tenants/tenants.controller.ts +45 -0
- package/nestjs/src/tenants/tenants.module.ts +10 -0
- package/nestjs/src/tenants/tenants.service.ts +41 -0
- package/nestjs/src/tools/adapters/elevenlabs-http.adapter.ts +51 -0
- package/nestjs/src/tools/adapters/elevenlabs-ws.adapter.ts +59 -0
- package/nestjs/src/tools/handlers/calendar.tools.ts +441 -0
- package/nestjs/src/tools/handlers/notification.tools.ts +34 -0
- package/nestjs/src/tools/handlers/operator.tools.ts +303 -0
- package/nestjs/src/tools/handlers/patient.tools.ts +242 -0
- package/nestjs/src/tools/handlers/payment.tools.ts +43 -0
- package/nestjs/src/tools/handlers/validation.tools.ts +152 -0
- package/nestjs/src/tools/tool-registry.service.ts +221 -0
- package/nestjs/src/tools/tool.decorator.ts +16 -0
- package/nestjs/src/tools/tool.interfaces.ts +26 -0
- package/nestjs/src/tools/tools.module.ts +50 -0
- package/nestjs/src/treatments/dto/create-plan-item.dto.ts +27 -0
- package/nestjs/src/treatments/dto/create-treatment-plan.dto.ts +69 -0
- package/nestjs/src/treatments/dto/create-treatment.dto.ts +59 -0
- package/nestjs/src/treatments/dto/index.ts +6 -0
- package/nestjs/src/treatments/dto/update-plan-item.dto.ts +23 -0
- package/nestjs/src/treatments/dto/update-treatment-plan.dto.ts +22 -0
- package/nestjs/src/treatments/dto/update-treatment.dto.ts +70 -0
- package/nestjs/src/treatments/treatment-plans.service.ts +362 -0
- package/nestjs/src/treatments/treatments.controller.ts +265 -0
- package/nestjs/src/treatments/treatments.module.ts +14 -0
- package/nestjs/src/treatments/treatments.service.ts +165 -0
- package/nestjs/src/users/doctor-profiles.service.ts +202 -0
- package/nestjs/src/users/dto/index.ts +4 -0
- package/nestjs/src/users/dto/invite-user.dto.ts +52 -0
- package/nestjs/src/users/dto/update-clinic-assignments.dto.ts +9 -0
- package/nestjs/src/users/dto/update-doctor-profile.dto.ts +49 -0
- package/nestjs/src/users/dto/update-user.dto.ts +41 -0
- package/nestjs/src/users/users.controller.ts +142 -0
- package/nestjs/src/users/users.module.ts +11 -0
- package/nestjs/src/users/users.service.ts +250 -0
- package/nestjs/src/webhooks/elevenlabs-tool.controller.ts +66 -0
- package/nestjs/src/webhooks/elevenlabs-webhook.controller.ts +60 -0
- package/nestjs/src/webhooks/meta-webhook.controller.ts +178 -0
- package/nestjs/src/webhooks/processors/elevenlabs-webhook.processor.ts +28 -0
- package/nestjs/src/webhooks/webhooks.module.ts +17 -0
- package/nestjs/src/whatsapp/chat-context.service.ts +281 -0
- package/nestjs/src/whatsapp/dto/add-blacklist.dto.ts +13 -0
- package/nestjs/src/whatsapp/dto/index.ts +2 -0
- package/nestjs/src/whatsapp/dto/send-message.dto.ts +14 -0
- package/nestjs/src/whatsapp/listeners/meta-message.listener.ts +579 -0
- package/nestjs/src/whatsapp/meta-auth.controller.ts +268 -0
- package/nestjs/src/whatsapp/meta-instagram-auth.controller.ts +244 -0
- package/nestjs/src/whatsapp/operator.service.ts +692 -0
- package/nestjs/src/whatsapp/processors/cost-sweep.processor.ts +130 -0
- package/nestjs/src/whatsapp/processors/grace.processor.ts +191 -0
- package/nestjs/src/whatsapp/processors/message-buffer.processor.ts +138 -0
- package/nestjs/src/whatsapp/processors/message-cleanup.processor.ts +148 -0
- package/nestjs/src/whatsapp/processors/meta-token-refresh.processor.ts +114 -0
- package/nestjs/src/whatsapp/processors/operator-expiry.processor.ts +105 -0
- package/nestjs/src/whatsapp/processors/profile-update.processor.ts +234 -0
- package/nestjs/src/whatsapp/processors/session-cleanup.processor.ts +178 -0
- package/nestjs/src/whatsapp/processors/session-labels.processor.ts +248 -0
- package/nestjs/src/whatsapp/whatsapp-agent.service.ts +2506 -0
- package/nestjs/src/whatsapp/whatsapp-recovery.service.ts +117 -0
- package/nestjs/src/whatsapp/whatsapp.controller.ts +398 -0
- package/nestjs/src/whatsapp/whatsapp.module.ts +51 -0
- package/nestjs/src/whatsapp/whatsapp.service.ts +592 -0
- package/nestjs/test/app.e2e-spec.ts +25 -0
- package/nestjs/test/jest-e2e.json +9 -0
- package/nestjs/tsconfig.build.json +4 -0
- package/nestjs/tsconfig.json +25 -0
- package/nginx.example.conf +18 -0
- package/package.json +47 -0
- package/scripts/addRecipient.ts +48 -0
- package/scripts/listRecipients.ts +31 -0
- package/scripts/migrate-agent-ref.ts +86 -0
- package/scripts/migrate-sessions.ts +183 -0
- package/scripts/promote.ts +27 -0
- package/scripts/retrigger.ts +84 -0
- package/scripts/seed.ts +435 -0
- package/scripts/testSend.ts +63 -0
- package/src/app.ts +85 -0
- package/src/config/agentPrompts.json +19 -0
- package/src/config/database.ts +21 -0
- package/src/config/env.ts +94 -0
- package/src/config/index.ts +86 -0
- package/src/controllers/webhook.controller.ts +150 -0
- package/src/errors/index.ts +9 -0
- package/src/hooks/webhookValidator.ts +55 -0
- package/src/index.ts +68 -0
- package/src/migrations/001_session-architecture.ts +138 -0
- package/src/migrations/002_agent-ref.ts +65 -0
- package/src/migrations/003_shift-schedule.ts +55 -0
- package/src/migrations/004_invert-shift-to-clinic-hours.ts +30 -0
- package/src/migrations/005_composite-baileys-chat-ids.ts +60 -0
- package/src/migrations/migration.model.ts +27 -0
- package/src/migrations/migration.routes.ts +40 -0
- package/src/migrations/migration.runner.ts +112 -0
- package/src/models/ConversationClaim.ts +17 -0
- package/src/models/ConversationEvaluation.ts +91 -0
- package/src/models/Summary.ts +77 -0
- package/src/models/Transcription.ts +68 -0
- package/src/models/WhatsAppRecipient.ts +37 -0
- package/src/modules/admin/admin.routes.ts +2385 -0
- package/src/modules/admin/elevenlabs-test-chat.routes.ts +193 -0
- package/src/modules/admin/whatsapp-test-chat.routes.ts +244 -0
- package/src/modules/agents/agent.model.ts +93 -0
- package/src/modules/agents/agent.routes.ts +65 -0
- package/src/modules/appointment-validation/appointment-validation.model.ts +163 -0
- package/src/modules/appointment-validation/appointment-validation.routes.ts +275 -0
- package/src/modules/appointment-validation/appointment-validation.service.ts +1028 -0
- package/src/modules/auth/auth.model.ts +42 -0
- package/src/modules/auth/auth.routes.ts +199 -0
- package/src/modules/auth/auth.service.ts +210 -0
- package/src/modules/auth/refresh-token.model.ts +26 -0
- package/src/modules/billing/billing-alert.model.ts +28 -0
- package/src/modules/billing/billing-period-snapshot.model.ts +68 -0
- package/src/modules/billing/billing.model.ts +42 -0
- package/src/modules/billing/billing.routes.ts +67 -0
- package/src/modules/billing/billing.service.ts +562 -0
- package/src/modules/billing/payment.model.ts +42 -0
- package/src/modules/calls/call.model.ts +102 -0
- package/src/modules/calls/call.routes.ts +118 -0
- package/src/modules/campaigns/campaign.model.ts +111 -0
- package/src/modules/campaigns/campaign.routes.ts +402 -0
- package/src/modules/campaigns/campaign.service.ts +99 -0
- package/src/modules/google-calendar/google-calendar.routes.ts +31 -0
- package/src/modules/inbound-call/inbound-call-config.model.ts +49 -0
- package/src/modules/inbound-call/inbound-call.routes.ts +289 -0
- package/src/modules/leads/lead.model.ts +40 -0
- package/src/modules/leads/lead.routes.ts +246 -0
- package/src/modules/logs/log.model.ts +27 -0
- package/src/modules/logs/log.routes.ts +102 -0
- package/src/modules/plans/plan.model.ts +45 -0
- package/src/modules/surveys/survey.model.ts +70 -0
- package/src/modules/surveys/survey.routes.ts +187 -0
- package/src/modules/tenants/tenant.model.ts +181 -0
- package/src/modules/tenants/tenant.routes.ts +78 -0
- package/src/modules/users/user.routes.ts +126 -0
- package/src/modules/webhooks/elevenlabs-tool.routes.ts +94 -0
- package/src/modules/webhooks/elevenlabs-tool.service.ts +491 -0
- package/src/modules/webhooks/elevenlabs.routes.ts +34 -0
- package/src/modules/webhooks/elevenlabs.service.ts +565 -0
- package/src/modules/webhooks/unipile.routes.ts +917 -0
- package/src/modules/whatsapp/appointment.model.ts +47 -0
- package/src/modules/whatsapp/operator-request.model.ts +58 -0
- package/src/modules/whatsapp/stt-usage.model.ts +30 -0
- package/src/modules/whatsapp/whatsapp-chat.model.ts +39 -0
- package/src/modules/whatsapp/whatsapp-contact-profile.model.ts +41 -0
- package/src/modules/whatsapp/whatsapp-message.model.ts +41 -0
- package/src/modules/whatsapp/whatsapp-session.model.ts +60 -0
- package/src/modules/whatsapp/whatsapp.routes.ts +1435 -0
- package/src/plugins/cors.ts +7 -0
- package/src/plugins/jwt.ts +18 -0
- package/src/plugins/rawBody.ts +12 -0
- package/src/plugins/rbac.ts +24 -0
- package/src/routes/admin.routes.ts +208 -0
- package/src/routes/health.routes.ts +12 -0
- package/src/routes/webhook.routes.ts +12 -0
- package/src/services/ai/base.ai.ts +132 -0
- package/src/services/ai/gemini.service.ts +41 -0
- package/src/services/ai/index.ts +24 -0
- package/src/services/ai/openai.service.ts +48 -0
- package/src/services/elevenlabs.service.ts +532 -0
- package/src/services/google-calendar.service.ts +656 -0
- package/src/services/inbound-call-schedule.service.ts +174 -0
- package/src/services/netgsm.service.ts +128 -0
- package/src/services/unipile.service.ts +200 -0
- package/src/services/whatsapp-agent.service.ts +2479 -0
- package/src/services/whatsapp.service.ts +245 -0
- package/src/templates/index.ts +71 -0
- package/src/templates/receptionist.ts +44 -0
- package/src/templates/survey.ts +44 -0
- package/src/types/index.ts +218 -0
- package/src/utils/logger.ts +83 -0
- package/tsconfig.json +19 -0
- package/web/.dockerignore +4 -0
- package/web/Dockerfile +18 -0
- package/web/README.md +73 -0
- package/web/components.json +23 -0
- package/web/eslint.config.js +23 -0
- package/web/index.html +14 -0
- package/web/nginx.conf +18 -0
- package/web/package-lock.json +10292 -0
- package/web/package.json +48 -0
- package/web/public/favicon.ico +0 -0
- package/web/public/vite.svg +1 -0
- package/web/src/App.tsx +191 -0
- package/web/src/assets/react.svg +1 -0
- package/web/src/components/Layout.tsx +261 -0
- package/web/src/components/LeadConversation.tsx +251 -0
- package/web/src/components/ProtectedRoute.tsx +20 -0
- package/web/src/components/conversation-review/CardAudioPlayer.tsx +200 -0
- package/web/src/components/conversation-review/CardEvaluation.tsx +351 -0
- package/web/src/components/conversation-review/CardMetadata.tsx +44 -0
- package/web/src/components/conversation-review/ConversationCard.tsx +95 -0
- package/web/src/components/conversation-review/ReviewContainer.tsx +120 -0
- package/web/src/components/conversation-review/ReviewFilters.tsx +88 -0
- package/web/src/components/empty-state.tsx +15 -0
- package/web/src/components/page-header.tsx +19 -0
- package/web/src/components/page-loader.tsx +32 -0
- package/web/src/components/pagination.tsx +38 -0
- package/web/src/components/stat-card.tsx +27 -0
- package/web/src/components/status-badge.tsx +125 -0
- package/web/src/components/ui/alert.tsx +66 -0
- package/web/src/components/ui/badge.tsx +48 -0
- package/web/src/components/ui/button.tsx +64 -0
- package/web/src/components/ui/card.tsx +92 -0
- package/web/src/components/ui/checkbox.tsx +32 -0
- package/web/src/components/ui/dialog.tsx +158 -0
- package/web/src/components/ui/dropdown-menu.tsx +255 -0
- package/web/src/components/ui/input.tsx +21 -0
- package/web/src/components/ui/label.tsx +22 -0
- package/web/src/components/ui/progress.tsx +29 -0
- package/web/src/components/ui/select.tsx +188 -0
- package/web/src/components/ui/separator.tsx +28 -0
- package/web/src/components/ui/sheet.tsx +123 -0
- package/web/src/components/ui/skeleton.tsx +13 -0
- package/web/src/components/ui/sonner.tsx +35 -0
- package/web/src/components/ui/table.tsx +116 -0
- package/web/src/components/ui/tabs.tsx +89 -0
- package/web/src/components/ui/textarea.tsx +18 -0
- package/web/src/components/ui/tooltip.tsx +57 -0
- package/web/src/components/whatsapp/ChatDetail.tsx +417 -0
- package/web/src/components/whatsapp/ChatHeader.tsx +78 -0
- package/web/src/components/whatsapp/ChatList.tsx +107 -0
- package/web/src/components/whatsapp/ChatListItem.tsx +60 -0
- package/web/src/components/whatsapp/MessageBubble.tsx +46 -0
- package/web/src/components/whatsapp/MessageInput.tsx +63 -0
- package/web/src/components/whatsapp/MessageStream.tsx +135 -0
- package/web/src/components/whatsapp/SessionDivider.tsx +65 -0
- package/web/src/components/whatsapp/SessionHistory.tsx +119 -0
- package/web/src/components/whatsapp/settings/AiSettingsTab.tsx +268 -0
- package/web/src/components/whatsapp/settings/CalendarTab.tsx +339 -0
- package/web/src/components/whatsapp/settings/ConnectionTab.tsx +236 -0
- package/web/src/components/whatsapp/settings/NotificationsTab.tsx +109 -0
- package/web/src/components/whatsapp/settings/OperatorTab.tsx +303 -0
- package/web/src/contexts/AuthContext.tsx +103 -0
- package/web/src/index.css +130 -0
- package/web/src/lib/api.ts +92 -0
- package/web/src/lib/utils.ts +6 -0
- package/web/src/main.tsx +10 -0
- package/web/src/pages/AppointmentDetail.tsx +206 -0
- package/web/src/pages/AppointmentValidation.tsx +157 -0
- package/web/src/pages/AppointmentValidationDetail.tsx +617 -0
- package/web/src/pages/AppointmentValidationNew.tsx +1005 -0
- package/web/src/pages/Appointments.tsx +283 -0
- package/web/src/pages/Billing.tsx +126 -0
- package/web/src/pages/CallDetail.tsx +293 -0
- package/web/src/pages/CallSettings.tsx +313 -0
- package/web/src/pages/Calls.tsx +188 -0
- package/web/src/pages/CampaignDetail.tsx +216 -0
- package/web/src/pages/CampaignNew.tsx +277 -0
- package/web/src/pages/CampaignResults.tsx +185 -0
- package/web/src/pages/Campaigns.tsx +171 -0
- package/web/src/pages/Dashboard.tsx +336 -0
- package/web/src/pages/InboundSchedule.tsx +246 -0
- package/web/src/pages/LeadDetail.tsx +183 -0
- package/web/src/pages/Leads.tsx +258 -0
- package/web/src/pages/Login.tsx +99 -0
- package/web/src/pages/Register.tsx +129 -0
- package/web/src/pages/Settings.tsx +133 -0
- package/web/src/pages/SurveyDetail.tsx +232 -0
- package/web/src/pages/SurveyPreview.tsx +179 -0
- package/web/src/pages/Surveys.tsx +207 -0
- package/web/src/pages/Users.tsx +199 -0
- package/web/src/pages/WhatsApp.tsx +147 -0
- package/web/src/pages/WhatsAppSettings.tsx +215 -0
- package/web/src/pages/admin/AdminBaileysMessageLog.tsx +331 -0
- package/web/src/pages/admin/AdminBaileysRawMessages.tsx +318 -0
- package/web/src/pages/admin/AdminConversationReview.tsx +116 -0
- package/web/src/pages/admin/AdminCredits.tsx +467 -0
- package/web/src/pages/admin/AdminElevenLabsAgentDetail.tsx +332 -0
- package/web/src/pages/admin/AdminElevenLabsAgents.tsx +164 -0
- package/web/src/pages/admin/AdminElevenLabsBatchCallDetail.tsx +214 -0
- package/web/src/pages/admin/AdminElevenLabsBatchCalls.tsx +293 -0
- package/web/src/pages/admin/AdminElevenLabsConversationDetail.tsx +230 -0
- package/web/src/pages/admin/AdminElevenLabsConversations.tsx +228 -0
- package/web/src/pages/admin/AdminElevenLabsInboundCalls.tsx +293 -0
- package/web/src/pages/admin/AdminElevenLabsPhoneNumbers.tsx +506 -0
- package/web/src/pages/admin/AdminElevenLabsTestChat.tsx +258 -0
- package/web/src/pages/admin/AdminElevenLabsWebhooks.tsx +289 -0
- package/web/src/pages/admin/AdminEvaluationDashboard.tsx +520 -0
- package/web/src/pages/admin/AdminLeads.tsx +339 -0
- package/web/src/pages/admin/AdminLogs.tsx +283 -0
- package/web/src/pages/admin/AdminMigrations.tsx +247 -0
- package/web/src/pages/admin/AdminPlans.tsx +313 -0
- package/web/src/pages/admin/AdminSystem.tsx +391 -0
- package/web/src/pages/admin/AdminTenantBillableItems.tsx +464 -0
- package/web/src/pages/admin/AdminTenantDetail.tsx +1317 -0
- package/web/src/pages/admin/AdminTenants.tsx +274 -0
- package/web/src/pages/admin/AdminWhatsApp.tsx +618 -0
- package/web/src/pages/admin/AdminWhatsAppTestChat.tsx +328 -0
- package/web/src/types/index.ts +242 -0
- package/web/tsconfig.app.json +32 -0
- package/web/tsconfig.json +13 -0
- package/web/tsconfig.node.json +26 -0
- package/web/vite.config.ts +23 -0
|
@@ -0,0 +1,742 @@
|
|
|
1
|
+
# 03 — Multi-Clinic & Multi-Phone Architecture
|
|
2
|
+
|
|
3
|
+
> Last updated: 2026-04-02
|
|
4
|
+
|
|
5
|
+
> Covers the Clinic model, PhoneNumber model, clinic connections (Calendar, Meta),
|
|
6
|
+
> phone-to-clinic-to-agent routing, and per-clinic self-contained configuration.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
|
|
12
|
+
1. [Overview](#1-overview)
|
|
13
|
+
2. [Clinic Model](#2-clinic-model)
|
|
14
|
+
3. [PhoneNumber Model](#3-phonenumber-model)
|
|
15
|
+
4. [Clinic Connections](#4-clinic-connections)
|
|
16
|
+
5. [Routing Flow](#5-routing-flow)
|
|
17
|
+
6. [Clinic Configuration](#6-clinic-configuration)
|
|
18
|
+
7. [Examples](#7-examples)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 1. Overview
|
|
23
|
+
|
|
24
|
+
The system supports two operating modes through the same data model:
|
|
25
|
+
|
|
26
|
+
- **Single-clinic tenants** -- one tenant, one clinic (auto-created on registration as the default clinic). Everything works transparently with no clinic selection required. This is the common case for a solo practice.
|
|
27
|
+
- **Multi-clinic tenants** -- one tenant, many clinics (branches/locations). Each clinic has its own phone numbers, calendar, operator, and agent configuration. Incoming messages and calls are routed to the correct clinic based on which phone number the patient contacted.
|
|
28
|
+
|
|
29
|
+
The architecture uses two layers:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
Tenant (1)
|
|
33
|
+
|
|
|
34
|
+
+-- Clinic (1..N)
|
|
35
|
+
|
|
|
36
|
+
+-- PhoneNumber (0..N) -- WhatsApp, inbound calls, outbound calls
|
|
37
|
+
+-- Agent (0..N) -- ElevenLabs AI agents
|
|
38
|
+
+-- CalendarConnection (0..1)
|
|
39
|
+
+-- ClinicMetaConnections (0..N) -- multiple WhatsApp/Instagram connections
|
|
40
|
+
+-- Users (via assignment)
|
|
41
|
+
+-- Patients (0..N)
|
|
42
|
+
+-- Treatments (0..N)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Each clinic is **self-contained** -- it owns all its operational configuration directly (language, AI provider, grace period, operator settings, SMS, notifications). There is no settings inheritance chain from tenant to clinic.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 2. Clinic Model
|
|
50
|
+
|
|
51
|
+
### Prisma Schema
|
|
52
|
+
|
|
53
|
+
```prisma
|
|
54
|
+
model Clinic {
|
|
55
|
+
clinic_id String @id @default(uuid())
|
|
56
|
+
clinic_tenant_id String
|
|
57
|
+
clinic_name String
|
|
58
|
+
clinic_slug String // URL-friendly: "kadikoy", "main"
|
|
59
|
+
clinic_address String @default("")
|
|
60
|
+
clinic_timezone String @default("Europe/Istanbul")
|
|
61
|
+
clinic_is_default Boolean @default(false)
|
|
62
|
+
clinic_is_active Boolean @default(true)
|
|
63
|
+
|
|
64
|
+
// Language & AI (self-contained -- no tenant fallback)
|
|
65
|
+
clinic_language String @default("tr")
|
|
66
|
+
clinic_ai_provider String @default("elevenlabs")
|
|
67
|
+
clinic_ai_model String @default("")
|
|
68
|
+
|
|
69
|
+
// Operator workflow (proper columns -- queried on every incoming message)
|
|
70
|
+
clinic_operator_phone String? // Doctor's WhatsApp number for this clinic
|
|
71
|
+
clinic_operator_prefix String @default("R") // REF code prefix
|
|
72
|
+
clinic_operator_timeout_hours Int @default(24) // hours before request expires
|
|
73
|
+
clinic_grace_period_seconds Int @default(180) // seconds before AI takes over
|
|
74
|
+
|
|
75
|
+
// AI shift (proper columns)
|
|
76
|
+
clinic_ai_shift_enabled Boolean @default(false)
|
|
77
|
+
clinic_ai_shift_schedule Json @default("[]") // [{day, start, end}]
|
|
78
|
+
|
|
79
|
+
// Messaging
|
|
80
|
+
clinic_blacklisted_numbers String[] @default([])
|
|
81
|
+
clinic_fixed_first_message String? // null = disabled
|
|
82
|
+
|
|
83
|
+
// SMS & notifications
|
|
84
|
+
clinic_sms_enabled Boolean @default(false)
|
|
85
|
+
clinic_sms_phones String[] @default([])
|
|
86
|
+
clinic_notification_phones String[] @default([])
|
|
87
|
+
|
|
88
|
+
// Message retention
|
|
89
|
+
clinic_message_retention_days Int @default(7) // auto-deletion via BullMQ job
|
|
90
|
+
|
|
91
|
+
clinic_created_at DateTime @default(now())
|
|
92
|
+
clinic_updated_at DateTime @updatedAt
|
|
93
|
+
|
|
94
|
+
tenant Tenant @relation(fields: [clinic_tenant_id], references: [tenant_id], onDelete: Cascade)
|
|
95
|
+
userAssignments UserClinicAssignment[]
|
|
96
|
+
agents Agent[]
|
|
97
|
+
phoneNumbers PhoneNumber[]
|
|
98
|
+
calendarConnection ClinicCalendarConnection?
|
|
99
|
+
metaConnections ClinicMetaConnection[] // 1:many -- multiple WhatsApp/Instagram connections
|
|
100
|
+
whatsappChats WhatsAppChat[]
|
|
101
|
+
whatsappSessions WhatsAppSession[]
|
|
102
|
+
appointments Appointment[]
|
|
103
|
+
operatorRequests OperatorRequest[]
|
|
104
|
+
calls Call[]
|
|
105
|
+
leads Lead[]
|
|
106
|
+
outboundCampaigns OutboundCampaign[]
|
|
107
|
+
validationBatches AppointmentValidationBatch[]
|
|
108
|
+
patients Patient[]
|
|
109
|
+
treatments Treatment[]
|
|
110
|
+
|
|
111
|
+
@@unique([clinic_tenant_id, clinic_slug])
|
|
112
|
+
@@index([clinic_tenant_id])
|
|
113
|
+
@@index([clinic_tenant_id, clinic_operator_phone])
|
|
114
|
+
@@map("clinics")
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Key Design Decisions
|
|
119
|
+
|
|
120
|
+
**Default clinic:** Every tenant gets exactly one clinic with `clinic_is_default = true` on registration. Single-clinic tenants never need to create or manage clinics -- the default clinic is used implicitly. Multi-clinic tenants can create additional clinics and optionally change which one is the default.
|
|
121
|
+
|
|
122
|
+
**All operational config is on proper columns:** There is no `clinic_settings` JSONB blob. Every configurable value is a typed column with a sensible default. This means no null-means-inherit pattern and no runtime resolution logic -- the clinic row is the single source of truth.
|
|
123
|
+
|
|
124
|
+
| Column | Reason |
|
|
125
|
+
|--------|--------|
|
|
126
|
+
| `clinic_operator_phone` | Queried on every incoming message (WHERE clause) |
|
|
127
|
+
| `clinic_operator_prefix` | Queried for REF code generation |
|
|
128
|
+
| `clinic_operator_timeout_hours` | Queried for expiry sweep |
|
|
129
|
+
| `clinic_grace_period_seconds` | Queried on every session start |
|
|
130
|
+
| `clinic_ai_shift_enabled`, `clinic_ai_shift_schedule` | Queried by schedule sync job |
|
|
131
|
+
| `clinic_blacklisted_numbers` | Queried on every incoming message |
|
|
132
|
+
| `clinic_fixed_first_message` | Queried on first message in session |
|
|
133
|
+
| `clinic_language`, `clinic_ai_provider`, `clinic_ai_model` | Used when initializing AI connections |
|
|
134
|
+
| `clinic_sms_enabled`, `clinic_sms_phones`, `clinic_notification_phones` | Queried by notification service |
|
|
135
|
+
| `clinic_message_retention_days` | Used by BullMQ auto-deletion job |
|
|
136
|
+
|
|
137
|
+
### Slug
|
|
138
|
+
|
|
139
|
+
The `clinic_slug` is a URL-friendly identifier unique within a tenant (enforced by `@@unique([clinic_tenant_id, clinic_slug])`). Used in URLs and webhook routing. Examples: `"main"`, `"kadikoy"`, `"besiktas"`.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 3. PhoneNumber Model
|
|
144
|
+
|
|
145
|
+
### Purpose
|
|
146
|
+
|
|
147
|
+
The `PhoneNumber` table is a **unified registry** for all phone numbers across all use cases. It replaces four scattered locations in the current system:
|
|
148
|
+
|
|
149
|
+
| Old Location | Now In |
|
|
150
|
+
|-------------|--------|
|
|
151
|
+
| `tenant.settings.whatsapp_agent.connected_phone` | `phone_numbers` with `purpose = whatsapp` |
|
|
152
|
+
| `InboundCallConfig.phone_number` | `phone_numbers` with `purpose = inbound_call` |
|
|
153
|
+
| `tenant.settings.outbound_call_phone_number_id` | `phone_numbers` with `purpose = outbound_call` |
|
|
154
|
+
| Per-clinic operator phones | `clinic.clinic_operator_phone` (separate -- this is the *doctor's* personal number, not a system phone) |
|
|
155
|
+
|
|
156
|
+
### Prisma Schema
|
|
157
|
+
|
|
158
|
+
```prisma
|
|
159
|
+
model PhoneNumber {
|
|
160
|
+
phone_id String @id @default(uuid())
|
|
161
|
+
phone_tenant_id String
|
|
162
|
+
phone_clinic_id String? // null = tenant-level (unassigned)
|
|
163
|
+
phone_number String // E.164: +905551234567
|
|
164
|
+
phone_label String @default("")
|
|
165
|
+
phone_purpose PhoneNumberPurpose // whatsapp | inbound_call | outbound_call
|
|
166
|
+
phone_provider PhoneNumberProvider // meta_business | elevenlabs
|
|
167
|
+
phone_external_account_id String? // Meta phone_number_id
|
|
168
|
+
phone_elevenlabs_phone_id String? // ElevenLabs phone number ID
|
|
169
|
+
phone_elevenlabs_agent_id String? // ElevenLabs agent ID (external)
|
|
170
|
+
phone_agent_id String? // FK to agents table (internal routing)
|
|
171
|
+
phone_wa_connected Boolean @default(false)
|
|
172
|
+
phone_wa_connected_at DateTime?
|
|
173
|
+
phone_wa_bridge_provider String? // reserved for future use
|
|
174
|
+
phone_schedule_enabled Boolean @default(false)
|
|
175
|
+
phone_schedule Json @default("[]") // [{day, start, end}]
|
|
176
|
+
phone_agent_assigned Boolean @default(false)
|
|
177
|
+
phone_is_active Boolean @default(true)
|
|
178
|
+
phone_created_at DateTime @default(now())
|
|
179
|
+
phone_updated_at DateTime @updatedAt
|
|
180
|
+
|
|
181
|
+
tenant Tenant @relation(fields: [phone_tenant_id], references: [tenant_id], onDelete: Cascade)
|
|
182
|
+
clinic Clinic? @relation(fields: [phone_clinic_id], references: [clinic_id])
|
|
183
|
+
agent Agent? @relation(fields: [phone_agent_id], references: [agent_id])
|
|
184
|
+
chats WhatsAppChat[]
|
|
185
|
+
campaigns OutboundCampaign[]
|
|
186
|
+
validationBatches AppointmentValidationBatch[]
|
|
187
|
+
|
|
188
|
+
@@unique([phone_tenant_id, phone_number, phone_purpose])
|
|
189
|
+
@@index([phone_tenant_id, phone_purpose])
|
|
190
|
+
@@index([phone_clinic_id])
|
|
191
|
+
@@index([phone_provider, phone_external_account_id])
|
|
192
|
+
@@map("phone_numbers")
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Enums
|
|
197
|
+
|
|
198
|
+
```prisma
|
|
199
|
+
enum PhoneNumberPurpose {
|
|
200
|
+
whatsapp // WhatsApp AI chat
|
|
201
|
+
inbound_call // Receive voice calls via ElevenLabs SIP trunk
|
|
202
|
+
outbound_call // Make voice calls (campaigns, appointment validation)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
enum PhoneNumberProvider {
|
|
206
|
+
meta_business // Official Meta Business Cloud API
|
|
207
|
+
elevenlabs // ElevenLabs SIP trunk (voice calls)
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Key Concepts
|
|
212
|
+
|
|
213
|
+
**One number, one purpose:** The same E.164 number can appear multiple times in the table with different purposes. For example, `+905551234567` might be both a `whatsapp` number (for AI chat) and an `inbound_call` number (for voice). The uniqueness constraint is `(tenant_id, number, purpose)`.
|
|
214
|
+
|
|
215
|
+
**Provider determines how the number connects:**
|
|
216
|
+
|
|
217
|
+
| Purpose | Provider | How It Works |
|
|
218
|
+
|---------|----------|-------------|
|
|
219
|
+
| `whatsapp` | `meta_business` | Official WhatsApp Business API via Meta. OAuth-based setup. |
|
|
220
|
+
| `inbound_call` | `elevenlabs` | ElevenLabs SIP trunk. Phone is registered with ElevenLabs, agent assigned via API. |
|
|
221
|
+
| `outbound_call` | `elevenlabs` | Same ElevenLabs SIP trunk. Used for outbound campaigns and appointment validation. |
|
|
222
|
+
|
|
223
|
+
**Phone -> Clinic -> Agent routing chain:**
|
|
224
|
+
A phone number is assigned to a clinic (`phone_clinic_id`). The phone number points to an agent (`phone_agent_id`). When a message arrives on a phone number, the system looks up the phone, gets the clinic, gets the agent, and routes accordingly.
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
Incoming message/call
|
|
228
|
+
|
|
|
229
|
+
v
|
|
230
|
+
PhoneNumber (lookup by phone_number + purpose)
|
|
231
|
+
|
|
|
232
|
+
+-- phone_clinic_id --> Clinic (settings, operator, calendar)
|
|
233
|
+
|
|
|
234
|
+
+-- phone_agent_id --> Agent (AI config, voice, prompt)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Unassigned phones:** A phone number with `phone_clinic_id = null` is tenant-level (not yet assigned to a clinic). It must be assigned before it can route messages.
|
|
238
|
+
|
|
239
|
+
### Indexes
|
|
240
|
+
|
|
241
|
+
| Index | Purpose |
|
|
242
|
+
|-------|---------|
|
|
243
|
+
| `(phone_tenant_id, phone_purpose)` | List all WhatsApp numbers for a tenant, all inbound numbers, etc. |
|
|
244
|
+
| `(phone_clinic_id)` | Find all numbers for a clinic |
|
|
245
|
+
| `(phone_provider, phone_external_account_id)` | Webhook routing: look up phone by Meta phone_number_id |
|
|
246
|
+
| `(phone_tenant_id, phone_number, phone_purpose)` UNIQUE | Prevent duplicate registrations |
|
|
247
|
+
|
|
248
|
+
### API Endpoints
|
|
249
|
+
|
|
250
|
+
| Method | Path | Permission | Description |
|
|
251
|
+
|--------|------|-----------|-------------|
|
|
252
|
+
| GET | `/phone-numbers` | `phone_numbers:read` | List all (filterable by purpose, clinic, provider) |
|
|
253
|
+
| POST | `/phone-numbers` | `phone_numbers:manage` | Register phone number |
|
|
254
|
+
| GET | `/phone-numbers/:id` | `phone_numbers:read` | Detail with status |
|
|
255
|
+
| PUT | `/phone-numbers/:id` | `phone_numbers:manage` | Update config (label, clinic, agent, schedule) |
|
|
256
|
+
| DELETE | `/phone-numbers/:id` | `phone_numbers:manage` | Remove phone number |
|
|
257
|
+
| POST | `/phone-numbers/:id/connect-whatsapp` | `phone_numbers:manage` | Initiate WhatsApp connection |
|
|
258
|
+
| POST | `/phone-numbers/:id/disconnect-whatsapp` | `phone_numbers:manage` | Disconnect WhatsApp |
|
|
259
|
+
| GET | `/phone-numbers/:id/whatsapp-status` | `phone_numbers:read` | Connection status |
|
|
260
|
+
| PUT | `/phone-numbers/:id/schedule` | `phone_numbers:manage` | Update inbound call schedule |
|
|
261
|
+
| POST | `/phone-numbers/:id/toggle-agent` | `phone_numbers:manage` | Manual assign/unassign ElevenLabs agent |
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## 4. Clinic Connections
|
|
266
|
+
|
|
267
|
+
Clinic connections are **extracted from JSONB into dedicated tables** because they contain encrypted secrets and have distinct query/lifecycle patterns.
|
|
268
|
+
|
|
269
|
+
### 4.1 ClinicCalendarConnection
|
|
270
|
+
|
|
271
|
+
One Google Calendar OAuth connection per clinic. Stores encrypted refresh tokens for the Calendar API.
|
|
272
|
+
|
|
273
|
+
```prisma
|
|
274
|
+
model ClinicCalendarConnection {
|
|
275
|
+
calcn_id String @id @default(uuid())
|
|
276
|
+
calcn_clinic_id String @unique // 1:1 with Clinic
|
|
277
|
+
calcn_google_email String?
|
|
278
|
+
calcn_encrypted_refresh_token String? // AES-256-GCM encrypted
|
|
279
|
+
calcn_calendar_id String?
|
|
280
|
+
calcn_calendar_name String?
|
|
281
|
+
calcn_timezone String @default("Europe/Istanbul")
|
|
282
|
+
calcn_appointment_duration Int @default(30) // minutes
|
|
283
|
+
calcn_working_hours Json @default("[]") // [{day, start, end}]
|
|
284
|
+
calcn_blocked_dates Json @default("[]") // [{date, reason}]
|
|
285
|
+
calcn_connected_at DateTime?
|
|
286
|
+
calcn_created_at DateTime @default(now())
|
|
287
|
+
calcn_updated_at DateTime @updatedAt
|
|
288
|
+
|
|
289
|
+
clinic Clinic @relation(fields: [calcn_clinic_id], references: [clinic_id], onDelete: Cascade)
|
|
290
|
+
|
|
291
|
+
@@map("clinic_calendar_connections")
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**Why extracted from JSONB:**
|
|
296
|
+
- Contains `calcn_encrypted_refresh_token` -- encrypted secrets should not live in a generic JSON blob. Dedicated columns allow explicit access control and audit.
|
|
297
|
+
- Queried independently: token refresh logic, FreeBusy API calls, and appointment tools all need this data without loading the full clinic settings.
|
|
298
|
+
- Lifecycle differs from clinic settings: connection/disconnection is a distinct operation with its own OAuth flow.
|
|
299
|
+
|
|
300
|
+
### 4.2 ClinicMetaConnection
|
|
301
|
+
|
|
302
|
+
Multiple Meta Business API connections per clinic. Each connection represents one WhatsApp phone number or Instagram page. A clinic can have several WhatsApp numbers and Instagram connections simultaneously.
|
|
303
|
+
|
|
304
|
+
```prisma
|
|
305
|
+
model ClinicMetaConnection {
|
|
306
|
+
meta_id String @id @default(uuid())
|
|
307
|
+
meta_clinic_id String // FK to clinics (1:many -- multiple connections per clinic)
|
|
308
|
+
meta_waba_id String? // WhatsApp Business Account ID
|
|
309
|
+
meta_phone_number_id String?
|
|
310
|
+
meta_display_phone String?
|
|
311
|
+
meta_encrypted_access_token String? // AES-256-GCM encrypted
|
|
312
|
+
meta_instagram_connected Boolean @default(false)
|
|
313
|
+
meta_instagram_page_id String?
|
|
314
|
+
meta_encrypted_page_token String? // AES-256-GCM encrypted
|
|
315
|
+
meta_connected_at DateTime?
|
|
316
|
+
meta_created_at DateTime @default(now())
|
|
317
|
+
meta_updated_at DateTime @updatedAt
|
|
318
|
+
|
|
319
|
+
clinic Clinic @relation(fields: [meta_clinic_id], references: [clinic_id], onDelete: Cascade)
|
|
320
|
+
|
|
321
|
+
@@map("clinic_meta_connections")
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Why extracted from JSONB:**
|
|
326
|
+
- Two encrypted token fields (`meta_encrypted_access_token`, `meta_encrypted_page_token`).
|
|
327
|
+
- Queried on every incoming Meta webhook to resolve the clinic.
|
|
328
|
+
- Indexed by `meta_phone_number_id` for webhook routing (not shown in schema but critical for performance).
|
|
329
|
+
- Connection/disconnection is a distinct OAuth flow with its own state.
|
|
330
|
+
|
|
331
|
+
### Why Not Keep Secrets in JSONB?
|
|
332
|
+
|
|
333
|
+
| Concern | JSONB column | Dedicated table |
|
|
334
|
+
|---------|-------------|-----------------|
|
|
335
|
+
| Encryption at field level | Must decrypt entire JSON, extract, re-encrypt on update | Encrypt/decrypt individual column |
|
|
336
|
+
| Query pattern | `WHERE settings->>'encrypted_token' IS NOT NULL` is awkward | `WHERE encrypted_refresh_token IS NOT NULL` |
|
|
337
|
+
| Audit trail | "settings changed" -- which field? | "calendar connection updated" -- clear |
|
|
338
|
+
| Access control | Cannot grant column-level DB permissions | Can restrict table access |
|
|
339
|
+
| Schema evolution | Add fields to JSON blob, hope nothing breaks | Add columns with proper migrations |
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 5. Routing Flow
|
|
344
|
+
|
|
345
|
+
### 5.1 Incoming WhatsApp Message
|
|
346
|
+
|
|
347
|
+
```
|
|
348
|
+
WhatsApp message arrives at webhook
|
|
349
|
+
|
|
|
350
|
+
v
|
|
351
|
+
Extract sender phone number + recipient phone number
|
|
352
|
+
|
|
|
353
|
+
v
|
|
354
|
+
Lookup: phone_numbers WHERE phone_number = recipient
|
|
355
|
+
AND phone_purpose = 'whatsapp'
|
|
356
|
+
AND phone_wa_connected = true
|
|
357
|
+
|
|
|
358
|
+
v
|
|
359
|
+
Found PhoneNumber record
|
|
360
|
+
|
|
|
361
|
+
+-- phone_clinic_id --> Clinic
|
|
362
|
+
| |
|
|
363
|
+
| +-- clinic_operator_phone (for operator workflow)
|
|
364
|
+
| +-- clinic_grace_period_seconds (self-contained, no fallback)
|
|
365
|
+
| +-- clinic_blacklisted_numbers (check sender)
|
|
366
|
+
| +-- calendarConnection (for appointment tools)
|
|
367
|
+
|
|
|
368
|
+
+-- phone_agent_id --> Agent
|
|
369
|
+
| |
|
|
370
|
+
| +-- agent_elevenlabs_id (for ElevenLabs WS connection)
|
|
371
|
+
| +-- agent_system_prompt
|
|
372
|
+
| +-- agent_language
|
|
373
|
+
|
|
|
374
|
+
+-- phone_tenant_id --> Tenant
|
|
375
|
+
|
|
|
376
|
+
+-- tenant_enabled_features (feature flags)
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### 5.2 Incoming Voice Call
|
|
380
|
+
|
|
381
|
+
```
|
|
382
|
+
Inbound call arrives at ElevenLabs SIP trunk
|
|
383
|
+
|
|
|
384
|
+
v
|
|
385
|
+
ElevenLabs routes to configured agent (by phone_elevenlabs_agent_id)
|
|
386
|
+
|
|
|
387
|
+
v
|
|
388
|
+
Post-call webhook fires with agent_id
|
|
389
|
+
|
|
|
390
|
+
v
|
|
391
|
+
Lookup: agents WHERE agent_elevenlabs_id = webhook.agent_id
|
|
392
|
+
|
|
|
393
|
+
v
|
|
394
|
+
Agent --> Clinic (agent_clinic_id) --> Tenant (agent_tenant_id)
|
|
395
|
+
|
|
|
396
|
+
v
|
|
397
|
+
Process call record, trigger post-call actions
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### 5.3 Single-Clinic vs Multi-Clinic Routing
|
|
401
|
+
|
|
402
|
+
**Single-clinic tenant:**
|
|
403
|
+
- One default clinic, one or two phone numbers.
|
|
404
|
+
- Routing is trivial: every phone number points to the same clinic.
|
|
405
|
+
- No clinic selection UI needed. The clinic is implicit.
|
|
406
|
+
- All data (chats, calls, appointments) belongs to the single clinic.
|
|
407
|
+
|
|
408
|
+
**Multi-clinic tenant:**
|
|
409
|
+
- Multiple clinics, each with its own phone numbers.
|
|
410
|
+
- Routing is phone-number-based: the patient's choice of which number to contact determines which clinic they reach.
|
|
411
|
+
- Each clinic can have a different agent, different calendar, different operator.
|
|
412
|
+
- The dashboard shows a clinic selector, and data is filtered by clinic.
|
|
413
|
+
|
|
414
|
+
```
|
|
415
|
+
Multi-clinic dental chain:
|
|
416
|
+
|
|
417
|
+
Tenant: "DentPlus"
|
|
418
|
+
|
|
|
419
|
+
+-- Clinic: "Kadikoy"
|
|
420
|
+
| +-- Phone: +905551001 (whatsapp, meta_business) --> Agent: "DentPlus Kadikoy AI"
|
|
421
|
+
| +-- Phone: +905551002 (inbound_call, elevenlabs) --> Agent: "DentPlus Kadikoy Voice"
|
|
422
|
+
| +-- CalendarConnection: dentplus.kadikoy@gmail.com
|
|
423
|
+
|
|
|
424
|
+
+-- Clinic: "Besiktas"
|
|
425
|
+
| +-- Phone: +905552001 (whatsapp, meta_business) --> Agent: "DentPlus Besiktas AI"
|
|
426
|
+
| +-- Phone: +905552002 (inbound_call, elevenlabs) --> Agent: "DentPlus Besiktas Voice"
|
|
427
|
+
| +-- CalendarConnection: dentplus.besiktas@gmail.com
|
|
428
|
+
|
|
|
429
|
+
+-- Clinic: "Atasehir"
|
|
430
|
+
+-- Phone: +905553001 (whatsapp, meta_business) --> Agent: "DentPlus Atasehir AI"
|
|
431
|
+
+-- CalendarConnection: dentplus.atasehir@gmail.com
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Patient texts `+905551001` --> routed to Kadikoy clinic --> Kadikoy AI agent responds --> appointments booked on Kadikoy calendar.
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## 6. Clinic Configuration
|
|
439
|
+
|
|
440
|
+
Each clinic is **self-contained** -- every operational setting lives directly on the clinic row with a sensible default. There is no null-means-inherit pattern and no tenant-level fallback chain.
|
|
441
|
+
|
|
442
|
+
### 6.1 Configuration Table
|
|
443
|
+
|
|
444
|
+
| Setting | Column | Type | Default | Description |
|
|
445
|
+
|---------|--------|------|---------|-------------|
|
|
446
|
+
| Language | `clinic_language` | String | `"tr"` | AI and notification language |
|
|
447
|
+
| AI provider | `clinic_ai_provider` | String | `"elevenlabs"` | AI service provider |
|
|
448
|
+
| AI model | `clinic_ai_model` | String | `""` | Specific model override |
|
|
449
|
+
| Grace period (seconds) | `clinic_grace_period_seconds` | Int | 180 | Time before AI takes over |
|
|
450
|
+
| Operator phone | `clinic_operator_phone` | String? | null (disabled) | Doctor's WhatsApp number |
|
|
451
|
+
| Operator REF prefix | `clinic_operator_prefix` | String | `"R"` | REF code prefix letter |
|
|
452
|
+
| Operator timeout (hours) | `clinic_operator_timeout_hours` | Int | 24 | Hours before request expires |
|
|
453
|
+
| AI shift | `clinic_ai_shift_enabled` + `clinic_ai_shift_schedule` | Bool + Json | disabled | Schedule windows |
|
|
454
|
+
| Blacklisted numbers | `clinic_blacklisted_numbers` | String[] | [] | Numbers that bypass AI |
|
|
455
|
+
| Fixed first message | `clinic_fixed_first_message` | String? | null (disabled) | Auto-greeting on new session |
|
|
456
|
+
| SMS enabled | `clinic_sms_enabled` | Bool | false | Whether SMS notifications are active |
|
|
457
|
+
| SMS phones | `clinic_sms_phones` | String[] | [] | Recipient numbers for SMS |
|
|
458
|
+
| Notification phones | `clinic_notification_phones` | String[] | [] | Recipient numbers for push notifications |
|
|
459
|
+
| Message retention (days) | `clinic_message_retention_days` | Int | 7 | Auto-deletion via BullMQ job |
|
|
460
|
+
| Appointment duration (min) | `calcn_appointment_duration` | Int | 30 | On `ClinicCalendarConnection` |
|
|
461
|
+
|
|
462
|
+
### 6.2 Reading Config
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
// No resolution logic needed -- read directly from clinic
|
|
466
|
+
const gracePeriod = clinic.clinic_grace_period_seconds; // always has a value (default 180)
|
|
467
|
+
const operatorPrefix = clinic.clinic_operator_prefix; // always has a value (default "R")
|
|
468
|
+
const operatorTimeout = clinic.clinic_operator_timeout_hours; // always has a value (default 24)
|
|
469
|
+
const operatorPhone = clinic.clinic_operator_phone; // null means operator workflow disabled
|
|
470
|
+
const retentionDays = clinic.clinic_message_retention_days; // always has a value (default 7)
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
No `resolveClinicSettings()` function is needed. The clinic row is the single source of truth.
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## 7. Examples
|
|
478
|
+
|
|
479
|
+
### Example 1: Single-Clinic Tenant (Solo Dental Practice)
|
|
480
|
+
|
|
481
|
+
**Tenant:** "Dr. Ayse Yilmaz Dis Klinigi"
|
|
482
|
+
|
|
483
|
+
```
|
|
484
|
+
Tenant
|
|
485
|
+
tenant_id: "t-001"
|
|
486
|
+
tenant_name: "Dr. Ayse Yilmaz Dis Klinigi"
|
|
487
|
+
tenant_enabled_features: ["whatsapp_agent", "google_calendar", "operator_workflow"]
|
|
488
|
+
|
|
489
|
+
Clinic (auto-created on registration)
|
|
490
|
+
clinic_id: "c-001"
|
|
491
|
+
clinic_tenant_id: "t-001"
|
|
492
|
+
clinic_name: "Dr. Ayse Yilmaz Dis Klinigi"
|
|
493
|
+
clinic_slug: "main"
|
|
494
|
+
clinic_is_default: true
|
|
495
|
+
clinic_language: "tr"
|
|
496
|
+
clinic_operator_phone: "+905559876543" (Dr. Ayse's personal WhatsApp)
|
|
497
|
+
clinic_operator_prefix: "K"
|
|
498
|
+
clinic_operator_timeout_hours: 24
|
|
499
|
+
clinic_grace_period_seconds: 180
|
|
500
|
+
clinic_message_retention_days: 7
|
|
501
|
+
|
|
502
|
+
PhoneNumber
|
|
503
|
+
phone_id: "p-001"
|
|
504
|
+
phone_tenant_id: "t-001"
|
|
505
|
+
phone_clinic_id: "c-001"
|
|
506
|
+
phone_number: "+905551112233"
|
|
507
|
+
phone_purpose: whatsapp
|
|
508
|
+
phone_provider: meta_business
|
|
509
|
+
phone_wa_connected: true
|
|
510
|
+
phone_agent_id: "a-001"
|
|
511
|
+
|
|
512
|
+
Agent
|
|
513
|
+
agent_id: "a-001"
|
|
514
|
+
agent_tenant_id: "t-001"
|
|
515
|
+
agent_clinic_id: "c-001"
|
|
516
|
+
agent_elevenlabs_id: "el_abc123"
|
|
517
|
+
agent_name: "Ayse Klinik AI"
|
|
518
|
+
agent_language: "tr"
|
|
519
|
+
|
|
520
|
+
ClinicCalendarConnection
|
|
521
|
+
calcn_clinic_id: "c-001"
|
|
522
|
+
calcn_google_email: "ayse.klinik@gmail.com"
|
|
523
|
+
calcn_encrypted_refresh_token: "AES256GCM:..."
|
|
524
|
+
calcn_calendar_id: "primary"
|
|
525
|
+
calcn_appointment_duration: 30
|
|
526
|
+
calcn_working_hours: [
|
|
527
|
+
{"day": 1, "start": "09:00", "end": "18:00"},
|
|
528
|
+
{"day": 2, "start": "09:00", "end": "18:00"},
|
|
529
|
+
{"day": 3, "start": "09:00", "end": "18:00"},
|
|
530
|
+
{"day": 4, "start": "09:00", "end": "18:00"},
|
|
531
|
+
{"day": 5, "start": "09:00", "end": "17:00"}
|
|
532
|
+
]
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
**What happens when a patient texts +905551112233:**
|
|
536
|
+
|
|
537
|
+
1. Meta webhook arrives. Lookup `phone_numbers` by `+905551112233` + `whatsapp`.
|
|
538
|
+
2. Found: clinic = `c-001`, agent = `a-001`.
|
|
539
|
+
3. Read settings directly from clinic: grace period = 180, operator prefix = "K".
|
|
540
|
+
4. Check blacklist: `clinic_blacklisted_numbers` is empty.
|
|
541
|
+
5. Route to Agent `a-001` (ElevenLabs ID `el_abc123`).
|
|
542
|
+
6. AI responds. If patient sends photo, forward to operator phone `+905559876543`.
|
|
543
|
+
7. If patient asks for appointment, AI checks `calcn_working_hours` and Google FreeBusy.
|
|
544
|
+
|
|
545
|
+
The user (Dr. Ayse, role = `owner`) never sees a clinic selector in the UI. The single default clinic is implicit everywhere.
|
|
546
|
+
|
|
547
|
+
---
|
|
548
|
+
|
|
549
|
+
### Example 2: Multi-Clinic Dental Chain (3 Branches)
|
|
550
|
+
|
|
551
|
+
**Tenant:** "DentPlus"
|
|
552
|
+
|
|
553
|
+
```
|
|
554
|
+
Tenant
|
|
555
|
+
tenant_id: "t-002"
|
|
556
|
+
tenant_name: "DentPlus"
|
|
557
|
+
tenant_enabled_features: [
|
|
558
|
+
"whatsapp_agent", "google_calendar", "operator_workflow",
|
|
559
|
+
"inbound_agent", "appointment_validation"
|
|
560
|
+
]
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
#### Clinic 1: Kadikoy (default)
|
|
564
|
+
|
|
565
|
+
```
|
|
566
|
+
Clinic
|
|
567
|
+
clinic_id: "c-010"
|
|
568
|
+
clinic_name: "DentPlus Kadikoy"
|
|
569
|
+
clinic_slug: "kadikoy"
|
|
570
|
+
clinic_is_default: true
|
|
571
|
+
clinic_operator_phone: "+905551110001" (Dr. Mehmet)
|
|
572
|
+
clinic_operator_prefix: "D"
|
|
573
|
+
clinic_operator_timeout_hours: 8
|
|
574
|
+
clinic_grace_period_seconds: 120
|
|
575
|
+
clinic_message_retention_days: 7
|
|
576
|
+
|
|
577
|
+
PhoneNumbers:
|
|
578
|
+
+905550010001 purpose=whatsapp provider=meta_business agent="DentPlus Kadikoy Chat AI"
|
|
579
|
+
+905550010002 purpose=inbound_call provider=elevenlabs agent="DentPlus Kadikoy Voice AI"
|
|
580
|
+
+905550010003 purpose=outbound_call provider=elevenlabs agent="DentPlus Outbound"
|
|
581
|
+
|
|
582
|
+
CalendarConnection:
|
|
583
|
+
google_email: dentplus.kadikoy@gmail.com
|
|
584
|
+
appointment_duration: 45
|
|
585
|
+
working_hours: Mon-Fri 09:00-19:00, Sat 10:00-14:00
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
#### Clinic 2: Besiktas
|
|
589
|
+
|
|
590
|
+
```
|
|
591
|
+
Clinic
|
|
592
|
+
clinic_id: "c-011"
|
|
593
|
+
clinic_name: "DentPlus Besiktas"
|
|
594
|
+
clinic_slug: "besiktas"
|
|
595
|
+
clinic_is_default: false
|
|
596
|
+
clinic_operator_phone: "+905551110002" (Dr. Elif)
|
|
597
|
+
clinic_operator_prefix: "B"
|
|
598
|
+
clinic_operator_timeout_hours: 12
|
|
599
|
+
clinic_grace_period_seconds: 90
|
|
600
|
+
clinic_message_retention_days: 7
|
|
601
|
+
|
|
602
|
+
PhoneNumbers:
|
|
603
|
+
+905550020001 purpose=whatsapp provider=meta_business agent="DentPlus Besiktas Chat AI"
|
|
604
|
+
+905550020002 purpose=inbound_call provider=elevenlabs agent="DentPlus Besiktas Voice AI"
|
|
605
|
+
|
|
606
|
+
CalendarConnection:
|
|
607
|
+
google_email: dentplus.besiktas@gmail.com
|
|
608
|
+
appointment_duration: 30 (shorter appointments at this branch)
|
|
609
|
+
working_hours: Mon-Sat 08:00-20:00
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
#### Clinic 3: Atasehir
|
|
613
|
+
|
|
614
|
+
```
|
|
615
|
+
Clinic
|
|
616
|
+
clinic_id: "c-012"
|
|
617
|
+
clinic_name: "DentPlus Atasehir"
|
|
618
|
+
clinic_slug: "atasehir"
|
|
619
|
+
clinic_is_default: false
|
|
620
|
+
clinic_operator_phone: null (no operator workflow -- AI only)
|
|
621
|
+
clinic_operator_prefix: "D"
|
|
622
|
+
clinic_operator_timeout_hours: 12
|
|
623
|
+
clinic_grace_period_seconds: 120
|
|
624
|
+
clinic_message_retention_days: 7
|
|
625
|
+
|
|
626
|
+
PhoneNumbers:
|
|
627
|
+
+905550030001 purpose=whatsapp provider=meta_business agent="DentPlus Atasehir Chat AI"
|
|
628
|
+
|
|
629
|
+
CalendarConnection:
|
|
630
|
+
google_email: dentplus.atasehir@gmail.com
|
|
631
|
+
appointment_duration: 45
|
|
632
|
+
working_hours: Mon-Fri 10:00-18:00
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
#### Users and Clinic Assignments
|
|
636
|
+
|
|
637
|
+
```
|
|
638
|
+
User: Ahmet (owner)
|
|
639
|
+
role: owner
|
|
640
|
+
--> sees ALL three clinics (no assignments needed)
|
|
641
|
+
|
|
642
|
+
User: Zeynep (admin)
|
|
643
|
+
role: admin
|
|
644
|
+
assignments: [c-010 (Kadikoy), c-011 (Besiktas)]
|
|
645
|
+
--> sees Kadikoy + Besiktas data
|
|
646
|
+
|
|
647
|
+
User: Dr. Mehmet (doctor)
|
|
648
|
+
role: doctor
|
|
649
|
+
assignments: [c-010 (Kadikoy)]
|
|
650
|
+
--> sees only Kadikoy patients, appointments, chats
|
|
651
|
+
|
|
652
|
+
User: Fatma (receptionist)
|
|
653
|
+
role: receptionist
|
|
654
|
+
assignments: [c-011 (Besiktas), c-012 (Atasehir)]
|
|
655
|
+
--> sees Besiktas + Atasehir chats, appointments, leads
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
#### Settings Per Clinic
|
|
659
|
+
|
|
660
|
+
| Setting | Kadikoy | Besiktas | Atasehir |
|
|
661
|
+
|---------|---------|----------|----------|
|
|
662
|
+
| Grace period (s) | 120 | 90 | 120 |
|
|
663
|
+
| Operator prefix | "D" | "B" | "D" |
|
|
664
|
+
| Operator timeout (h) | 8 | 12 | 12 |
|
|
665
|
+
| Appt duration (min) | 45 (calendar) | 30 (calendar) | 45 (calendar) |
|
|
666
|
+
| Operator phone | +905551110001 | +905551110002 | null (disabled) |
|
|
667
|
+
| WhatsApp provider | Meta Business | Meta Business | Meta Business |
|
|
668
|
+
| Message retention | 7 days | 7 days | 7 days |
|
|
669
|
+
|
|
670
|
+
#### Routing in Action
|
|
671
|
+
|
|
672
|
+
**Patient A texts +905550020001 (Besiktas WhatsApp):**
|
|
673
|
+
- Lookup phone number --> Besiktas clinic (`c-011`)
|
|
674
|
+
- Agent: "DentPlus Besiktas Chat AI"
|
|
675
|
+
- Grace period: 90s (Besiktas override)
|
|
676
|
+
- If patient sends photo: forward to Dr. Elif (+905551110002)
|
|
677
|
+
- REF code prefix: "B" (Besiktas override) --> B0001
|
|
678
|
+
- Appointment: booked on `dentplus.besiktas@gmail.com`, 30-minute slots
|
|
679
|
+
|
|
680
|
+
**Patient B calls +905550010002 (Kadikoy inbound):**
|
|
681
|
+
- ElevenLabs routes to "DentPlus Kadikoy Voice AI"
|
|
682
|
+
- Post-call webhook: agent_elevenlabs_id --> Agent --> Clinic = Kadikoy
|
|
683
|
+
- Call record stored with `call_clinic_id = c-010`
|
|
684
|
+
- Lead generated with `lead_clinic_id = c-010`
|
|
685
|
+
|
|
686
|
+
**Patient C texts +905550030001 (Atasehir WhatsApp):**
|
|
687
|
+
- Lookup phone number --> Atasehir clinic (`c-012`)
|
|
688
|
+
- Operator phone is null --> operator workflow disabled for this clinic
|
|
689
|
+
- AI handles everything directly (no photo forwarding)
|
|
690
|
+
|
|
691
|
+
---
|
|
692
|
+
|
|
693
|
+
## Appendix: Agent Model
|
|
694
|
+
|
|
695
|
+
Agents are not tied to a specific phone number or call direction. The same agent can serve WhatsApp chat, inbound calls, and outbound campaigns through different PhoneNumber records pointing to it.
|
|
696
|
+
|
|
697
|
+
```prisma
|
|
698
|
+
model Agent {
|
|
699
|
+
agent_id String @id @default(uuid())
|
|
700
|
+
agent_tenant_id String
|
|
701
|
+
agent_clinic_id String? // null = tenant-level (shared across clinics)
|
|
702
|
+
agent_elevenlabs_id String // ElevenLabs agent ID
|
|
703
|
+
agent_name String @default("")
|
|
704
|
+
agent_description String @default("")
|
|
705
|
+
|
|
706
|
+
// Voice & language
|
|
707
|
+
agent_voice_id String @default("")
|
|
708
|
+
agent_language String @default("tr")
|
|
709
|
+
agent_greeting String @default("")
|
|
710
|
+
|
|
711
|
+
// AI config
|
|
712
|
+
agent_system_prompt String @default("")
|
|
713
|
+
agent_ai_provider String? // null = use clinic default
|
|
714
|
+
agent_ai_model String? // null = use clinic default
|
|
715
|
+
|
|
716
|
+
// Post-call processing
|
|
717
|
+
agent_post_call_strategy PostCallStrategy @default(summarize)
|
|
718
|
+
agent_post_call_wa_notify Boolean @default(true)
|
|
719
|
+
agent_post_call_sms_notify Boolean @default(false)
|
|
720
|
+
agent_summary_prompt String @default("")
|
|
721
|
+
agent_wa_message_template String @default("")
|
|
722
|
+
|
|
723
|
+
agent_extra_config Json @default("{}")
|
|
724
|
+
agent_is_active Boolean @default(true)
|
|
725
|
+
agent_created_at DateTime @default(now())
|
|
726
|
+
agent_updated_at DateTime @updatedAt
|
|
727
|
+
|
|
728
|
+
tenant Tenant @relation(...)
|
|
729
|
+
clinic Clinic? @relation(...)
|
|
730
|
+
phoneNumbers PhoneNumber[]
|
|
731
|
+
calls Call[]
|
|
732
|
+
outboundCampaigns OutboundCampaign[]
|
|
733
|
+
validationBatches AppointmentValidationBatch[]
|
|
734
|
+
|
|
735
|
+
@@index([agent_tenant_id])
|
|
736
|
+
@@index([agent_clinic_id])
|
|
737
|
+
@@index([agent_elevenlabs_id])
|
|
738
|
+
@@map("agents")
|
|
739
|
+
}
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
**Tenant-level agents** (`agent_clinic_id = null`) are shared across all clinics. Clinic-level agents are scoped to a specific clinic. A phone number references an agent via `phone_agent_id`; the agent's clinic assignment does not need to match the phone's clinic assignment (though it usually does).
|