Integration Examples
Practical code examples for integrating Enfuse Smart Avatar webhooks with popular platforms and frameworks.
CRM Integrations
Salesforce Integration
// Salesforce webhook integration using JSForce
const jsforce = require('jsforce');
const salesforceConn = new jsforce.Connection({
loginUrl: process.env.SALESFORCE_LOGIN_URL
});
app.post('/webhook/conversation', async (req, res) => {
const payload = req.body;
try {
// Login to Salesforce
await salesforceConn.login(
process.env.SALESFORCE_USERNAME,
process.env.SALESFORCE_PASSWORD + process.env.SALESFORCE_TOKEN
);
// Find or create contact
const contact = await findOrCreateContact(payload.user_id, payload.user_info);
// Create activity record
const activity = {
Subject: `Avatar Conversation - ${payload.type}`,
Description: payload.conversation_transcript || payload.message,
WhoId: contact.Id,
Status: 'Completed',
Priority: payload.human_intervention?.urgency || 'Normal',
Sentiment__c: payload.sentiment?.label,
Sentiment_Score__c: payload.sentiment?.score,
Conversation_Health__c: payload.conversation_analytics?.conversation_health,
Escalation_Needed__c: payload.human_intervention?.needed
};
await salesforceConn.sobject('Task').create(activity);
// Create case if escalation needed
if (payload.human_intervention?.needed) {
const escalationCase = {
Subject: `Escalation: ${payload.human_intervention.reason}`,
Description: payload.conversation_transcript,
ContactId: contact.Id,
Priority: payload.human_intervention.urgency,
Origin: 'Avatar Chat',
Status: 'New',
Type: 'Customer Service'
};
await salesforceConn.sobject('Case').create(escalationCase);
}
res.json({ success: true });
} catch (error) {
console.error('Salesforce integration error:', error);
res.status(500).json({ error: error.message });
}
});
async function findOrCreateContact(email, userInfo) {
// Search for existing contact
const result = await salesforceConn.sobject('Contact')
.find({ Email: email }, 'Id, Name, Email');
if (result.length > 0) {
return result[0];
}
// Create new contact if not found
const newContact = {
Email: email,
FirstName: userInfo?.name?.split(' ')[0] || 'Unknown',
LastName: userInfo?.name?.split(' ').slice(1).join(' ') || 'User',
LeadSource: 'Avatar Chat'
};
const createResult = await salesforceConn.sobject('Contact').create(newContact);
return { Id: createResult.id, ...newContact };
}
HubSpot Integration
// HubSpot webhook integration
const hubspot = require('@hubspot/api-client');
const hubspotClient = new hubspot.Client({
apiKey: process.env.HUBSPOT_API_KEY
});
app.post('/webhook/conversation', async (req, res) => {
const payload = req.body;
try {
// Find or create contact
const contact = await findOrCreateHubSpotContact(payload.user_id, payload.user_info);
// Create note/activity
const noteProperties = {
hs_note_body: `
<h3>Avatar Conversation</h3>
<p><strong>Type:</strong> ${payload.type}</p>
<p><strong>Sentiment:</strong> ${payload.sentiment?.label} (${payload.sentiment?.score})</p>
<p><strong>Health:</strong> ${payload.conversation_analytics?.conversation_health}</p>
${payload.human_intervention?.needed ?
`<p><strong>⚠️ Escalation Needed:</strong> ${payload.human_intervention.reason}</p>` : ''
}
<hr>
<pre>${payload.conversation_transcript || payload.message}</pre>
`,
hs_timestamp: new Date().getTime(),
hubspot_owner_id: contact.properties.hubspot_owner_id
};
await hubspotClient.crm.objects.notes.basicApi.create({
properties: noteProperties,
associations: [
{
to: { id: contact.id },
types: [{ associationCategory: 'HUBSPOT_DEFINED', associationTypeId: 214 }]
}
]
});
// Update contact with latest conversation data
await hubspotClient.crm.contacts.basicApi.update(contact.id, {
properties: {
last_avatar_conversation: new Date().toISOString(),
latest_sentiment: payload.sentiment?.label,
conversation_health: payload.conversation_analytics?.conversation_health,
needs_attention: payload.human_intervention?.needed
}
});
// Create ticket if escalation needed
if (payload.human_intervention?.needed) {
await createHubSpotTicket(contact, payload);
}
res.json({ success: true });
} catch (error) {
console.error('HubSpot integration error:', error);
res.status(500).json({ error: error.message });
}
});
Support Platform Integrations
Zendesk Integration
# Zendesk webhook integration using zenpy
from zenpy import Zenpy
from zenpy.lib.api_objects import Ticket, User, Comment
# Initialize Zendesk client
zendesk_client = Zenpy(
email=os.environ['ZENDESK_EMAIL'],
token=os.environ['ZENDESK_TOKEN'],
subdomain=os.environ['ZENDESK_SUBDOMAIN']
)
@app.post("/webhook/conversation")
async def zendesk_webhook_handler(payload: WebhookPayload):
try:
# Find or create user
user = await find_or_create_zendesk_user(payload.user_id, payload.user_info)
# Create ticket for escalations
if payload.human_intervention and payload.human_intervention.get('needed'):
priority_map = {'low': 'low', 'medium': 'normal', 'high': 'urgent'}
ticket = Ticket(
subject=f"Avatar Escalation: {payload.human_intervention.get('reason')}",
description=format_zendesk_description(payload),
requester_id=user.id,
priority=priority_map.get(payload.human_intervention.get('urgency'), 'normal'),
type='incident',
tags=['avatar-chat', 'escalation', f"sentiment-{payload.sentiment.get('label')}"],
custom_fields=[
{'id': 360000123456, 'value': payload.conversation_id}, # Conversation ID field
{'id': 360000123457, 'value': payload.sentiment.get('score')}, # Sentiment Score field
{'id': 360000123458, 'value': payload.conversation_analytics.get('conversation_health')} # Health field
]
)
created_ticket = zendesk_client.tickets.create(ticket)
# Add conversation transcript as internal note
if payload.conversation_transcript:
comment = Comment(
body=f"Full Conversation Transcript:\n\n{payload.conversation_transcript}",
public=False
)
zendesk_client.tickets.update(created_ticket.ticket, comment)
# Always create a note for conversation tracking
else:
# Create a light ticket or add to existing conversation thread
await track_conversation_in_zendesk(user, payload)
return {"success": True}
except Exception as e:
logger.error(f"Zendesk integration error: {e}")
return {"success": False, "error": str(e)}
def format_zendesk_description(payload):
"""Format webhook payload for Zendesk ticket description"""
return f"""
Avatar Conversation Escalation
ESCALATION DETAILS:
- Reason: {payload.human_intervention.get('reason')}
- Urgency: {payload.human_intervention.get('urgency')}
- Pattern: {payload.human_intervention.get('pattern', 'N/A')}
CONVERSATION ANALYTICS:
- Health: {payload.conversation_analytics.get('conversation_health')}
- Sentiment: {payload.sentiment.get('label')} ({payload.sentiment.get('score')})
- Turn Count: {payload.conversation_analytics.get('turn_count')}
- Resolution Probability: {payload.conversation_analytics.get('resolution_probability')}
USER INFORMATION:
- Email: {payload.user_id}
- Name: {payload.user_info.get('name') if payload.user_info else 'Not provided'}
CONVERSATION TRANSCRIPT:
{payload.conversation_transcript or 'Transcript not available'}
"""
Freshdesk Integration
// Freshdesk webhook integration
const axios = require('axios');
const freshdeskAPI = axios.create({
baseURL: `https://${process.env.FRESHDESK_DOMAIN}.freshdesk.com/api/v2`,
auth: {
username: process.env.FRESHDESK_API_KEY,
password: 'X'
},
headers: {
'Content-Type': 'application/json'
}
});
app.post('/webhook/conversation', async (req, res) => {
const payload = req.body;
try {
// Find or create contact
const contact = await findOrCreateFreshdeskContact(payload.user_id, payload.user_info);
// Create ticket for escalations
if (payload.human_intervention?.needed) {
const priorityMap = { 'low': 1, 'medium': 2, 'high': 3 };
const ticketData = {
name: payload.user_info?.name || 'Avatar User',
email: payload.user_id,
subject: `Avatar Escalation: ${payload.human_intervention.reason}`,
description: formatFreshdeskDescription(payload),
status: 2, // Open
priority: priorityMap[payload.human_intervention.urgency] || 2,
source: 3, // Chat
tags: [
'avatar-chat',
'escalation',
`sentiment-${payload.sentiment?.label}`,
`health-${payload.conversation_analytics?.conversation_health}`
],
custom_fields: {
conversation_id: payload.conversation_id,
sentiment_score: payload.sentiment?.score,
conversation_health: payload.conversation_analytics?.conversation_health,
escalation_pattern: payload.human_intervention.pattern
}
};
const response = await freshdeskAPI.post('/tickets', ticketData);
console.log('Freshdesk ticket created:', response.data.id);
}
// Track conversation activity
await trackFreshdeskActivity(contact, payload);
res.json({ success: true });
} catch (error) {
console.error('Freshdesk integration error:', error);
res.status(500).json({ error: error.message });
}
});
function formatFreshdeskDescription(payload) {
return `
<h3>Avatar Conversation Escalation</h3>
<h4>Escalation Details:</h4>
<ul>
<li><strong>Reason:</strong> ${payload.human_intervention.reason}</li>
<li><strong>Urgency:</strong> ${payload.human_intervention.urgency}</li>
<li><strong>Pattern:</strong> ${payload.human_intervention.pattern || 'N/A'}</li>
</ul>
<h4>Conversation Analytics:</h4>
<ul>
<li><strong>Health:</strong> ${payload.conversation_analytics?.conversation_health}</li>
<li><strong>Sentiment:</strong> ${payload.sentiment?.label} (${payload.sentiment?.score})</li>
<li><strong>Turn Count:</strong> ${payload.conversation_analytics?.turn_count}</li>
<li><strong>Resolution Probability:</strong> ${payload.conversation_analytics?.resolution_probability}</li>
</ul>
<h4>Full Conversation:</h4>
<pre>${payload.conversation_transcript || 'Transcript not available'}</pre>
`;
}
Analytics and Monitoring
Mixpanel Integration
// Mixpanel analytics integration
const Mixpanel = require('mixpanel');
const mixpanel = Mixpanel.init(process.env.MIXPANEL_TOKEN);
app.post('/webhook/conversation', async (req, res) => {
const payload = req.body;
try {
// Track conversation event
mixpanel.track('Avatar Conversation', {
distinct_id: payload.user_id,
conversation_id: payload.conversation_id,
message_type: payload.type,
sentiment_label: payload.sentiment?.label,
sentiment_score: payload.sentiment?.score,
conversation_health: payload.conversation_analytics?.conversation_health,
escalation_needed: payload.human_intervention?.needed,
escalation_urgency: payload.human_intervention?.urgency,
turn_count: payload.conversation_analytics?.turn_count,
resolution_probability: payload.conversation_analytics?.resolution_probability,
user_name_collected: !!payload.user_info?.name,
user_email_collected: !!payload.user_info?.email
});
// Update user profile
mixpanel.people.set(payload.user_id, {
$email: payload.user_info?.email || payload.user_id,
$name: payload.user_info?.name,
last_avatar_conversation: new Date().toISOString(),
latest_sentiment: payload.sentiment?.label,
total_conversations: payload.conversation_analytics?.turn_count
});
// Track escalation events separately
if (payload.human_intervention?.needed) {
mixpanel.track('Avatar Escalation', {
distinct_id: payload.user_id,
conversation_id: payload.conversation_id,
reason: payload.human_intervention.reason,
urgency: payload.human_intervention.urgency,
pattern: payload.human_intervention.pattern,
sentiment_score: payload.sentiment?.score,
conversation_health: payload.conversation_analytics?.conversation_health
});
}
res.json({ success: true });
} catch (error) {
console.error('Mixpanel integration error:', error);
res.status(500).json({ error: error.message });
}
});
Google Analytics 4 Integration
// Google Analytics 4 integration using Measurement Protocol
const axios = require('axios');
const GA4_MEASUREMENT_ID = process.env.GA4_MEASUREMENT_ID;
const GA4_API_SECRET = process.env.GA4_API_SECRET;
app.post('/webhook/conversation', async (req, res) => {
const payload = req.body;
try {
// Send event to GA4
const ga4Event = {
client_id: generateClientId(payload.user_id),
events: [
{
name: 'avatar_conversation',
parameters: {
conversation_id: payload.conversation_id,
message_type: payload.type,
sentiment_label: payload.sentiment?.label,
sentiment_score: payload.sentiment?.score,
conversation_health: payload.conversation_analytics?.conversation_health,
escalation_needed: payload.human_intervention?.needed,
turn_count: payload.conversation_analytics?.turn_count,
custom_parameter_1: payload.user_info?.name ? 'name_collected' : 'name_not_collected'
}
}
]
};
// Send escalation event if needed
if (payload.human_intervention?.needed) {
ga4Event.events.push({
name: 'avatar_escalation',
parameters: {
conversation_id: payload.conversation_id,
escalation_reason: payload.human_intervention.reason,
escalation_urgency: payload.human_intervention.urgency,
sentiment_score: payload.sentiment?.score
}
});
}
await axios.post(
`https://www.google-analytics.com/mp/collect?measurement_id=${GA4_MEASUREMENT_ID}&api_secret=${GA4_API_SECRET}`,
ga4Event
);
res.json({ success: true });
} catch (error) {
console.error('GA4 integration error:', error);
res.status(500).json({ error: error.message });
}
});
function generateClientId(userId) {
// Generate a consistent client ID from user ID
const crypto = require('crypto');
return crypto.createHash('md5').update(userId).digest('hex');
}
Database Storage Examples
PostgreSQL with Prisma
// Prisma schema (schema.prisma)
/*
model Conversation {
id String @id
userId String
organizationId String
projectId String
avatarProfileId String
startedAt DateTime @default(now())
lastActivity DateTime @updatedAt
cumulativeSentiment Float?
conversationHealth String?
escalationNeeded Boolean @default(false)
messages ConversationMessage[]
@@map("conversations")
}
model ConversationMessage {
id String @id @default(cuid())
conversationId String
message String
type String // 'user' or 'ai'
timestamp DateTime @default(now())
sentimentScore Float?
sentimentLabel String?
escalationRisk String?
conversation Conversation @relation(fields: [conversationId], references: [id])
@@map("conversation_messages")
}
*/
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
app.post('/webhook/conversation', async (req, res) => {
const payload = req.body;
try {
// Upsert conversation
const conversation = await prisma.conversation.upsert({
where: { id: payload.conversation_id },
update: {
lastActivity: new Date(),
cumulativeSentiment: payload.conversation_analytics?.cumulative_sentiment,
conversationHealth: payload.conversation_analytics?.conversation_health,
escalationNeeded: payload.human_intervention?.needed || false
},
create: {
id: payload.conversation_id,
userId: payload.user_id,
organizationId: payload.organization_id,
projectId: payload.project_id,
avatarProfileId: payload.avatar_profile_id,
cumulativeSentiment: payload.conversation_analytics?.cumulative_sentiment,
conversationHealth: payload.conversation_analytics?.conversation_health,
escalationNeeded: payload.human_intervention?.needed || false
}
});
// Create message record
await prisma.conversationMessage.create({
data: {
conversationId: payload.conversation_id,
message: payload.message,
type: payload.type,
sentimentScore: payload.sentiment?.score,
sentimentLabel: payload.sentiment?.label,
escalationRisk: payload.sentiment?.escalation_risk
}
});
res.json({ success: true });
} catch (error) {
console.error('Database error:', error);
res.status(500).json({ error: error.message });
}
});
MongoDB Integration
// MongoDB with Mongoose
const mongoose = require('mongoose');
const conversationSchema = new mongoose.Schema({
conversationId: { type: String, required: true, unique: true },
userId: { type: String, required: true },
organizationId: String,
projectId: String,
avatarProfileId: String,
messages: [{
message: String,
type: { type: String, enum: ['user', 'ai'] },
timestamp: { type: Date, default: Date.now },
sentiment: {
score: Number,
label: String,
confidence: Number,
emotions: [String],
urgency: String,
escalationRisk: String
}
}],
analytics: {
cumulativeSentiment: Number,
sentimentTrend: String,
conversationHealth: String,
turnCount: Number,
resolutionProbability: Number,
dominantEmotions: [String]
},
userInfo: {
name: String,
email: String,
collected: { type: Boolean, default: false },
declined: { type: Boolean, default: false }
},
escalations: [{
needed: Boolean,
reason: String,
urgency: String,
pattern: String,
timestamp: { type: Date, default: Date.now }
}],
startedAt: { type: Date, default: Date.now },
lastActivity: { type: Date, default: Date.now }
});
const Conversation = mongoose.model('Conversation', conversationSchema);
app.post('/webhook/conversation', async (req, res) => {
const payload = req.body;
try {
const messageData = {
message: payload.message,
type: payload.type,
timestamp: new Date(payload.timestamp),
sentiment: payload.sentiment
};
const escalationData = payload.human_intervention?.needed ? {
needed: payload.human_intervention.needed,
reason: payload.human_intervention.reason,
urgency: payload.human_intervention.urgency,
pattern: payload.human_intervention.pattern
} : null;
await Conversation.findOneAndUpdate(
{ conversationId: payload.conversation_id },
{
$set: {
userId: payload.user_id,
organizationId: payload.organization_id,
projectId: payload.project_id,
avatarProfileId: payload.avatar_profile_id,
analytics: payload.conversation_analytics,
userInfo: payload.user_info,
lastActivity: new Date()
},
$push: {
messages: messageData,
...(escalationData && { escalations: escalationData })
}
},
{
upsert: true,
new: true,
setDefaultsOnInsert: true
}
);
res.json({ success: true });
} catch (error) {
console.error('MongoDB error:', error);
res.status(500).json({ error: error.message });
}
});
Real-time Dashboard Example
React Dashboard with Socket.IO
// Dashboard component (React)
import React, { useState, useEffect } from 'react';
import io from 'socket.io-client';
const ConversationDashboard = () => {
const [conversations, setConversations] = useState([]);
const [metrics, setMetrics] = useState({});
const [socket, setSocket] = useState(null);
useEffect(() => {
const newSocket = io('http://localhost:3000');
setSocket(newSocket);
newSocket.on('conversation-event', (data) => {
setConversations(prev => {
const existing = prev.findIndex(c => c.conversationId === data.conversationId);
if (existing >= 0) {
const updated = [...prev];
updated[existing] = { ...updated[existing], ...data };
return updated;
} else {
return [data, ...prev].slice(0, 50); // Keep latest 50
}
});
});
newSocket.on('dashboard-metrics', (data) => {
setMetrics(data);
});
return () => newSocket.close();
}, []);
return (
<div className="dashboard">
<div className="metrics">
<div className="metric">
<h3>Active Conversations</h3>
<span>{metrics.activeConversations || 0}</span>
</div>
<div className="metric">
<h3>Avg Sentiment</h3>
<span className={`sentiment-${metrics.avgSentiment > 0 ? 'positive' : 'negative'}`}>
{metrics.avgSentiment?.toFixed(2) || '0.00'}
</span>
</div>
<div className="metric">
<h3>Escalation Rate</h3>
<span>{(metrics.escalationRate * 100)?.toFixed(1) || '0.0'}%</span>
</div>
</div>
<div className="conversations">
<h2>Live Conversations</h2>
{conversations.map(conv => (
<div key={conv.conversationId} className={`conversation ${conv.interventionNeeded ? 'needs-attention' : ''}`}>
<div className="conversation-header">
<span className="user">{conv.userEmail}</span>
<span className="time">{new Date(conv.timestamp).toLocaleTimeString()}</span>
<span className={`health health-${conv.health}`}>{conv.health}</span>
</div>
<div className="message">{conv.message}</div>
{conv.interventionNeeded && (
<div className="alert">⚠️ Human intervention needed</div>
)}
<div className="sentiment">
Sentiment: <span className={`sentiment-${conv.sentiment?.label}`}>
{conv.sentiment?.label} ({conv.sentiment?.score?.toFixed(2)})
</span>
</div>
</div>
))}
</div>
</div>
);
};
export default ConversationDashboard;
These examples provide a comprehensive foundation for integrating Enfuse Smart Avatar webhooks with various platforms and systems. Each example is designed to be production-ready with proper error handling, security considerations, and scalability in mind.