Backend Development (Convex)
Relevant source files
The following files were used as context for generating this wiki page:
The backend of TalkCRM is built entirely on the Convex serverless platform. Convex provides the real-time database, serverless functions (queries, mutations, and actions), and HTTP endpoints required to power the application's voice and text interactions. It acts as the central nervous system, orchestrating communication between the voice interface (ElevenLabs), the AI brain (Anthropic's Claude), and the CRM (Salesforce).
This architecture allows TalkCRM to focus on product logic without managing servers, databases, or webhook infrastructure. Key capabilities provided by Convex include instant deployment of HTTP endpoints, orchestration of external API calls, real-time data synchronization for the frontend dashboard, and secure credential management.
Sources: PROJECT_DESCRIPTION.md, README.md
Architecture Overview
The Convex backend sits at the core of the TalkCRM architecture, handling all business logic and data persistence. It exposes HTTP endpoints that are used as webhook tools by the ElevenLabs Conversational AI agent. When a user speaks a command, ElevenLabs calls the appropriate Convex endpoint, which then runs actions to interpret the command, interact with the Salesforce API, and store results in the Convex database.
The diagram below illustrates the flow of information through the Convex backend.
Sources: README.md, PROJECT_DESCRIPTION.md
Database Schema
The data model is defined in convex/schema.ts and uses Convex's schema enforcement. The schema is designed to be multi-tenant, with most data linked to a specific user.
Sources: convex/schema.ts
Entity Relationship Diagram
This diagram shows the relationships between the main tables in the Convex database.
Sources: convex/schema.ts
Key Tables
users
Stores information about each registered user.
| Field | Type | Description |
|---|---|---|
email | v.string() | Unique user email, used as an index. |
name | v.optional(v.string()) | User's full name. |
verifiedPhones | v.optional(v.array(v.string())) | Array of E.164 formatted phone numbers for Caller ID authentication. |
status | v.union(...) | Account status (active, suspended, pending). |
tier | v.union(...) | Subscription tier (free, pro, etc.). |
| Sources: convex/schema.ts:12-25 |
salesforceAuth
Stores per-user Salesforce OAuth credentials, enabling multi-tenant connections.
| Field | Type | Description |
|---|---|---|
userId | v.id("users") | Foreign key linking to the users table. |
accessToken | v.string() | The current Salesforce access token. |
refreshToken | v.string() | The refresh token to obtain new access tokens. |
instanceUrl | v.string() | The user's specific Salesforce instance URL. |
expiresAt | v.number() | Timestamp when the access token expires. |
| Sources: convex/schema.ts:36-43 |
conversations
Logs every voice conversation, linking it to a user and capturing all relevant metadata.
| Field | Type | Description |
|---|---|---|
conversationId | v.string() | The Twilio Call SID, used as the primary key. |
elevenlabsConversationId | v.optional(v.string()) | The ID from the ElevenLabs conversation. |
userId | v.optional(v.id("users")) | The user who initiated the call. |
startTime | v.number() | Unix timestamp when the call started. |
status | v.union(...) | The call status (active, completed, failed). |
transcript | v.optional(v.string()) | The full call transcript. |
summary | v.optional(v.string()) | An AI-generated summary of the call. |
costCents | v.optional(v.number()) | Call cost from ElevenLabs. |
successEvaluation | v.optional(v.object(...)) | Structured success metrics from ElevenLabs. |
| Sources: convex/schema.ts:100-136 |
agentActivities
A real-time feed of AI agent actions, used to power the live dashboard. Old activities are automatically cleaned up based on the expiresAt field.
| Field | Type | Description |
|---|---|---|
type | v.union(...) | The type of activity (e.g., thinking, searching, updating). |
message | v.string() | A human-readable description of the action. |
recordId | v.optional(v.string()) | The Salesforce record ID involved in the action. |
conversationId | v.optional(v.string()) | The conversation associated with this activity. |
timestamp | v.number() | When the activity occurred. |
expiresAt | v.number() | Timestamp for auto-cleanup. |
| Sources: convex/schema.ts:154-171 |
HTTP Endpoints
Convex's httpRouter is used to define all public-facing HTTP endpoints. These endpoints serve as webhooks for external services like ElevenLabs, Twilio, and Slack.
Sources: convex/http.ts
Key Endpoints
| Method | Path | Handler | Description |
|---|---|---|---|
POST | /tools/assistant | api.ai.askSalesforce | The primary endpoint for ElevenLabs. It receives a natural language message, interprets it with Claude AI, and executes the corresponding Salesforce action. |
POST | /tools/search | api.salesforce.searchRecords | (Legacy) A specific tool to search Salesforce records. |
POST | /tools/create-record | api.salesforce.createRecord | (Legacy) A specific tool to create a new Salesforce record. |
POST | /tools/update-record | api.salesforce.updateRecord | (Legacy) A specific tool to update an existing Salesforce record. |
POST | /webhooks/elevenlabs/post-call | internal.conversations.completeConversation | Receives post-call analytics from ElevenLabs, including transcript, summary, and cost. |
POST | /webhooks/twilio/incoming | internal.users.getUserByPhoneInternal | Handles incoming Twilio calls, performs caller ID lookup, and connects the call to the ElevenLabs agent. |
GET | /auth/salesforce/callback | internal.salesforce.updateAuthForUser | Handles the OAuth2 callback from Salesforce to link a user's account. |
POST | /webhooks/slack/events | internal.slackCommands.handleAppMention | Handles events from Slack, such as @mentions of the bot. |
POST | /webhooks/slack/commands | internal.slackCommands.handleSlashCommand | Handles slash commands (e.g., /crm search) from Slack. |
Sources: convex/http.ts:90-689
Webhook Security
The backend implements signature verification for webhooks from ElevenLabs and Slack to ensure that incoming requests are authentic. This is handled by verifyElevenLabsSignature and verifySlackSignature helper functions. The verification process uses an HMAC-SHA256 hash of the request body and a timestamp, compared against a secret stored in environment variables.
Sources: convex/http.ts:40-87, 893-941
AI Assistant Flow
The /tools/assistant endpoint is the most critical. It unifies all user requests into a single intelligent entry point.
Sources: convex/http.ts:90-165, convex/ai.ts:153-203
AI Orchestration
The core AI logic resides in convex/ai.ts. The askSalesforce action is the central function that interprets natural language and orchestrates Salesforce operations.
Sources: convex/ai.ts
askSalesforce Action
This function follows a clear pattern:
- Fetch Context: It retrieves the user's Salesforce organization metadata (custom objects, fields) from the
orgMetadatatable. This provides Claude with a rich, dynamic schema to understand the user's specific Salesforce setup. - Interpret with Claude: It sends the user's message, conversation history, and the Salesforce schema context to the Claude API via the
interpretWithClaudefunction. - Parse Intent: Claude returns a structured JSON object (
ParsedIntent) containing theactionto perform (e.g.,query,create,update), the targetobjectType, and any necessary data likefieldsorsoqlqueries. - Execute Action: A
switchstatement routes the parsed intent to the appropriate Convex action that interacts with the Salesforce API (e.g.,api.salesforce.searchRecords,api.salesforce.createRecord). - Format Response: The result from the Salesforce action is formatted into a concise, human-readable string suitable for a voice response.
This flow is visualized below:
Sources: convex/ai.ts:153-386
AI Context (SALESFORCE_SCHEMA_CONTEXT)
A critical component of the AI's accuracy is the system prompt, which includes a detailed SALESFORCE_SCHEMA_CONTEXT. This static text defines the standard Salesforce objects, key fields, picklist values (e.g., Opportunity Stages), and rules for constructing SOQL queries. It explicitly tells the AI how to query for user-specific data using the OwnerId = CURRENT_USER filter, which the backend replaces with the actual user's Salesforce ID. This, combined with the dynamic customObjectsContext, gives the AI a comprehensive understanding of the available data structure.
Sources: convex/ai.ts:8-149
Conclusion
The TalkCRM backend, powered by Convex, is a prime example of a modern serverless architecture. It effectively offloads infrastructure management, allowing developers to focus on business logic. By leveraging Convex's integrated database, serverless functions, and real-time capabilities, the system provides a robust, scalable, and responsive foundation for the AI-driven features of the application. The clear separation of concerns—with http.ts for routing, schema.ts for data, and ai.ts for intelligence—creates a maintainable and extensible codebase.
Sources: PROJECT_DESCRIPTION.md, README.md