User can automatically update contacts in external CRM or database systems after AI calls complete using Callab’s webhook feature. This enables real-time synchronization of call outcomes and contact data.
Contact auto-update uses Callab’s webhooks to send call data and outcomes to external systems immediately after calls end.
Contact auto-update automatically syncs call data back to external systems:
How It Works:
- AI agent completes call
- Call outcomes extracted
- Webhook triggered automatically
- Data sent to external API
- CRM contact updated in real-time
Benefits:
- Real-time CRM synchronization
- Automatic contact updates
- Call outcome tracking
- No manual data entry
- Bi-directional data flow
Use Cases:
- Update lead status after sales calls
- Sync call notes to CRM
- Track appointment bookings
- Update contact information
- Record call outcomes
Webhook vs Integration
Webhooks (Push):
- Callab pushes data to external system
- Triggered by events (call ended, outcome extracted)
- Real-time updates
- Best for updating external systems
Integrations (Pull):
- Callab pulls data from external system
- Triggered by schedule or webhook
- Batch processing
- Best for importing contacts
Use both together: Integrations to import contacts, Webhooks to update them after calls.
Create Webhook
User can create webhook to update contacts:
Webhook Configuration:
Webhook Name:
- Enter descriptive name
- Example: “Update HubSpot Leads”
- Example: “Sync Call Outcomes to Salesforce”
Webhook URL:
- Enter your API endpoint
- Format:
https://your-api.com/webhook
- Must be HTTPS (secure)
- Receives POST requests from Callab
Authentication:
User can configure authentication:
API Key Authentication:
Header: Authorization
Value: Bearer YOUR_API_KEY
Custom Headers:
Header: X-API-Key
Value: YOUR_SECRET_KEY
Basic Authentication:
Header: Authorization
Value: Basic base64(username:password)
Trigger Events:
User can select which events trigger webhook:
Call Started:
- Fires when call begins
- Contains caller information
- Initial contact data
Call Ended:
- Fires when call completes
- Contains call duration
- Final call status
- Recording URL
Call Outcome Extracted:
- Fires after outcome extraction
- Contains extracted data fields
- Post-call analysis results
- Agent-defined outcomes
Select Data Fields:
User can choose which fields to send:
Contact Information:
- Contact ID
- Phone number
- First name
- Last name
- Email
- Company
- Custom fields
Call Details:
- Call ID
- Campaign ID
- Agent ID
- Call duration
- Call status
- Start timestamp
- End timestamp
Call Content:
- Recording URL
- Transcript text
- Summary
Extracted Outcomes:
- All outcome fields
- Custom extracted data
- Agent variables
Webhook Payload Structure
Complete Payload Example:
{
"event": "call_outcome_extracted",
"timestamp": "2025-11-17T10:30:00Z",
"workspace_id": "ws_abc123",
"campaign": {
"id": "camp_xyz789",
"name": "Sales Outreach",
"type": "outbound"
},
"contact": {
"id": "cont_123456",
"phone": "+14085551234",
"first_name": "John",
"last_name": "Smith",
"email": "john@techcorp.com",
"company": "Tech Corp",
"category": "Lead",
"tags": ["Interested", "Enterprise"],
"custom_fields": {
"crm_id": "lead_123",
"source": "website"
}
},
"call": {
"id": "call_abc789",
"status": "completed",
"duration": 180,
"start_time": "2025-11-17T10:27:00Z",
"end_time": "2025-11-17T10:30:00Z",
"recording_url": "https://...",
"transcript": "Full call transcript...",
"summary": "Customer expressed interest..."
},
"outcomes": {
"interested": true,
"product": "Enterprise Plan",
"budget": "$50,000",
"decision_timeline": "Q1 2026",
"next_action": "Schedule demo",
"appointment_booked": true,
"appointment_date": "2025-11-20T14:00:00Z",
"objections": ["Price concerns"],
"sentiment": "Positive"
},
"agent": {
"id": "agent_xyz",
"name": "Sales Agent"
}
}
Webhook Endpoint Implementation:
User implements endpoint to receive webhook and update CRM:
HubSpot Update Example
// Express.js webhook endpoint
app.post('/webhook/callab-outcomes', async (req, res) => {
const { contact, call, outcomes } = req.body;
// Extract CRM ID from Callab contact
const hubspotId = contact.custom_fields.crm_id;
// Update HubSpot contact
await hubspot.contacts.update(hubspotId, {
properties: {
// Update call status
last_call_date: call.end_time,
last_call_duration: call.duration,
call_outcome: call.status,
// Update extracted outcomes
product_interest: outcomes.product,
budget_range: outcomes.budget,
decision_timeline: outcomes.decision_timeline,
next_action: outcomes.next_action,
// Update lead status based on interest
lifecyclestage: outcomes.interested ? 'qualifiedlead' : 'subscriber',
// Add call notes
last_call_notes: call.summary,
// Update appointment if booked
appointment_scheduled: outcomes.appointment_booked,
appointment_date: outcomes.appointment_date
}
});
// Respond with success
res.status(200).json({ success: true });
});
Salesforce Update Example
app.post('/webhook/callab-outcomes', async (req, res) => {
const { contact, call, outcomes } = req.body;
const salesforceId = contact.custom_fields.crm_id;
// Update Salesforce lead
await salesforce.sobject('Lead').update({
Id: salesforceId,
// Call information
Last_Call_Date__c: call.end_time,
Call_Duration__c: call.duration,
Recording_URL__c: call.recording_url,
// Outcomes
Product_Interest__c: outcomes.product,
Budget__c: outcomes.budget,
Timeline__c: outcomes.decision_timeline,
// Lead status
Status: outcomes.interested ? 'Qualified' : 'Nurturing',
// Activity
Description: `Call Summary: ${call.summary}\n\nOutcomes: ${JSON.stringify(outcomes)}`
});
// Create task for next action
if (outcomes.next_action) {
await salesforce.sobject('Task').create({
WhoId: salesforceId,
Subject: outcomes.next_action,
Status: 'Not Started',
Priority: outcomes.interested ? 'High' : 'Normal',
ActivityDate: outcomes.appointment_date || new Date()
});
}
res.status(200).json({ success: true });
});
Generic CRM Update Example
app.post('/webhook/callab-outcomes', async (req, res) => {
const { contact, call, outcomes, event } = req.body;
try {
// Get CRM ID from contact
const crmId = contact.custom_fields.crm_id;
// Build update payload
const updateData = {
// Contact info (in case changed during call)
first_name: contact.first_name,
last_name: contact.last_name,
email: contact.email,
// Call metadata
last_contacted: call.end_time,
last_call_duration: call.duration,
total_calls: contact.custom_fields.total_calls + 1,
// Outcomes
...outcomes,
// Call content
last_call_transcript: call.transcript,
last_call_recording: call.recording_url
};
// Update CRM via API
await axios.patch(
`https://your-crm-api.com/contacts/${crmId}`,
updateData,
{
headers: {
'Authorization': `Bearer ${CRM_API_KEY}`,
'Content-Type': 'application/json'
}
}
);
// Log success
console.log(`Updated CRM contact ${crmId} from call ${call.id}`);
// Acknowledge webhook
res.status(200).json({
success: true,
contact_id: crmId
});
} catch (error) {
console.error('Webhook processing error:', error);
// Return error but acknowledge receipt
// (prevents retry storms)
res.status(200).json({
success: false,
error: error.message
});
}
});
Webhook Security
Verify Webhook Authenticity:
User should verify webhooks come from Callab:
1. HTTPS Only:
- Always use HTTPS endpoint
- Never accept HTTP webhooks
- Ensures encrypted transmission
2. Webhook Signature Verification:
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(JSON.stringify(payload)).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(digest)
);
}
app.post('/webhook', (req, res) => {
const signature = req.headers['x-callab-signature'];
const isValid = verifyWebhookSignature(
req.body,
signature,
WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process webhook...
});
3. IP Whitelisting:
- Restrict webhook endpoint to Callab IPs
- Contact support for current IP ranges
- Configure firewall rules
4. Rate Limiting:
- Implement rate limiting
- Prevent abuse
- Set reasonable limits
Webhook Best Practices
Implementation:
- Respond quickly (within 5 seconds)
- Return 200 status immediately
- Process data asynchronously
- Don’t wait for CRM API calls
- Use job queue for processing
Error Handling:
- Always return 200 (even on error)
- Log errors for investigation
- Implement retry logic
- Handle missing fields gracefully
- Validate data before processing
Example Async Processing:
const queue = require('bull');
const webhookQueue = new queue('webhook-processing');
// Webhook endpoint - respond immediately
app.post('/webhook', async (req, res) => {
// Add to queue
await webhookQueue.add(req.body);
// Respond immediately
res.status(200).json({ received: true });
});
// Process queue asynchronously
webhookQueue.process(async (job) => {
const { contact, call, outcomes } = job.data;
// Update CRM (can take time)
await updateCRM(contact, call, outcomes);
return { success: true };
});
Data Validation:
function validateWebhookPayload(payload) {
const required = ['event', 'timestamp', 'contact', 'call'];
for (const field of required) {
if (!payload[field]) {
throw new Error(`Missing required field: ${field}`);
}
}
// Validate contact has CRM ID
if (!payload.contact.custom_fields?.crm_id) {
throw new Error('Contact missing CRM ID');
}
return true;
}
Monitoring:
- Log all webhook receipts
- Track processing success rate
- Monitor CRM update failures
- Alert on errors
- Review webhook latency
Complete Integration Flow
Bi-directional CRM Sync:
Import Contacts (Integration):
1. Integration runs every 5 minutes
2. Fetches new leads from CRM
3. Creates contacts in Callab
4. Adds CRM ID to custom fields
5. Campaign picks up contacts
Call Contacts (Campaign):
1. Outbound campaign runs
2. AI agent calls imported contacts
3. Has conversation
4. Extracts call outcomes
5. Call completes
Update CRM (Webhook):
1. Call outcome extracted
2. Webhook triggered
3. Data sent to your endpoint
4. Endpoint updates CRM
5. Lead status synced
Complete Flow:
CRM New Lead Created
↓
Integration Imports to Callab
↓
Campaign Calls Contact
↓
AI Has Conversation
↓
Call Outcomes Extracted
↓
Webhook Sends to Your API
↓
Your API Updates CRM
↓
Lead Status Updated in CRM
↓
(Repeat for next lead)
Outcome Mapping Examples
Lead Qualification:
function mapOutcomesToCRMStatus(outcomes) {
// Qualified lead
if (outcomes.interested && outcomes.budget && outcomes.decision_timeline) {
return {
status: 'Qualified',
priority: 'High',
next_step: 'Schedule demo'
};
}
// Interested but not ready
if (outcomes.interested && !outcomes.decision_timeline) {
return {
status: 'Nurturing',
priority: 'Medium',
next_step: 'Follow up in 30 days'
};
}
// Not interested
if (!outcomes.interested) {
return {
status: 'Disqualified',
priority: 'Low',
next_step: 'Archive'
};
}
// No clear outcome
return {
status: 'Attempted',
priority: 'Medium',
next_step: 'Retry call'
};
}
Appointment Tracking:
function syncAppointment(outcomes, contact) {
if (outcomes.appointment_booked) {
return {
calendar_event: {
title: `Demo with ${contact.first_name} ${contact.last_name}`,
start: outcomes.appointment_date,
duration: 60,
attendees: [contact.email],
description: outcomes.appointment_notes
},
crm_update: {
status: 'Meeting Scheduled',
meeting_date: outcomes.appointment_date
}
};
}
return null;
}
Troubleshooting
Webhook not firing:
Endpoint not receiving data:
CRM not updating:
Timeout errors:
Data validation errors:
Monitoring and Logging
Webhook Dashboard:
User can monitor webhooks in Callab dashboard:
Metrics Available:
- Total webhook deliveries
- Successful deliveries
- Failed deliveries
- Average response time
- Retry attempts
Webhook Logs:
- Request timestamp
- Response status
- Response time
- Payload sent
- Error messages
- Retry history
Server-Side Logging:
const logger = require('winston');
app.post('/webhook', async (req, res) => {
const startTime = Date.now();
try {
// Log receipt
logger.info('Webhook received', {
event: req.body.event,
call_id: req.body.call.id,
contact_id: req.body.contact.id
});
// Process webhook
await processWebhook(req.body);
// Log success
const duration = Date.now() - startTime;
logger.info('Webhook processed successfully', {
call_id: req.body.call.id,
duration_ms: duration
});
res.status(200).json({ success: true });
} catch (error) {
// Log error
logger.error('Webhook processing failed', {
call_id: req.body.call?.id,
error: error.message,
stack: error.stack
});
// Still return 200 to prevent retries
res.status(200).json({
success: false,
error: error.message
});
}
});
Next Steps
After setting up contact auto-update:
- Test Webhook - Make test call and verify CRM updates
- Monitor Logs - Check webhook delivery logs
- Verify CRM - Confirm data appears correctly in CRM
- Optimize Mapping - Refine outcome field mappings
- Add Automation - Create CRM workflows based on outcomes
Start with “Call Outcome Extracted” event as it contains all call data plus extracted outcomes in a single webhook.
Important: Always Respond with 200Your webhook endpoint must respond with 200 status code within 5 seconds, even if processing fails. Process data asynchronously to avoid timeouts.