A call ends in Twilio and the contact record it belongs to never updates. Nobody notices straight away, because the rep has already moved to the next call, and the missing note only surfaces weeks later when a manager pulls the account history and finds a gap. This guide sets out how to wire n8n between HubSpot and Twilio so that stops happening: the credentials and scopes you actually need, the shape of the workflow itself, the branch most implementations skip, where it breaks in production, and how it compares to HubSpot’s own connector and to Zapier or Make.
Why Telephony Data Breaks Down Between Twilio and HubSpot
Twilio and HubSpot were built around different data models, and nothing in either product bridges the gap by default. Twilio thinks in call objects: a CallSid, raw From and To numbers as dialled, a duration in seconds, and a recording that lives behind an authenticated URL. HubSpot thinks in engagements: a typed record attached to a contact, company, or deal, with a disposition and an owner. A completed call doesn’t become a logged engagement on its own; something has to translate one shape into the other, on every call, without exception.
When that translation is left to a person, the person becomes the bottleneck. Calls made from a mobile line outside a softphone never touch any logging tool at all. When it’s skipped under pressure, pipeline reporting quietly loses the call-touch data that would have explained why a deal moved, or didn’t. The recording itself usually lives in a completely separate storage tier from the CRM note that references it, so even a diligent rep logging manually is often just typing a summary and hoping the audio stays findable later. An automation layer sitting between the two systems removes the person from that chain entirely, which is the point of everything that follows.
What You Need Before You Build This
Three things need to exist before the first node goes down: a HubSpot private app with the right scopes, a Twilio number configured to fire the right webhook, and a decision about where n8n itself will run.
HubSpot Scopes and the Private App
Use a private app rather than a personal OAuth token. A private app’s scopes are fixed and visible in the settings screen, whereas a personal token inherits whatever access the underlying user account has, which changes silently if that person’s permissions change. Grant only what the workflow touches: contact read and write, and the calls engagement scope. Check the exact current scope names in HubSpot’s own reference before building, since HubSpot has renamed and split scopes across API versions; the HubSpot developer documentation is the source of truth, not a screenshot from an older tutorial.
Twilio Numbers and Webhook Configuration
Each Twilio number has separate webhook settings for an inbound call arriving and for a status callback firing when a call finishes. Use the status callback, not the inbound webhook, as the trigger for CRM logging: the inbound webhook fires before the call has a duration or a recording, so anything built on it has to guess at outcome data that doesn’t exist yet. The status callback fires once, after the call ends, with the full record. Configuration happens under the number’s console settings, documented in Twilio’s own docs. For authentication, generate a scoped API Key and Secret rather than using the account’s master Auth Token in the n8n credential. The Auth Token has full access to the entire Twilio account and cannot be selectively revoked; a scoped key can be rotated or killed without touching anything else that depends on the account.
Choosing Where n8n Runs
Self-hosting gives control over data residency and network egress, which matters if the business has a position on where UK customer data physically transits, but it also means owning uptime and patching for a system that a live sales floor now depends on. n8n Cloud removes that operational burden, at the cost of Twilio’s webhook traffic passing through a third-party host before it reaches HubSpot, which is worth a specific line in any data processing review rather than an assumption. For anything beyond a handful of lines, check n8n’s own guidance on queue mode and multiple workers at docs.n8n.io before going live, because a single-instance setup queues webhooks in memory and will fall behind under a real call-centre load.
How the n8n Workflow Actually Works
The Webhook Payload and What to Do With It
Twilio’s status callback arrives as form-encoded data, not JSON. A common early mistake is setting the Webhook node to expect a JSON body and then staring at an empty payload, when the fields (CallSid, From, To, CallStatus, CallDuration, RecordingUrl, Direction) are there but under a different content type. Once the raw fields are captured, cast CallDuration from a string to a number and rebuild both the From and To numbers into E.164 format before anything downstream touches them. Twilio doesn’t always send inbound and outbound legs formatted identically, and every match against HubSpot depends on that number being consistent.
Matching Calls to Existing Contacts
Search HubSpot for a contact where the phone property equals the normalised number, rather than relying on HubSpot’s built-in “create or update” upsert behaviour by phone alone. Upsert-by-phone only reliably matches against the primary phone field, and will happily create a duplicate contact for someone whose number sits in a secondary phone property or is stored in a slightly different format. If the primary-field search comes back empty, run a second search against the secondary phone field before falling back to “no match”: that second check catches a meaningful share of the false negatives that would otherwise generate an unnecessary stub contact.
Logging the Call as an Engagement
Create the engagement with type CALL, store the recording as a link property rather than pulling the audio file itself into HubSpot, and set the disposition (completed, no answer, busy) from Twilio’s CallStatus field. Associate the engagement with both the contact and, where an open deal exists for that contact, the deal itself, not just the contact. Logging only at contact level means a rep reviewing an opportunity has to leave the deal record and go hunting through the contact timeline to see whether a call happened at all, which defeats a large part of the point of automating this in the first place.
Building the Workflow Step by Step
Laid out end to end, the workflow has six stages:
1. A Webhook node listens for Twilio’s status callback on the number’s configured URL.
2. A Set node normalises the From and To numbers to E.164 and casts duration to a number.
3. A HubSpot node searches for a contact by the normalised phone number, with the secondary-field fallback described above.
4. An IF node branches on whether that search returned a match.
5. On the matched branch, the workflow creates the call engagement directly and associates it with the existing contact and any open deal. On the unmatched branch, it creates a minimal contact stub from the phone number alone, logs the call against that stub, and posts a Slack notification so a rep can enrich the record by hand.
6. Every HubSpot-facing node sits behind error handling, either an Error Trigger workflow or an IF node checking the HTTP status of the previous call, that routes failures to a dedicated alert channel instead of letting the execution stop with no trace.
Step 5’s unmatched branch is the one most implementations treat as an afterthought or skip entirely, and it’s the branch that decides whether the whole workflow degrades gracefully under a busy day or just quietly drops calls from numbers the CRM has never seen.
Handling SMS Alongside Voice
Twilio’s SMS status callback carries an overlapping but different field set: a MessageSid instead of a CallSid, and a message Body instead of a duration and recording link. Add a Switch node right after the webhook that routes based on which callback fired, then reuse the same normalisation and contact-matching branch already built for voice rather than duplicating that logic for SMS. Log the message as its own engagement type instead of reusing the CALL type. Mixing voice and text under one engagement type means any report filtering by call volume silently includes text message counts too, which produces a number nobody can trust once someone asks where it came from.
Common Failure Modes and How to Diagnose Them
Token Expiry and Silent Scope Loss
HubSpot private app tokens don’t expire on a schedule the way OAuth access tokens do, but their scopes can be edited or narrowed in the app’s settings by anyone with admin access, and the workflow will keep executing with no obvious error until logging simply stops working. Build a scheduled health-check workflow, separate from the main sync, that makes one lightweight authenticated call each morning and alerts if it fails. That catches the gap the same day, rather than three weeks later when someone notices a hole in the pipeline reports.
Duplicate Contacts and Number Normalisation
Duplicates usually trace back to number formatting: a number with or without a country code, an extension appended by an internal switchboard, or a shared office line that legitimately belongs to several contacts. Phone-based matching alone can’t fully resolve that. Where an email address is available on the Twilio side, for instance from a linked click-to-call widget on a form, use it as a secondary match signal rather than relying on phone number matching by itself. Accept that some duplicates will still occur, and run a periodic dedupe pass using HubSpot’s own merge tooling instead of trying to build a perfect filter at ingestion.
Webhook Floods During Peak Call Volume
A busy contact centre can generate status callbacks faster than a single n8n instance and HubSpot’s API rate limits can absorb, producing 429 responses on the HubSpot node. Switching to n8n’s queue mode with multiple workers spreads that load instead of processing everything on one thread, and pairing it with a retry-with-backoff pattern on the HubSpot request handles the rate-limit responses that still occur. A flat delay node on every execution doesn’t fix this; it just moves the backlog further down the queue without addressing the underlying throughput mismatch. n8n’s own documentation covers scaling and queue mode configuration at docs.n8n.io.
Call Recordings and Data Protection
Keep the audio file itself in Twilio’s own storage, or an encrypted bucket the business controls, and pass HubSpot only a link to it. A CRM contact record isn’t built to be a compliant audio archive, and access to that link should follow the same permission model as anything else attached to the contact. UK teams recording customer calls need a lawful basis for doing so and a defined retention period; the ICO’s guidance for organisations is the right starting reference for that, not an assumption carried over from a previous employer’s policy. Twilio also allows recording to be disabled per number or per call leg, which is worth applying to any line that might take card details or other data that shouldn’t be captured as audio at all.
n8n Versus HubSpot’s Native Connector Versus Zapier and Make
HubSpot’s own app marketplace connectors are the fastest route to something working: they cover the basic case of logging a call, and sometimes a transcription, without building anything. Their limitation is that the field mapping and trigger conditions are fixed by whoever built the app. There’s no way to add the unmatched-contact branch, a Slack alert on failure, or deal-level association through the marketplace app’s settings screen; anything beyond what the vendor designed for means leaving the tool entirely.
Zapier and Make sit between the native connector and a custom build. Both offer wide app support and a visual builder, but they’re priced and rate-limited per task or operation, which gets expensive quickly at contact-centre call volumes, and their branching logic tends to be shallow once a workflow needs more than two or three conditional paths. n8n’s tradeoff runs the other way: more upfront engineering time, and if self-hosted, ongoing responsibility for the instance it runs on, in exchange for no per-execution billing ceiling and full control over retries, error routing, and the unmatched-number branch described earlier in this guide. For a team running a small number of lines at low volume, the native connector is a reasonable starting point. The custom workflow earns its complexity once call volume or branching requirements outgrow what a prebuilt app was designed to handle.
Equanax has recorded an 86 percent reduction in fixable sync errors across its CRM integration work.
Related Reading
How long does connecting HubSpot and Twilio through n8n take to set up?
Longer than the node count suggests. Building the webhook and mapping logic is quick, but confirming HubSpot scopes, provisioning a scoped Twilio API key, and running enough test calls to trust the contact-matching branch usually takes more calendar time than assembling the workflow itself.
Can the same workflow handle SMS as well as voice calls?
Yes. Twilio’s SMS status callback carries a different field set, including a message body instead of a duration and recording link, so route it through a Switch node early and log it as its own engagement type rather than reusing the call engagement, otherwise voice and text volumes get mixed together in reporting.
Where should call recordings be stored for GDPR compliance?
Keep the audio file in Twilio’s own storage or an encrypted bucket you control, and pass HubSpot only a link to it. HubSpot’s contact record is not designed as a compliant audio archive, and UK teams recording calls need a lawful basis and a retention policy under the ICO’s guidance for organisations.
What happens when a call comes from a number with no matching HubSpot contact?
The IF node routes it down the unmatched branch, which creates a minimal contact stub from the phone number, logs the call against it, and posts a Slack notification so a rep can enrich the record manually rather than the call disappearing from the workflow entirely.
Is n8n better than HubSpot’s native Twilio connector or Zapier?
It depends on volume and how much branching logic you need. HubSpot’s native connector and Zapier both cover the basic log-a-call case with little setup, but neither handles the unmatched-contact branch or custom error handling well, and Zapier’s per-task pricing gets expensive at contact-centre call volumes. n8n earns its extra setup time once you need that branching or hit that volume.
For more on this, see the full HubSpot archive, including Automate HubSpot Contact Creation with n8n Webhooks, Fixing HubSpot and Google Ads Integration: Attribution, Sync, and Revenue Tracking, and Automating Consent Management with n8n, HubSpot & DocuSign.
Leave a Reply