CRM Integration: HubSpot & Salesforce for ChatGPT Apps
Integrating your ChatGPT app with CRM platforms like HubSpot and Salesforce unlocks powerful sales automation capabilities. Users can manage contacts, track deals, sync emails, and automate workflows directly through natural language conversations—transforming how sales teams interact with their CRM data.
This comprehensive guide shows you how to build production-ready CRM integrations for ChatGPT apps, complete with OAuth authentication, real-time data synchronization, and intelligent workflow automation.
Table of Contents
- Why CRM Integration Matters for ChatGPT Apps
- OAuth 2.1 Authentication Setup
- HubSpot Integration Implementation
- Salesforce Integration Implementation
- Contact Management & Synchronization
- Deal Tracking & Pipeline Automation
- Email Sync & Activity Logging
- Task Automation & Workflows
- Custom Objects & Field Mapping
- Reporting & Analytics Integration
Why CRM Integration Matters for ChatGPT Apps {#why-crm-integration-matters}
CRM integrations transform ChatGPT apps from simple chatbots into intelligent sales assistants. Instead of forcing users to navigate complex CRM interfaces, they can simply ask: "Show me my top 10 deals closing this week" or "Create a contact for john@example.com with company Acme Corp."
Key Benefits:
- Natural Language CRM Access: Query and update CRM data using conversational commands
- Real-Time Data Sync: Automatic synchronization of contacts, deals, and activities
- Workflow Automation: Trigger CRM workflows based on conversation context
- Mobile-First Sales: Access full CRM capabilities from ChatGPT mobile app
- AI-Powered Insights: Leverage ChatGPT's intelligence to analyze sales data and suggest next actions
According to Salesforce's State of Sales Report, 79% of sales teams say conversational interfaces improve their productivity. ChatGPT apps with CRM integration deliver exactly that capability.
Related Resources:
- Build ChatGPT Apps: Complete No-Code Guide
- API Integration Patterns for ChatGPT Apps
- OAuth 2.1 Authentication for ChatGPT Apps
OAuth 2.1 Authentication Setup {#oauth-authentication-setup}
Both HubSpot and Salesforce require OAuth 2.1 with PKCE for secure authentication. Your ChatGPT app must implement the full OAuth flow to access user CRM data.
OAuth Configuration for HubSpot
HubSpot uses standard OAuth 2.0 with refresh tokens. Here's the metadata configuration:
{
"authorization_endpoint": "https://app.hubspot.com/oauth/authorize",
"token_endpoint": "https://api.hubapi.com/oauth/v1/token",
"scope": "crm.objects.contacts.read crm.objects.contacts.write crm.objects.deals.read crm.objects.deals.write crm.schemas.contacts.read crm.objects.companies.read",
"client_id": "YOUR_HUBSPOT_CLIENT_ID",
"client_secret": "YOUR_HUBSPOT_CLIENT_SECRET",
"redirect_uri": "https://chatgpt.com/connector_platform_oauth_redirect"
}
OAuth Configuration for Salesforce
Salesforce requires instance-specific URLs and supports custom connected apps:
{
"authorization_endpoint": "https://login.salesforce.com/services/oauth2/authorize",
"token_endpoint": "https://login.salesforce.com/services/oauth2/token",
"scope": "api refresh_token offline_access",
"client_id": "YOUR_SALESFORCE_CONSUMER_KEY",
"client_secret": "YOUR_SALESFORCE_CONSUMER_SECRET",
"redirect_uri": "https://chatgpt.com/connector_platform_oauth_redirect"
}
Token Refresh Handler
Both platforms require automatic token refresh. Here's a universal refresh handler:
// token-refresh-handler.ts
import axios from 'axios';
interface TokenData {
access_token: string;
refresh_token: string;
expires_at: number;
platform: 'hubspot' | 'salesforce';
}
export class TokenRefreshHandler {
private tokens: Map<string, TokenData> = new Map();
async getValidToken(userId: string, platform: 'hubspot' | 'salesforce'): Promise<string> {
const tokenData = this.tokens.get(`${userId}:${platform}`);
if (!tokenData) {
throw new Error('No tokens found for user');
}
// Check if token expires within 5 minutes
const expiresIn = tokenData.expires_at - Date.now();
if (expiresIn > 5 * 60 * 1000) {
return tokenData.access_token;
}
// Refresh token
return await this.refreshToken(userId, platform, tokenData);
}
private async refreshToken(
userId: string,
platform: 'hubspot' | 'salesforce',
tokenData: TokenData
): Promise<string> {
const config = this.getRefreshConfig(platform);
try {
const response = await axios.post(config.tokenEndpoint, {
grant_type: 'refresh_token',
refresh_token: tokenData.refresh_token,
client_id: config.clientId,
client_secret: config.clientSecret,
}, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
const newTokenData: TokenData = {
access_token: response.data.access_token,
refresh_token: response.data.refresh_token || tokenData.refresh_token,
expires_at: Date.now() + (response.data.expires_in * 1000),
platform
};
this.tokens.set(`${userId}:${platform}`, newTokenData);
return newTokenData.access_token;
} catch (error) {
throw new Error(`Token refresh failed: ${error.message}`);
}
}
private getRefreshConfig(platform: 'hubspot' | 'salesforce') {
if (platform === 'hubspot') {
return {
tokenEndpoint: 'https://api.hubapi.com/oauth/v1/token',
clientId: process.env.HUBSPOT_CLIENT_ID,
clientSecret: process.env.HUBSPOT_CLIENT_SECRET,
};
} else {
return {
tokenEndpoint: 'https://login.salesforce.com/services/oauth2/token',
clientId: process.env.SALESFORCE_CONSUMER_KEY,
clientSecret: process.env.SALESFORCE_CONSUMER_SECRET,
};
}
}
storeTokens(userId: string, platform: 'hubspot' | 'salesforce', tokenData: TokenData) {
this.tokens.set(`${userId}:${platform}`, tokenData);
}
}
Related Resources:
- Building ChatGPT Apps for Sales Teams
- ChatGPT App Security Best Practices
HubSpot Integration Implementation {#hubspot-integration}
HubSpot provides comprehensive CRM APIs with excellent developer documentation. This client implementation covers all major CRM operations.
// hubspot-client.ts
import axios, { AxiosInstance } from 'axios';
import { TokenRefreshHandler } from './token-refresh-handler';
interface HubSpotContact {
id?: string;
properties: {
email: string;
firstname?: string;
lastname?: string;
phone?: string;
company?: string;
jobtitle?: string;
lifecyclestage?: string;
};
}
interface HubSpotDeal {
id?: string;
properties: {
dealname: string;
amount?: string;
dealstage?: string;
pipeline?: string;
closedate?: string;
};
associations?: Array<{
to: { id: string };
types: Array<{ associationCategory: string; associationTypeId: number }>;
}>;
}
export class HubSpotClient {
private client: AxiosInstance;
private tokenHandler: TokenRefreshHandler;
private userId: string;
constructor(userId: string, tokenHandler: TokenRefreshHandler) {
this.userId = userId;
this.tokenHandler = tokenHandler;
this.client = axios.create({
baseURL: 'https://api.hubapi.com',
headers: {
'Content-Type': 'application/json',
},
});
// Add request interceptor for authentication
this.client.interceptors.request.use(async (config) => {
const token = await this.tokenHandler.getValidToken(this.userId, 'hubspot');
config.headers.Authorization = `Bearer ${token}`;
return config;
});
}
// Contact Operations
async createContact(contact: HubSpotContact): Promise<HubSpotContact> {
const response = await this.client.post('/crm/v3/objects/contacts', contact);
return response.data;
}
async updateContact(contactId: string, properties: Partial<HubSpotContact['properties']>): Promise<HubSpotContact> {
const response = await this.client.patch(`/crm/v3/objects/contacts/${contactId}`, {
properties
});
return response.data;
}
async getContact(contactId: string): Promise<HubSpotContact> {
const response = await this.client.get(`/crm/v3/objects/contacts/${contactId}`);
return response.data;
}
async searchContacts(filters: Record<string, any>): Promise<HubSpotContact[]> {
const response = await this.client.post('/crm/v3/objects/contacts/search', {
filterGroups: [{ filters }],
properties: ['email', 'firstname', 'lastname', 'phone', 'company'],
limit: 100,
});
return response.data.results;
}
// Deal Operations
async createDeal(deal: HubSpotDeal): Promise<HubSpotDeal> {
const response = await this.client.post('/crm/v3/objects/deals', deal);
return response.data;
}
async updateDeal(dealId: string, properties: Partial<HubSpotDeal['properties']>): Promise<HubSpotDeal> {
const response = await this.client.patch(`/crm/v3/objects/deals/${dealId}`, {
properties
});
return response.data;
}
async getDeals(filters?: { pipeline?: string; stage?: string }): Promise<HubSpotDeal[]> {
const filterGroups: any[] = [];
if (filters?.pipeline) {
filterGroups.push({
filters: [{
propertyName: 'pipeline',
operator: 'EQ',
value: filters.pipeline
}]
});
}
if (filters?.stage) {
filterGroups.push({
filters: [{
propertyName: 'dealstage',
operator: 'EQ',
value: filters.stage
}]
});
}
const response = await this.client.post('/crm/v3/objects/deals/search', {
filterGroups: filterGroups.length > 0 ? filterGroups : undefined,
properties: ['dealname', 'amount', 'dealstage', 'pipeline', 'closedate'],
sorts: [{ propertyName: 'closedate', direction: 'ASCENDING' }],
limit: 100,
});
return response.data.results;
}
// Association Operations
async associateContactToDeal(contactId: string, dealId: string): Promise<void> {
await this.client.put(
`/crm/v3/objects/contacts/${contactId}/associations/deals/${dealId}/3`
);
}
// Activity Logging
async createNote(objectType: 'contact' | 'deal', objectId: string, note: string): Promise<any> {
const response = await this.client.post('/crm/v3/objects/notes', {
properties: {
hs_note_body: note,
hs_timestamp: new Date().toISOString(),
},
associations: [{
to: { id: objectId },
types: [{
associationCategory: 'HUBSPOT_DEFINED',
associationTypeId: objectType === 'contact' ? 202 : 214
}]
}]
});
return response.data;
}
// Pipeline & Stage Management
async getPipelines(): Promise<any[]> {
const response = await this.client.get('/crm/v3/pipelines/deals');
return response.data.results;
}
async getStages(pipelineId: string): Promise<any[]> {
const response = await this.client.get(`/crm/v3/pipelines/deals/${pipelineId}`);
return response.data.stages;
}
}
Related Resources:
- ChatGPT Apps for Business Automation
- No-Code ChatGPT App Builder Features
Salesforce Integration Implementation {#salesforce-integration}
Salesforce uses a more complex data model with custom objects and relationships. This client handles the intricacies of Salesforce's API structure.
// salesforce-client.ts
import axios, { AxiosInstance } from 'axios';
import { TokenRefreshHandler } from './token-refresh-handler';
interface SalesforceContact {
Id?: string;
FirstName?: string;
LastName: string;
Email?: string;
Phone?: string;
AccountId?: string;
Title?: string;
LeadSource?: string;
}
interface SalesforceOpportunity {
Id?: string;
Name: string;
Amount?: number;
StageName: string;
CloseDate: string;
AccountId?: string;
Probability?: number;
Type?: string;
}
export class SalesforceClient {
private client: AxiosInstance;
private tokenHandler: TokenRefreshHandler;
private userId: string;
private instanceUrl: string;
constructor(userId: string, instanceUrl: string, tokenHandler: TokenRefreshHandler) {
this.userId = userId;
this.instanceUrl = instanceUrl;
this.tokenHandler = tokenHandler;
this.client = axios.create({
baseURL: `${instanceUrl}/services/data/v58.0`,
headers: {
'Content-Type': 'application/json',
},
});
// Add request interceptor for authentication
this.client.interceptors.request.use(async (config) => {
const token = await this.tokenHandler.getValidToken(this.userId, 'salesforce');
config.headers.Authorization = `Bearer ${token}`;
return config;
});
}
// Contact Operations
async createContact(contact: SalesforceContact): Promise<{ id: string; success: boolean }> {
const response = await this.client.post('/sobjects/Contact', contact);
return response.data;
}
async updateContact(contactId: string, updates: Partial<SalesforceContact>): Promise<void> {
await this.client.patch(`/sobjects/Contact/${contactId}`, updates);
}
async getContact(contactId: string): Promise<SalesforceContact> {
const response = await this.client.get(`/sobjects/Contact/${contactId}`);
return response.data;
}
async queryContacts(whereClause?: string): Promise<SalesforceContact[]> {
const query = `SELECT Id, FirstName, LastName, Email, Phone, AccountId, Title, LeadSource FROM Contact${whereClause ? ` WHERE ${whereClause}` : ''} LIMIT 100`;
const response = await this.client.get('/query', {
params: { q: query }
});
return response.data.records;
}
// Opportunity Operations
async createOpportunity(opportunity: SalesforceOpportunity): Promise<{ id: string; success: boolean }> {
const response = await this.client.post('/sobjects/Opportunity', opportunity);
return response.data;
}
async updateOpportunity(opportunityId: string, updates: Partial<SalesforceOpportunity>): Promise<void> {
await this.client.patch(`/sobjects/Opportunity/${opportunityId}`, updates);
}
async getOpportunity(opportunityId: string): Promise<SalesforceOpportunity> {
const response = await this.client.get(`/sobjects/Opportunity/${opportunityId}`);
return response.data;
}
async queryOpportunities(whereClause?: string): Promise<SalesforceOpportunity[]> {
const query = `SELECT Id, Name, Amount, StageName, CloseDate, AccountId, Probability, Type FROM Opportunity${whereClause ? ` WHERE ${whereClause}` : ''} ORDER BY CloseDate ASC LIMIT 100`;
const response = await this.client.get('/query', {
params: { q: query }
});
return response.data.records;
}
// Account Operations
async createAccount(name: string, additionalFields?: Record<string, any>): Promise<{ id: string; success: boolean }> {
const response = await this.client.post('/sobjects/Account', {
Name: name,
...additionalFields
});
return response.data;
}
async getAccount(accountId: string): Promise<any> {
const response = await this.client.get(`/sobjects/Account/${accountId}`);
return response.data;
}
// Task Creation
async createTask(task: {
Subject: string;
WhoId?: string;
WhatId?: string;
ActivityDate?: string;
Priority?: 'High' | 'Normal' | 'Low';
Status?: string;
}): Promise<{ id: string; success: boolean }> {
const response = await this.client.post('/sobjects/Task', task);
return response.data;
}
// Custom Object Query
async queryCustomObject(objectName: string, fields: string[], whereClause?: string): Promise<any[]> {
const fieldList = fields.join(', ');
const query = `SELECT ${fieldList} FROM ${objectName}${whereClause ? ` WHERE ${whereClause}` : ''} LIMIT 100`;
const response = await this.client.get('/query', {
params: { q: query }
});
return response.data.records;
}
// Metadata Operations
async describeObject(objectName: string): Promise<any> {
const response = await this.client.get(`/sobjects/${objectName}/describe`);
return response.data;
}
// Batch Operations
async batchCreateRecords(objectType: string, records: any[]): Promise<any[]> {
const requests = records.map((record, index) => ({
method: 'POST',
url: `/services/data/v58.0/sobjects/${objectType}`,
referenceId: `ref${index}`,
body: record
}));
const response = await this.client.post('/composite/batch', {
batchRequests: requests
});
return response.data.results;
}
}
Related Resources:
- ChatGPT App Templates for Every Industry
- Enterprise ChatGPT Apps: Security & Compliance
Contact Management & Synchronization {#contact-management}
Synchronizing contacts between your ChatGPT app and CRM platforms requires intelligent deduplication, conflict resolution, and bidirectional sync.
// contact-syncer.ts
import { HubSpotClient } from './hubspot-client';
import { SalesforceClient } from './salesforce-client';
interface UnifiedContact {
id?: string;
email: string;
firstName?: string;
lastName?: string;
phone?: string;
company?: string;
jobTitle?: string;
source: 'hubspot' | 'salesforce' | 'chatgpt';
lastModified: Date;
}
export class ContactSynchronizer {
private hubspot: HubSpotClient;
private salesforce: SalesforceClient;
constructor(hubspot: HubSpotClient, salesforce: SalesforceClient) {
this.hubspot = hubspot;
this.salesforce = salesforce;
}
async syncContactToHubSpot(contact: UnifiedContact): Promise<string> {
// Search for existing contact by email
const existing = await this.hubspot.searchContacts([{
propertyName: 'email',
operator: 'EQ',
value: contact.email
}]);
const hubspotContact = {
properties: {
email: contact.email,
firstname: contact.firstName,
lastname: contact.lastName,
phone: contact.phone,
company: contact.company,
jobtitle: contact.jobTitle,
}
};
if (existing.length > 0) {
// Update existing contact
await this.hubspot.updateContact(existing[0].id!, hubspotContact.properties);
return existing[0].id!;
} else {
// Create new contact
const result = await this.hubspot.createContact(hubspotContact);
return result.id!;
}
}
async syncContactToSalesforce(contact: UnifiedContact): Promise<string> {
// Search for existing contact by email
const existing = await this.salesforce.queryContacts(`Email = '${contact.email}'`);
const salesforceContact = {
FirstName: contact.firstName,
LastName: contact.lastName || 'Unknown',
Email: contact.email,
Phone: contact.phone,
Title: contact.jobTitle,
};
if (existing.length > 0) {
// Update existing contact
await this.salesforce.updateContact(existing[0].Id!, salesforceContact);
return existing[0].Id!;
} else {
// Create new contact
const result = await this.salesforce.createContact(salesforceContact);
return result.id;
}
}
async bidirectionalSync(): Promise<{ synced: number; errors: string[] }> {
const errors: string[] = [];
let syncedCount = 0;
try {
// Get all contacts from both platforms
const hubspotContacts = await this.hubspot.searchContacts([]);
const salesforceContacts = await this.salesforce.queryContacts();
// Create unified contact map by email
const contactMap = new Map<string, UnifiedContact[]>();
// Add HubSpot contacts
for (const hc of hubspotContacts) {
const unified: UnifiedContact = {
id: hc.id,
email: hc.properties.email,
firstName: hc.properties.firstname,
lastName: hc.properties.lastname,
phone: hc.properties.phone,
company: hc.properties.company,
jobTitle: hc.properties.jobtitle,
source: 'hubspot',
lastModified: new Date(), // Would come from API in production
};
const existing = contactMap.get(unified.email) || [];
existing.push(unified);
contactMap.set(unified.email, existing);
}
// Add Salesforce contacts
for (const sc of salesforceContacts) {
const unified: UnifiedContact = {
id: sc.Id,
email: sc.Email || '',
firstName: sc.FirstName,
lastName: sc.LastName,
phone: sc.Phone,
jobTitle: sc.Title,
source: 'salesforce',
lastModified: new Date(),
};
if (unified.email) {
const existing = contactMap.get(unified.email) || [];
existing.push(unified);
contactMap.set(unified.email, existing);
}
}
// Sync contacts that exist in one platform but not the other
for (const [email, contacts] of contactMap) {
const hasHubSpot = contacts.some(c => c.source === 'hubspot');
const hasSalesforce = contacts.some(c => c.source === 'salesforce');
if (hasHubSpot && !hasSalesforce) {
const contact = contacts.find(c => c.source === 'hubspot')!;
await this.syncContactToSalesforce(contact);
syncedCount++;
} else if (hasSalesforce && !hasHubSpot) {
const contact = contacts.find(c => c.source === 'salesforce')!;
await this.syncContactToHubSpot(contact);
syncedCount++;
}
}
} catch (error) {
errors.push(`Sync error: ${error.message}`);
}
return { synced: syncedCount, errors };
}
}
Related Resources:
- ChatGPT Apps for Customer Service
- Data Privacy in ChatGPT Apps
Deal Tracking & Pipeline Automation {#deal-tracking}
Automate deal progression through your sales pipeline based on conversation triggers and AI-detected intent.
// deal-tracker.ts
import { HubSpotClient } from './hubspot-client';
import { SalesforceClient } from './salesforce-client';
interface DealUpdate {
dealId: string;
stage?: string;
amount?: number;
closeDate?: string;
probability?: number;
notes?: string;
}
export class DealTracker {
private hubspot: HubSpotClient;
private salesforce: SalesforceClient;
private platform: 'hubspot' | 'salesforce';
constructor(
platform: 'hubspot' | 'salesforce',
hubspot?: HubSpotClient,
salesforce?: SalesforceClient
) {
this.platform = platform;
if (hubspot) this.hubspot = hubspot;
if (salesforce) this.salesforce = salesforce;
}
async getDealsClosingSoon(days: number = 7): Promise<any[]> {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() + days);
const cutoffStr = cutoffDate.toISOString().split('T')[0];
if (this.platform === 'hubspot') {
return await this.hubspot.getDeals();
} else {
return await this.salesforce.queryOpportunities(
`CloseDate <= ${cutoffStr} AND IsClosed = false`
);
}
}
async updateDealStage(update: DealUpdate): Promise<void> {
if (this.platform === 'hubspot') {
const properties: any = {};
if (update.stage) properties.dealstage = update.stage;
if (update.amount) properties.amount = update.amount.toString();
if (update.closeDate) properties.closedate = update.closeDate;
await this.hubspot.updateDeal(update.dealId, properties);
if (update.notes) {
await this.hubspot.createNote('deal', update.dealId, update.notes);
}
} else {
const opportunityUpdate: any = {};
if (update.stage) opportunityUpdate.StageName = update.stage;
if (update.amount) opportunityUpdate.Amount = update.amount;
if (update.closeDate) opportunityUpdate.CloseDate = update.closeDate;
if (update.probability) opportunityUpdate.Probability = update.probability;
await this.salesforce.updateOpportunity(update.dealId, opportunityUpdate);
if (update.notes) {
await this.salesforce.createTask({
Subject: 'Deal Update Notes',
WhatId: update.dealId,
Status: 'Completed',
});
}
}
}
async autoProgressDeal(dealId: string, trigger: string): Promise<void> {
// AI-powered deal progression logic
const stageMap: Record<string, { hubspot: string; salesforce: string }> = {
'demo_completed': { hubspot: 'presentationscheduled', salesforce: 'Proposal/Price Quote' },
'proposal_sent': { hubspot: 'decisionmakerboughtin', salesforce: 'Negotiation/Review' },
'contract_signed': { hubspot: 'contractsent', salesforce: 'Closed Won' },
'payment_received': { hubspot: 'closedwon', salesforce: 'Closed Won' },
};
const newStage = stageMap[trigger]?.[this.platform];
if (newStage) {
await this.updateDealStage({
dealId,
stage: newStage,
notes: `Automatically progressed to ${newStage} based on trigger: ${trigger}`,
});
}
}
async analyzeDealHealth(dealId: string): Promise<{
health: 'good' | 'warning' | 'critical';
recommendations: string[];
}> {
// Fetch deal details and analyze health signals
let deal: any;
if (this.platform === 'hubspot') {
deal = await this.hubspot.getDeals();
deal = deal.find(d => d.id === dealId);
} else {
deal = await this.salesforce.getOpportunity(dealId);
}
const recommendations: string[] = [];
let health: 'good' | 'warning' | 'critical' = 'good';
// Check close date
const closeDate = new Date(deal.properties?.closedate || deal.CloseDate);
const daysUntilClose = Math.floor((closeDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
if (daysUntilClose < 0) {
health = 'critical';
recommendations.push('Deal is past close date - update timeline or mark as lost');
} else if (daysUntilClose < 7) {
health = 'warning';
recommendations.push('Deal closing soon - ensure all paperwork is ready');
}
// Check deal amount
const amount = parseFloat(deal.properties?.amount || deal.Amount || '0');
if (amount === 0) {
health = 'warning';
recommendations.push('No deal amount set - add expected revenue');
}
return { health, recommendations };
}
}
Related Resources:
- ChatGPT Apps ROI Calculator
- Sales Automation with ChatGPT Apps
Email Sync & Activity Logging {#email-sync}
Automatically log email conversations and activities to your CRM as they happen in ChatGPT.
// email-activity-logger.ts
import { HubSpotClient } from './hubspot-client';
import { SalesforceClient } from './salesforce-client';
interface EmailActivity {
contactId?: string;
dealId?: string;
subject: string;
body: string;
direction: 'inbound' | 'outbound';
timestamp: Date;
metadata?: Record<string, any>;
}
export class EmailActivityLogger {
private hubspot?: HubSpotClient;
private salesforce?: SalesforceClient;
constructor(hubspot?: HubSpotClient, salesforce?: SalesforceClient) {
this.hubspot = hubspot;
this.salesforce = salesforce;
}
async logEmailToHubSpot(activity: EmailActivity): Promise<void> {
if (!this.hubspot) throw new Error('HubSpot client not configured');
// Create engagement (email activity)
const engagement = {
properties: {
hs_timestamp: activity.timestamp.toISOString(),
hs_email_subject: activity.subject,
hs_email_text: activity.body,
hs_email_direction: activity.direction.toUpperCase(),
hs_email_status: 'SENT',
},
associations: [] as any[]
};
// Associate with contact
if (activity.contactId) {
engagement.associations.push({
to: { id: activity.contactId },
types: [{ associationCategory: 'HUBSPOT_DEFINED', associationTypeId: 198 }]
});
}
// Associate with deal
if (activity.dealId) {
engagement.associations.push({
to: { id: activity.dealId },
types: [{ associationCategory: 'HUBSPOT_DEFINED', associationTypeId: 206 }]
});
}
// Log as note if no specific email engagement endpoint
const noteBody = `**Email: ${activity.subject}**\n\nDirection: ${activity.direction}\n\n${activity.body}`;
if (activity.contactId) {
await this.hubspot.createNote('contact', activity.contactId, noteBody);
}
if (activity.dealId) {
await this.hubspot.createNote('deal', activity.dealId, noteBody);
}
}
async logEmailToSalesforce(activity: EmailActivity): Promise<void> {
if (!this.salesforce) throw new Error('Salesforce client not configured');
// Create Email Message (requires enhanced email feature)
// Fallback to Task for standard Salesforce
const task = {
Subject: `Email: ${activity.subject}`,
Description: activity.body,
ActivityDate: activity.timestamp.toISOString().split('T')[0],
Status: 'Completed',
TaskSubtype: 'Email',
WhoId: activity.contactId, // Contact/Lead ID
WhatId: activity.dealId, // Opportunity ID
};
await this.salesforce.createTask(task);
}
}
Task Automation & Workflows {#task-automation}
Trigger CRM workflows and automate task creation based on conversation context.
// workflow-automator.ts
import { HubSpotClient } from './hubspot-client';
import { SalesforceClient } from './salesforce-client';
interface WorkflowTrigger {
type: 'contact_created' | 'deal_stage_changed' | 'email_sent' | 'meeting_scheduled';
data: Record<string, any>;
}
export class WorkflowAutomator {
private hubspot?: HubSpotClient;
private salesforce?: SalesforceClient;
constructor(hubspot?: HubSpotClient, salesforce?: SalesforceClient) {
this.hubspot = hubspot;
this.salesforce = salesforce;
}
async triggerWorkflow(trigger: WorkflowTrigger): Promise<void> {
switch (trigger.type) {
case 'contact_created':
await this.onContactCreated(trigger.data);
break;
case 'deal_stage_changed':
await this.onDealStageChanged(trigger.data);
break;
case 'meeting_scheduled':
await this.onMeetingScheduled(trigger.data);
break;
}
}
private async onContactCreated(data: { contactId: string; email: string }): Promise<void> {
// Create welcome task
if (this.salesforce) {
await this.salesforce.createTask({
Subject: 'Send welcome email',
WhoId: data.contactId,
Priority: 'Normal',
Status: 'Not Started',
ActivityDate: new Date().toISOString().split('T')[0],
});
}
}
private async onDealStageChanged(data: { dealId: string; newStage: string; oldStage: string }): Promise<void> {
if (data.newStage === 'Proposal/Price Quote' || data.newStage === 'decisionmakerboughtin') {
// Create follow-up task
if (this.salesforce) {
await this.salesforce.createTask({
Subject: 'Follow up on proposal',
WhatId: data.dealId,
Priority: 'High',
Status: 'Not Started',
});
}
}
}
private async onMeetingScheduled(data: { contactId: string; dealId?: string; meetingDate: string }): Promise<void> {
// Log meeting activity
if (this.hubspot && data.dealId) {
await this.hubspot.createNote(
'deal',
data.dealId,
`Meeting scheduled for ${data.meetingDate}`
);
}
}
}
Related Resources:
- Workflow Automation with ChatGPT Apps
- ChatGPT Apps Pricing Guide
Custom Objects & Field Mapping {#custom-objects}
Both HubSpot and Salesforce support custom objects and fields. Here's how to work with them dynamically.
HubSpot Custom Objects:
// Get custom object schema
const schema = await hubspot.client.get('/crm/v3/schemas');
// Create custom object record
await hubspot.client.post(`/crm/v3/objects/${customObjectType}`, {
properties: {
custom_field_1: 'value1',
custom_field_2: 'value2'
}
});
Salesforce Custom Objects:
// Query custom object
const records = await salesforce.queryCustomObject(
'CustomObject__c',
['Id', 'Name', 'CustomField__c'],
"Status__c = 'Active'"
);
// Describe object to get field metadata
const metadata = await salesforce.describeObject('CustomObject__c');
Reporting & Analytics Integration {#reporting-analytics}
Pull CRM analytics data into your ChatGPT app for AI-powered insights and reporting.
Example: Sales pipeline analysis
async function analyzePipeline() {
const deals = await hubspot.getDeals();
const byStage = deals.reduce((acc, deal) => {
const stage = deal.properties.dealstage;
acc[stage] = (acc[stage] || 0) + parseFloat(deal.properties.amount || '0');
return acc;
}, {} as Record<string, number>);
return {
totalValue: Object.values(byStage).reduce((sum, val) => sum + val, 0),
byStage,
dealCount: deals.length
};
}
Conclusion
CRM integration transforms ChatGPT apps into powerful sales automation tools. By implementing HubSpot and Salesforce connectors with OAuth authentication, real-time synchronization, and intelligent workflow automation, you enable sales teams to manage their entire CRM through natural language conversations.
Key Takeaways:
- Use OAuth 2.1 with automatic token refresh for secure authentication
- Implement bidirectional sync to prevent data silos
- Leverage AI to automate deal progression and task creation
- Log all activities to maintain complete CRM records
- Support custom objects for flexibility across industries
Ready to build your own CRM-integrated ChatGPT app? Start with MakeAIHQ's no-code platform and deploy your app in 48 hours—no programming required.
Related Resources:
- Build ChatGPT Apps: Complete No-Code Guide
- ChatGPT App Templates for Sales
- Enterprise ChatGPT Apps Documentation
- API Integration Best Practices
This article is part of MakeAIHQ's comprehensive guide to building production-ready ChatGPT apps. For more integration tutorials, check out our complete ChatGPT apps guide.