CRM Tracking Script For Call Logging: 2026 Webhook Guide

CRM Call Tracking Scripts: Automate Sales Logging

Content

Written by: Doug Camplejohn, CEO & Co-Founder, Coffee | Last updated: September 11, 2026

What A CRM Call Logging Script Does

A CRM call logging script runs a webhook-driven pipeline. The telephony provider fires call events, your service receives them, and the CRM matches the phone number to a contact. The script normalizes phone numbers to E.164 to avoid format drift and matching failures. It then creates a Call activity record, deduplicates on a stable external call ID (for example, hs_call_external_id) so webhook retries update instead of duplicating, and triggers follow-up tasks automatically without manual data entry.

Key Takeaways

  • A CRM call logging script runs a webhook pipeline that captures telephony events, normalizes phone numbers to E.164, deduplicates on a stable call_id, and creates activity records in HubSpot or Salesforce.
  • Production webhook handlers verify HMAC signatures, deduplicate atomically before any CRM write, and use upsert operations keyed on an external ID to prevent duplicate records.
  • HubSpot models calls as engagement objects while Salesforce uses Task records with Type set to Call. Each CRM needs specific field mappings for timestamps, direction, duration, disposition, and recording URLs.
  • Third-party dialers such as Twilio, RingCentral, Aircall, and Dialpad require webhook integrations because native CRM dialers only capture calls made inside the CRM UI.
  • Teams that want to eliminate webhook maintenance can See how Coffee handles call logging and let an autonomous agent manage call logging, data enrichment, and CRM updates.

How CRM Call Logging Works End To End

A production call logging pipeline follows six discrete steps.

  1. Capture call metadata from the telephony provider via webhook.
  2. Validate the payload and deduplicate on call_id.
  3. Normalize the phone number to E.164 format.
  4. Match the call to a CRM contact by normalized phone number.
  5. Create the call activity record in HubSpot or Salesforce.
  6. Attach the recording URL and create a follow-up task.

The rest of this guide walks through each step in detail, starting with the payload that triggers the pipeline.

Inside A CRM Call Logging Payload

The telephony provider fires a POST request to your webhook endpoint the moment a call ends. The payload contains everything needed to create a CRM activity record. Below is a representative, annotated JSON payload based on the field conventions used by providers such as Twilio and RingCentral.

{ "call_id": "CA1234567890abcdef1234567890abcdef", // Stable unique ID — your dedupe key "session_id": "sess_abc123", // Provider session reference "direction": "inbound", // "inbound" or "outbound" "from": "+12125551234", // Caller E.164 number "to": "+18005559876", // Callee E.164 number "start_time": "2026-09-11T14:00:00Z", // ISO 8601 UTC "end_time": "2026-09-11T14:07:43Z", // ISO 8601 UTC "duration_seconds": 463, // Integer seconds "disposition": "answered", // Outcome label from dialer "recording_url": "https://cdn.provider.com/recordings/CA1234.mp3", "agent_id": "agent_007", // Internal rep identifier "account_id": "acct_xyz" // Your platform account }

The call_id field is the single most important value in this payload. Every production webhook system operates on at-least-once delivery. Exactly-once delivery does not exist in real distributed systems, so handlers must safely receive the same event multiple times. The call_id makes that safe.

The Webhook Processing Flow: From Payload To CRM Record

Once the payload arrives, the pipeline runs in a strict sequence that protects data quality.

  1. Verify And Acknowledge. Validate the HMAC signature against the raw request body before parsing JSON, then persist the raw payload to a durable store and return HTTP 200 immediately. Provider timeouts are typically 5–10 seconds, so a slow synchronous handler often causes duplicate deliveries when the provider retries.
  2. Deduplicate On call_id. Before any CRM write, check whether this call_id has already been processed. The correct implementation uses an atomic database operation. In PostgreSQL, use INSERT INTO processed_calls (call_id, processed_at) VALUES ($1, NOW()) ON CONFLICT (call_id) DO NOTHING RETURNING xmax. If xmax = 0, the row is new. If xmax != 0, it is a duplicate, so return 200 and stop. A 7-day TTL on dedupe keys comfortably exceeds Twilio’s 4-hour retry window and Stripe’s 3-day window while bounding table growth.
  3. Normalize To E.164. Phone numbers arrive from dialers in many formats such as (212) 555-1234, 212-555-1234, or 12125551234. E.164 gives one representation that means the same thing in a CRM, a billing record, a SIP INVITE, and a carrier interconnect. Use Google’s libphonenumber (Java), phonenumbers (Python), libphonenumber-js (JavaScript), or libphonenumber-for-php (PHP) to parse and reformat. Always specify a default region when parsing numbers without a country code prefix. The output should match the structural regex ^\+[1-9]\d{1,14}$, which validates format only.
  4. Match To A CRM Contact. Query the CRM by normalized phone number. HubSpot contact records expose phone numbers in a non-E.164 format, so normalize both the stored value and the inbound number before comparison. If no match appears, create a stub contact or route the event to a dead-letter queue for manual review.
  5. Create The Activity Record. Write the call activity to the CRM using the field mappings in the next section. Use upsert operations keyed on an external ID wherever the CRM API supports that pattern.
  6. Attach Recording And Create Follow-Up Task. Write the recording_url to the activity record and, based on disposition logic, create a follow-up task. Without a webhook integration, follow-up tasks are forgotten roughly 40% of the time.

Field Mapping For HubSpot And Salesforce Call Records

The two CRMs model call activities differently. HubSpot uses a dedicated engagement object, while Salesforce uses a generic Task record with a Type of call. The table below shows that the same call attribute often needs a different field name and data type in each CRM, so a single mapping cannot serve both.

Attribute HubSpot Calls API Field Salesforce Task/Activity Field
Timestamp / Start Time hs_timestamp (required; epoch-ms or ISO 8601) ActivityDate (date) + CompletedDateTime (datetime)
Call Direction hs_call_direction (INBOUND / OUTBOUND) Type (for example, Call) + Description for direction detail
Duration hs_call_duration (milliseconds, as string) Description or a custom field (no native duration field on Task)
Disposition / Outcome hs_call_disposition CallDisposition
Recording URL hs_call_recording_url Description or a custom field (for example, Recording_URL__c)
Contact Association associations parameter (inline, links to contact vid) WhoId (Contact or Lead ID)
Related Record (Account/Deal) associations (link to deal or company object) WhatId (Account, Opportunity, and similar)
Subject / Title hs_call_title Subject (required)
Status hs_call_status (for example, COMPLETED) Status (for example, Completed)
Owner / Assigned Rep hubspot_owner_id OwnerId

Salesforce assigns every field both a user-facing label and an internal API Name, so webhook implementations must reference exact API names, not display labels. All HubSpot property values must be passed as strings, including numeric fields such as duration.

How Salesforce Tracks Calls Through Tasks

Salesforce does not expose a dedicated call object. Calls appear as Task records with Type = 'Call'. The native Salesforce dialer (Sales Dialer, formerly Lightning Dialer) automatically creates a VoiceCall object record when a rep completes a call inside the Salesforce UI. A Task record with TaskSubtype set to Call appears only when a rep manually uses the Log a Call action. For teams using a third-party telephony provider such as Twilio, RingCentral, Aircall, or Dialpad, the Task must be created through the REST API.

The API path is a POST to /services/data/vXX.X/sobjects/Task/. Among the commonly listed fields, only Status is documented as Required. Subject, WhoId (the Contact or Lead), WhatId (the Account or Opportunity), OwnerId, ActivityDate, and CallDisposition are all nillable. To prevent duplicate Task records, add a custom external ID field such as Call_ID__c and use the upsert endpoint PATCH /services/data/vXX.X/sobjects/Task/Call_ID__c/{call_id}. Salesforce REST API supports upserting records using an external ID endpoint. This pattern creates the record if it does not exist and updates it if it does, using a single idempotent operation.

How To Log Calls In HubSpot With A Webhook

HubSpot models calls as engagement objects, distinct from contacts, deals, and companies. The endpoint to create a call engagement is POST /crm/v3/objects/calls. The required parameters are objectType (set to calls) and properties (a key-value map of string values), with hs_timestamp mandatory for all engagement objects.

To associate the call with a contact in a single API call, include the associations parameter with the contact’s vid. HubSpot supports atomic inline associations, so a new call can link to existing contacts without a separate association API call.

To find the contact by phone number, use the HubSpot contact search endpoint with the normalized E.164 number as the query parameter. As noted earlier, HubSpot stores phone numbers in non-E.164 format, so normalize both sides before comparison. The contact search endpoint accepts any one of the OAuth scopes content, crm.objects.contacts.read, or crm.objects.contacts.write.

How To Prevent Duplicate Call Records In Your CRM

Webhook providers will always send duplicates, so your system must avoid processing them twice.

The correct deduplication architecture has three components.

  1. A Stable Dedupe Key. Use the provider-issued call_id. Twilio’s CallSid is a 34-character SID with the CA prefix, while RingCentral’s call log record identifier appears in the id field. Deduplicate on the provider’s stable event identifier rather than hashing the payload body, because timestamps, retry counters, and nondeterministic field ordering can shift between delivery attempts.
  2. An Atomic Claim Operation. Write the dedupe record before any CRM write using INSERT ... ON CONFLICT (call_id) DO NOTHING in PostgreSQL or SET key 1 NX EX 604800 in Redis (7-day TTL). A naive check-then-act pattern creates a race condition because two concurrent deliveries can both pass the check and both write to the CRM.
  3. Idempotent CRM Writes. Use upsert operations keyed on an external ID field instead of create-only calls. Salesforce supports upserting records using a REST PATCH to an external ID endpoint. HubSpot supports upsert through PATCH /crm/v3/objects/contacts/{email}?idProperty=email.

Without idempotency, a single duplicated webhook can create the same lead twice, assign two sales reps the same task, inflate CRM dashboards, or cause an AI agent to summarize duplicated activity history.

Native CRM Call Logging And Third-Party Call Tracking

Native CRM dialers such as Salesforce Sales Dialer and HubSpot Calling create activity records automatically when a rep completes a call inside the CRM UI. They work well for teams whose reps live entirely inside one CRM, handle a manageable call volume, and do not need custom disposition logic or cross-platform recording storage.

Third-party call tracking through webhooks becomes necessary in several situations.

  • The team uses a dedicated dialer such as Twilio, RingCentral, Aircall, or Dialpad that operates outside the CRM UI.
  • Call data must flow into multiple systems at the same time.
  • The team needs custom disposition mapping, recording attachment, or follow-up task logic.
  • The team wants call data in a data warehouse alongside CRM records.

Manual call logging captures only approximately 58–60% of calls, and only 12% of recordings reach the CRM. The follow-up task gap mentioned earlier compounds the problem. Webhook integrations reach 99–100% logging and recording rates.

Build Vs Buy: Webhook Script Or Agent-Led CRM

A custom webhook pipeline works well for a team with dedicated engineering capacity, a single telephony provider, and a stable CRM configuration. The team can build it once, test it thoroughly, and run it in production.

The maintenance burden grows quickly. Telephony providers update their payload schemas. HubSpot and Salesforce release API versions that deprecate field names. Dedupe TTLs need tuning as retry windows change. Compliance requirements evolve, and California law (cited as AB 2013 or AB 2905 in different sources) now requires businesses using AI to analyze calls to disclose that AI is being used. Each change can trigger a production incident.

The build-versus-buy decision shifts when the team is small, when engineering time is better spent on the product, or when the CRM data quality problem extends beyond call logging to emails, meetings, and pipeline tracking. A custom webhook script solves one data stream. The underlying problem remains: legacy CRMs rely on humans to ensure data quality, and that reliance creates the gap.

Coffee’s Agent addresses this at the system level. Coffee deploys an autonomous agent that handles data entry, unifies structured and unstructured data such as call transcripts, emails, and calendar events, and logs all activity automatically. Coffee works as a standalone CRM for small to mid-sized teams or as a Companion App that sits on top of an existing Salesforce or HubSpot instance, writing enriched, accurate data back to the system of record without human effort.

The agent handles automatic contact creation, activity logging, meeting briefings, post-call summaries, and follow-up drafts. In doing so, it replaces the webhook pipeline, the enrichment tool, the recording intelligence layer, and the manual follow-up process, all under a single seat-based subscription with simple metering.

Stop maintaining webhook infrastructure — see Coffee’s pricing.

Validation And Success Criteria For Call Logging

A call logging pipeline works correctly when it passes all of the following checks.

Frequently Asked Questions

How Difficult Is It To Set Up A CRM Call Logging Webhook?

The initial setup involves registering a webhook endpoint with a telephony provider, parsing the payload, and writing a call record to HubSpot or Salesforce. This typically takes 30 to 60 minutes for a first deployment and fits within an hour for an engineer familiar with REST APIs. The real complexity appears in production hardening. You need HMAC signature verification, E.164 normalization, idempotent dedupe logic, contact matching, error classification, dead-letter queuing, and monitoring. A pipeline that works in testing will encounter edge cases in production such as provider payload schema changes, CRM API rate limits, and phone numbers that do not match any contact. Each scenario needs a deliberate handling strategy, or the integration becomes fragile and requires regular manual intervention.

Who Owns The Maintenance Of A Custom Call Logging Script?

Ownership usually falls to whoever built the pipeline, often a RevOps engineer or a sales engineer who has moved on to other priorities. Custom webhook pipelines rarely include documentation detailed enough for a new owner to maintain them confidently. When the telephony provider updates its API, when HubSpot deprecates a field, or when a new compliance requirement appears, the pipeline can break silently. The CRM stops receiving call data, nobody notices for days or weeks, and the data gap becomes permanent. Assign explicit ownership, maintain runbooks, and set up monitoring alerts for queue depth and dedupe hit rate for any pipeline that must run reliably in production.

How Does The Process Change As My Sales Team Grows?

At small scale, under roughly 10,000 events per day, a simple webhook pipeline with a single server, a single worker, and a database that includes a dedupe table is sufficient. As the team grows, several things break simultaneously. Call volume exceeds the worker’s throughput, which causes queue backlog and delayed CRM updates. Multiple telephony providers enter the stack, each with different payload schemas. The contact matching logic also becomes more complex as the CRM accumulates duplicate and merged records.

The architecture then needs a queued, horizontally scalable worker pattern with per-provider payload normalization and a more sophisticated contact resolution strategy. At that point, the engineering cost of maintaining the pipeline often exceeds the cost of a purpose-built solution. Teams that start with a managed agent such as Coffee avoid this scaling cliff because the agent’s capacity scales with the team without infrastructure changes.

Conclusion

Building a CRM call logging script is a real engineering task. It requires a webhook pipeline with HMAC verification, E.164 normalization through libphonenumber, atomic dedupe on call_id, per-CRM field mapping, idempotent upsert operations, and careful handling of recording data. The implementation details in this guide, including the annotated payload, the field mapping table, and the dedupe pattern, fill in the gaps that vendor pages usually skip.

For teams that want the outcome without the ongoing maintenance burden, Coffee’s Agent handles the entire process automatically. It captures call data, logs activity, unifies structured and unstructured data, and writes accurate records back to HubSpot or Salesforce. Coffee maintains the webhook infrastructure, handles deduplication, and keeps compliance gaps closed.

See Coffee’s pricing and start closing deals.

Read Next