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.
- Capture call metadata from the telephony provider via webhook.
- Validate the payload and deduplicate on
call_id. - Normalize the phone number to E.164 format.
- Match the call to a CRM contact by normalized phone number.
- Create the call activity record in HubSpot or Salesforce.
- 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.
- 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.
- Deduplicate On
call_id. Before any CRM write, check whether thiscall_idhas already been processed. The correct implementation uses an atomic database operation. In PostgreSQL, useINSERT INTO processed_calls (call_id, processed_at) VALUES ($1, NOW()) ON CONFLICT (call_id) DO NOTHING RETURNING xmax. Ifxmax = 0, the row is new. Ifxmax != 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. - Normalize To E.164. Phone numbers arrive from dialers in many formats such as
(212) 555-1234,212-555-1234, or12125551234. 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), orlibphonenumber-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. - 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.
- 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.
- Attach Recording And Create Follow-Up Task. Write the
recording_urlto 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.
- A Stable Dedupe Key. Use the provider-issued
call_id. Twilio’s CallSid is a 34-character SID with theCAprefix, while RingCentral’s call log record identifier appears in theidfield. 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. - An Atomic Claim Operation. Write the dedupe record before any CRM write using
INSERT ... ON CONFLICT (call_id) DO NOTHINGin PostgreSQL orSET key 1 NX EX 604800in 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. - 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.
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.
- Data Presence: Every completed call appears as an activity record in the CRM within a defined SLA. For a business-critical (P0) call logging pipeline, the target freshness SLA is under 5 minutes.
- Dedupe Integrity: No contact has two call records with the same
call_id. Run a weekly query such asSELECT call_id, COUNT(*) FROM call_activities GROUP BY call_id HAVING COUNT(*) > 1. - Contact Match Rate: Track the percentage of calls successfully matched to a CRM contact. A contact match rate significantly below identifier-specific benchmarks (for example, 50–70% for postal-to-phone matches) usually indicates a data hygiene or formatting issue rather than an identity graph coverage gap.
- Recording Attachment Rate: Verify that
recording_urlis populated on completed call records. A gap here often means the recording webhook fires before the recording is available. - Follow-Up Task Creation: Confirm that disposition-triggered tasks appear in the CRM and are assigned to the correct owner.
- Adoption Signal: If reps stop manually logging calls after the pipeline goes live, the automation is working. If they continue logging manually, the pipeline has a gap.
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.


