Skip to main content
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.

What is Contact Auto Update?

Contact auto-update automatically syncs call data back to external systems: How It Works:
  1. AI agent completes call
  2. Call outcomes extracted
  3. Webhook triggered automatically
  4. Data sent to external API
  5. 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.

Contact Auto-Update Flow

Create Webhook

Webhooks > create new webhook > step 1 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"
  }
}

Update CRM Contact Example

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:
  • Is webhook active (not paused)?
  • Are correct events selected?
  • Is campaign using webhook?
  • Check webhook logs in dashboard
  • Verify call completed successfully
Endpoint not receiving data:
  • Is webhook URL correct (HTTPS)?
  • Is endpoint publicly accessible?
  • Check firewall/security rules
  • Verify endpoint returns 200
  • Review server logs
CRM not updating:
  • Is CRM API key valid?
  • Does contact have CRM ID?
  • Check CRM API rate limits
  • Verify field mapping correct
  • Review CRM API logs
Timeout errors:
  • Respond to webhook within 5 seconds
  • Process data asynchronously
  • Use job queue for heavy operations
  • Don’t wait for CRM API in webhook handler
  • Return 200 immediately
Data validation errors:
  • Handle missing fields gracefully
  • Check outcome field names match
  • Verify data types correct
  • Validate before sending to CRM
  • Log 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:
  1. Test Webhook - Make test call and verify CRM updates
  2. Monitor Logs - Check webhook delivery logs
  3. Verify CRM - Confirm data appears correctly in CRM
  4. Optimize Mapping - Refine outcome field mappings
  5. 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.