Skip to main content

Connect Heilo to your CRM

Eight steps to connect Heilo to any CRM — using Zapier, Make, or any other tool that supports webhooks.

After every call, Heilo sends a notification with the call details to an address you choose. This guide shows how to connect Heilo to any CRM using Zapier or Make — no code, in 15–20 minutes.

It’s a universal path that works with any CRM supported by Zapier or Make. Dedicated step-by-step recipes for Pipedrive, HubSpot, and Zoho CRM are included below. One-click Zapier/Make templates are still on the roadmap — until then, each recipe walks you through the last step (saving to your CRM) manually.

1Before you start: what the Zapier and Make plans require

The “Webhooks by Zapier” trigger is a premium app and does not run on the Free plan: Free gives 100 tasks a month and two-step Zaps, meaning one trigger and one action. The recipe in this guide has at least three actions after the trigger, so you need Professional or higher; Professional starts at $19.99/month billed annually, or $29.99/month billed monthly, for 750 tasks. Only a successful action spends a task — the trigger, Filter and Formatter steps do not count. Zapier plans and pricing

Make puts no plan gate in front of webhooks — the “Custom webhook” module runs on the Free plan too, which gives up to 1,000 credits a month and two active scenarios. The billing unit in Make is the credit, not the operation; one module run is one credit, and the trigger module counts as well. Do not convert that into a number of calls: after the first live event, open the scenario history and read the credits that one run consumed. Make plans and pricing

We checked the providers' prices and limits on their own pages on 9 September 2026. They change without us, so confirm them at the links above before you decide.

2Create a new Zap in Zapier

The “Webhooks by Zapier (Catch Hook)” trigger generates an address that Heilo will send events to.

  1. Sign in to your Zapier dashboard
  2. Create Zap → Choose Trigger → “Webhooks by Zapier”
  3. Event = “Catch Hook” → Continue
  4. Zapier shows an address like https://hooks.zapier.com/hooks/catch/… — copy it

3Paste the address into Heilo as a webhook subscription

Heilo verifies the address — your Zapier endpoint responds automatically. After activation, every call is sent to this address.

  1. Go back to Settings → Integrations in Heilo
  2. Click “Add subscription”
  3. Paste the Zapier address into the “Endpoint URL” field, choose the event types (completed call + outbound call attempt) and click “Create + handshake”
  4. The status should change to “Active”. We show the signing secret exactly once — save it now, even if you are staying on the basic variant for the moment, because without it you cannot check the signature later

4Send a test event from Heilo

The test event carries exactly the structure of a real completed call, so a mapping built on it keeps working afterwards. One caveat: the recording link in this event is an example, not a file — opening it does not prove that real audio will be reachable.

  1. In Heilo, click the “Test” icon next to your subscription
  2. The event goes through the same delivery queue as any other — if the receiver refuses it, you will see that in Heilo's delivery history

5Read the full data structure

Zapier only offers fields it has actually seen. That is why you read the structure after sending the test rather than before — otherwise half the fields never appear in the list.

  1. In Zapier go back to the trigger step and run “Test trigger”
  2. Check that you can see the call data: caller number, name, summary and the recording link
  3. Continue → move on to the action step (your CRM)

6Set up the save in your CRM

In your tool (Zapier or Make) add actions for your CRM — for example Pipedrive, Zoho CRM, Notion or Airtable. For sales workflows this is usually three steps: (1) find or create a contact by phone number, (2) create or update a lead/deal, (3) attach an activity, task or note.

Before you publish the automation, put a filter on the event_type field. Let the business type you chose through and reject webhook.test and the address confirmation — without that your own tests will create records in the CRM. You may let the test through while mapping; you have to cut it off before publishing. Where exactly the filter goes for each of the three recipes is in the recipes section below.

Heilo fieldMap to CRM
data.call_idID / reference field
data.customer_phone_e164Contact phone (“Phone” / “Primary Phone”)
data.transcript_processed.caller_nameContact name (if Heilo detected it)
data.transcript_processed.summaryNote body or activity description
data.transcript_processed.service_neededActivity subject or deal title
data.transcript_processed.subjectLead/deal title (best fallback for service_needed)
data.transcript_processed.lead_scorePriority, label or qualification filter
data.transcript_processed.preferred_dateFollow-up due date or expected close date
data.transcript_processed.client_cityContact city / address field
data.transcript_processed.client_addressContact street address
data.transcript_processed.services_matchWhether the request matches your offer (true/false) — for qualification
data.transcript_processed.additional_detailsExtra details from the call → note / description
data.recording_urlA URL field (e.g. “Recording link”)
data.durationActivity duration (in seconds)

The data.outbound_lifecycle field appears only in call.outbound.attempted and call.failed events — it is not present in call.completed.

7Publish the automation

Until now everything happened in edit mode and nothing reacted to real calls. Turn the automation on in your tool — in Zapier you publish the Zap, in Make you switch the scenario on. Before you do, make sure the filter from the previous step is saved: publishing without it means the next test event will create something in the CRM as well.

8Check a real call in your CRM

The last step is not a formality. A test event proves the wire is connected, not that the record landed where it belongs. Take or make one real call, wait for it to finish, and open the contact in your CRM. Check three things: the activity exists and there is exactly one of it, it carries the Heilo Call ID, and the summary matches what was actually said. Only that is the integration signed off.

Recipes for specific CRMs

Below are CRM-specific configurations. The generic webhook gives you the call data; this section shows how to turn it into a contact, lead/deal and activity in a specific CRM.

Pipedrive: contact + lead/deal + activity

Target outcome: after a completed call, Pipedrive finds or creates a person, creates a sales lead/deal and attaches an activity with the summary and recording link.

Use Lead if calls should first land in Leads Inbox for qualification. Use Deal if every qualified call should enter the pipeline immediately.

Zapier
  1. Trigger: Webhooks by Zapier → Catch Hook. Keep the Heilo subscription in permissive mode.
  2. Filter by Zapier: allow event_type = call.completed; during setup also allow webhook.test (or filter by data._test = true).
  3. Pipedrive → Find or Create Person. Search by Phone Numbers = data.customer_phone_e164, and set Person Name from caller_name or the phone number.
  4. Pipedrive → Search Activities on your Heilo Call ID field = data.call_id. This step decides whether you update or create below; without it a retry adds a second activity for the same call.
  5. Activity found → Update Activity; nothing found → Create Activity attached to the person. In both cases write Heilo Call ID = data.call_id plus summary, recording_url and transcript_original. No deal is created here — the rule for opportunities is in its own section below.
Make
  1. Trigger: Webhooks → Custom webhook. The scenario must be active and run when data arrives.
  2. Add a filter after the webhook: event_type = call.completed; while mapping test data, also allow webhook.test (or filter by data._test = true).
  3. Pipedrive CRM → Search Persons by data.customer_phone_e164. If there is no result, create a Person with that phone and caller_name or the phone number as the name.
  4. Pipedrive CRM → Search Activities on your Heilo Call ID field = data.call_id. Set the scenario to process one at a time, so two parallel runs do not pass each other on this search.
  5. Router: activity exists → Update an Activity; it does not → Create an Activity attached to the person. Write Heilo Call ID = data.call_id, summary and recording_url. This recipe creates no opportunity.
Heilo fieldPipedrive field / actionUse
event_typeFilter / RouterProcess call.completed; use webhook.test only for field mapping.
data.customer_phone_e164Person → Phone NumbersPrimary field for contact lookup and deduplication.
data.transcript_processed.caller_namePerson → Person NameIf empty, use the phone number as a safe fallback name.
data.transcript_processed.subjectLead/Deal → TitleBest opportunity title; fall back to service_needed.
data.transcript_processed.service_neededLead/Deal → Title or custom fieldInquiry category; useful for pipeline routing or labels.
data.transcript_processed.summaryActivity/Note → Note or Public DescriptionShort call description visible on the opportunity.
data.transcript_processed.lead_scoreLead label / priority / filterFor example, create a lead/deal only from 7/10 or send lower scores to review.
data.transcript_processed.preferred_dateExpected close date or activity due dateUse when the customer mentioned a deadline or preferred follow-up date.
data.transcript_processed.client_cityPerson → Address (city) or custom fieldCustomer city; handy for segmentation or a regional pipeline.
data.transcript_processed.client_addressPerson/Organization → AddressFill the person/organization address when the caller provided it.
data.transcript_processed.services_matchFilter / Lead labelfalse = request outside your offer; skip the deal or flag it for rejection.
data.transcript_processed.additional_detailsActivity/Note → NoteExtra details from the call; append them to the activity body.
data.recording_urlActivity/Note → Recording linkPlayback link; may stop working after the call is deleted for GDPR retention.
data.call_idCustom field → Heilo Call IDIdempotency key. Prevents a second CRM record for the same call.
data.durationActivity durationHeilo sends seconds; convert if your Pipedrive field expects a time format.

Conditions for correct pairing

  • Do not turn on strict mode when confirming the address — Zapier and Make webhooks acknowledge receipt but do not echo the challenge field back. This applies only to the one-off address confirmation, not to the signature on ordinary deliveries.
  • Do not mix event types without a filter: call.outbound.attempted does not include the full transcript or recording.
  • Create a Heilo Call ID custom field in Pipedrive and write data.call_id into it. That is what you search on before every write — without it there is nothing to deduplicate against.
  • A withheld number, or no number at all: do not create a person from that value. Route the event to manual handling instead, or every such call lands on one fictitious contact.
HubSpot: contact + deal + note

Target outcome: after a completed call, HubSpot finds or creates a contact by phone number, creates a deal and logs a note with the summary and recording link.

Deduplication trap: Zapier's Create or Update Contact matches existing contacts by e-mail only. Heilo events carry a phone number and often no e-mail, so that action creates a duplicate contact on every call. Always search by the phone property first and create a contact only when the search finds nothing.

Zapier
  1. Trigger: Webhooks by Zapier → Catch Hook. Keep the Heilo subscription in permissive mode.
  2. Filter by Zapier: allow event_type = call.completed; during setup also allow webhook.test (or filter by data._test = true).
  3. HubSpot → Find Contact. Set the search property to Phone Number and map data.customer_phone_e164; enable the option to create the contact when nothing is found, with caller_name or the phone number as the name. Do not use Create or Update Contact here — it matches by e-mail.
  4. HubSpot → Find Engagement on your Heilo Call ID property = data.call_id. Do not enable create-if-missing here; the next step decides that.
  5. Note found → update it; nothing found → Create Engagement (type Note) associated with the contact. Write Heilo Call ID = data.call_id plus summary, recording_url and transcript_original. Create Deal is not part of the default recipe.
Make
  1. Trigger: Webhooks → Custom webhook. The scenario must be active and run when data arrives.
  2. Add a filter after the webhook: event_type = call.completed; while mapping test data, also allow webhook.test (or filter by data._test = true).
  3. HubSpot CRM → Search for Contacts by the phone property = data.customer_phone_e164. If there is no result, create the contact with Create a Contact (that phone plus caller_name or the phone number as the name).
  4. HubSpot CRM → Search for Engagements on the Heilo Call ID property = data.call_id. Set the scenario to process one at a time.
  5. Router: the note exists → update it; it does not → Create an Engagement (type Note) associated with the contact. Write Heilo Call ID = data.call_id, summary and recording_url. This recipe creates no deal.
Heilo fieldHubSpot field / actionUse
event_typeFilter / RouterProcess call.completed; use webhook.test only for field mapping.
data.customer_phone_e164Contact → Phone NumberPrimary lookup key. Store phones in HubSpot in E.164 format — the phone search matches exact values, not formatting variants.
data.transcript_processed.caller_nameContact → First Name / Last NameIf empty, use the phone number as a safe fallback name.
data.transcript_processed.subjectDeal → Deal NameBest opportunity title; fall back to service_needed.
data.transcript_processed.service_neededDeal → Deal Name or custom propertyInquiry category; useful for pipeline routing or labels.
data.transcript_processed.summaryNote → Note bodyShort call description visible on the opportunity.
data.transcript_processed.lead_scoreCustom property / filterFor example, create a lead/deal only from 7/10 or send lower scores to review.
data.transcript_processed.preferred_dateDeal → Close Date or task due dateUse when the customer mentioned a deadline or preferred follow-up date.
data.transcript_processed.client_cityContact → CityCustomer city; handy for segmentation or a regional pipeline.
data.transcript_processed.client_addressContact → Street AddressFill the person/organization address when the caller provided it.
data.transcript_processed.services_matchFilter / deal tagfalse = request outside your offer; skip the deal or flag it for rejection.
data.transcript_processed.additional_detailsNote → Note bodyExtra details from the call; append them to the activity body.
data.recording_urlNote → Recording linkPlayback link; may stop working after the call is deleted for GDPR retention.
data.call_idCustom property → Heilo Call IDIdempotency key. Prevents a second CRM record for the same call.
data.durationCustom property or noteHeilo sends seconds; convert if your HubSpot property expects a different format.

Conditions for correct pairing

  • Do not turn on strict mode when confirming the address — Zapier and Make webhooks acknowledge receipt but do not echo the challenge field back. This applies only to the one-off address confirmation, not to the signature on ordinary deliveries.
  • Do not mix event types without a filter: call.outbound.attempted does not include the full transcript or recording.
  • Do not rely on Create or Update Contact for matching — it deduplicates by e-mail only; always run the phone search first.
  • Add a HubSpot custom property named Heilo Call ID (single-line text) and store data.call_id there to avoid duplicates.
  • A withheld number, or no number at all: do not create a person from that value. Route the event to manual handling instead, or every such call lands on one fictitious contact.
Zoho CRM: contact + deal + note

Target outcome: after a completed call, Zoho CRM finds or creates a contact by phone number, creates a deal and attaches a note with the summary and recording link.

Search contacts by the Phone field (or Mobile, if that is where you store numbers). Heilo sends data.customer_phone_e164 in E.164 format — keep Zoho numbers in the same format, otherwise the search will not match.

Zapier
  1. Trigger: Webhooks by Zapier → Catch Hook. Keep the Heilo subscription in permissive mode.
  2. Filter by Zapier: allow event_type = call.completed; during setup also allow webhook.test (or filter by data._test = true).
  3. Zoho CRM → Find Module Entry in the Contacts module, searching the Phone field by data.customer_phone_e164; enable the option to create the entry when nothing is found. Zoho requires Last Name — use caller_name or the phone number.
  4. Zoho CRM → Find Module Entry in the Notes module, searching your Heilo Call ID field = data.call_id. Do not enable create-if-missing.
  5. Note found → update Note Content; nothing found → Create Module Entry in the Notes module, related to the contact. Write Heilo Call ID = data.call_id plus summary, recording_url and transcript_original. The default recipe does not touch the Deals module.
Make
  1. Trigger: Webhooks → Custom webhook. The scenario must be active and run when data arrives.
  2. Add a filter after the webhook: event_type = call.completed; while mapping test data, also allow webhook.test (or filter by data._test = true).
  3. Zoho CRM → Search Objects in the Contacts module with the criterion Phone = data.customer_phone_e164. If there is no result, create the contact with Create an Object (Last Name from caller_name or the phone number).
  4. Zoho CRM → Search Objects in the Notes module with the criterion Heilo Call ID = data.call_id. Set the scenario to process one at a time.
  5. Router: the note exists → update it; it does not → Create an Object in the Notes module, related to the contact. Write Heilo Call ID = data.call_id, summary and recording_url. This recipe creates no opportunity.
Heilo fieldZoho CRM field / actionUse
event_typeFilter / RouterProcess call.completed; use webhook.test only for field mapping.
data.customer_phone_e164Contact → PhonePrimary lookup key. Search the Phone field (or Mobile) and keep one number format on both sides — the search matches exact values.
data.transcript_processed.caller_nameContact → Last NameZoho requires Last Name; if caller_name is empty, use the phone number as a safe fallback name.
data.transcript_processed.subjectDeal → Deal NameBest opportunity title; fall back to service_needed.
data.transcript_processed.service_neededDeal → Deal Name or custom fieldInquiry category; useful for pipeline routing or labels.
data.transcript_processed.summaryNote → Note ContentShort call description visible on the opportunity.
data.transcript_processed.lead_scoreCustom field / filterFor example, create a lead/deal only from 7/10 or send lower scores to review.
data.transcript_processed.preferred_dateDeal → Closing Date or task due dateUse when the customer mentioned a deadline or preferred follow-up date.
data.transcript_processed.client_cityContact → Mailing CityCustomer city; handy for segmentation or a regional pipeline.
data.transcript_processed.client_addressContact → Mailing StreetFill the person/organization address when the caller provided it.
data.transcript_processed.services_matchFilter / tagfalse = request outside your offer; skip the deal or flag it for rejection.
data.transcript_processed.additional_detailsNote → Note ContentExtra details from the call; append them to the activity body.
data.recording_urlNote → Note Content (link)Playback link; may stop working after the call is deleted for GDPR retention.
data.call_idCustom field → Heilo Call IDIdempotency key. Prevents a second CRM record for the same call.
data.durationCustom field or noteHeilo sends seconds; convert if your Zoho CRM field expects a different format.

Conditions for correct pairing

  • Do not turn on strict mode when confirming the address — Zapier and Make webhooks acknowledge receipt but do not echo the challenge field back. This applies only to the one-off address confirmation, not to the signature on ordinary deliveries.
  • Do not mix event types without a filter: call.outbound.attempted does not include the full transcript or recording.
  • Add a Zoho CRM custom field named Heilo Call ID and store data.call_id there to avoid duplicates.
  • Zoho rejects records with missing required fields: Contacts need Last Name and Deals need at least Deal Name and Stage (depending on your layout also Closing Date) — map safe fallbacks for them.
  • A withheld number, or no number at all: do not create a person from that value. Route the event to manual handling instead, or every such call lands on one fictitious contact.

Three different keys — do not mix them

event_id identifies one specific notification. The same call can send several notifications, and the same notification can arrive twice on a retry. Store event_id to recognise a repeat.

call_id identifies the conversation, which is the activity in your CRM. One call is one activity, no matter how many notifications arrived about it. This is the key you search on before writing.

The phone number, or the contact id in your CRM, identifies the person. Watch three cases: a withheld number arrives as a literal value rather than a number; there may be no number at all; and two contacts can match one number. In none of them should you create a contact from that value — route the event to manual handling instead, or every withheld-number call will collapse into one fictitious contact.

A sales opportunity — a deliberate addition, not a default step

The default recipe does not create a sales opportunity, because not every call is one and an unconditional "create deal" buries the pipeline under one deal per call. If you do want them, make it a rule: search for an open opportunity for that contact first and attach the activity to it; create a new one only when there is none. When several open opportunities match, do not guess — attach the activity to the contact and leave the choice to a person.

Repeats, partial failures and unknown outcomes

Heilo retries a failed delivery five times, spaced 2 minutes, 5 minutes, 30 minutes and 2 hours apart. We do not guarantee ordering: a recording event can arrive before the call-completed event. The recipe has to survive that rather than assume the first sent arrived first.

Deduplication has to cover every call that creates a record, not only the first one. The "check a flag, create, write the flag" layout is not enough: two parallel runs both read the flag before either writes it, and you get two records. If your CRM has a unique key or an upsert on Heilo Call ID, use it. If it does not, set the scenario to process one at a time and search before every write.

A timeout after sending a request that creates a record does not mean the record was not created — it means you do not know. Do not retry the creation. Read the state back by Heilo Call ID first and decide from that. The same applies to a failure halfway through: if the contact was created and the note was not, the retry should add the note, not a second contact.

Full data structure (reference)

Every notification has the same structure. The fields inside `data` depend on the event type — a completed call, for instance, carries the processed transcript. Heilo signs every delivery with the Heilo-Signature header (HMAC-SHA256 over the raw request body), but a signature does nothing until the receiver checks it. Zapier's Catch Hook and Make's Custom webhook accept the request without checking it — what that means in practice is in the section on the two security variants.

{
  "api_version": "2026-06-15",
  "event_id": "<uuid>",
  "event_type": "call.completed",
  "resource_id": "<call_id>",
  "created_at": "2026-06-03T12:34:56Z",
  "data": {
    "call_id": "...",
    "direction": "inbound",
    "caller_phone": "+48600100200",
    "customer_phone_e164": "+48600100200",
    "customer_phone_national": "600 100 200",
    "company_phone_e164": "+48222630000",
    "call_created_at": "2026-06-03T12:30:11Z",
    "app_url": "https://www.heilo.io/calls/3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "duration": 87,
    "recording_url": "https://heilo.io/api/v1/calls/.../recording.mp3?token=...",
    "transcript_processed": {
      "caller_name": "Jan Kowalski",
      "client_address": "ul. Przykladowa 10",
      "client_city": "Warszawa",
      "summary": "...",
      "service_needed": "Tynki gipsowe w mieszkaniu 65m2",
      "subject": "Wycena tynkow - Warszawa, 65m2",
      "lead_score": 7,
      "preferred_date": "2026-06-10",
      "services_match": true,
      "additional_details": "Klient wspomnial o terminie do konca czerwca."
    },
    "transcript_original": "..."
  }
}

Common issues

Verification (handshake) failed after adding the subscription
Your address didn’t confirm within 10 seconds. In Zapier this happens automatically; in Make the scenario must be switched on (and in strict mode you also need a “Webhook response” module). Also check the address has no spaces or “&” characters, fix it, then click “Reverify” on the paused subscription.
Duplicate contacts are being created in my CRM
Connector tools don’t normalise phone formats — “+48 600 100 200” and “+48600100200” look like two different numbers. Heilo already sends the number in E.164 format (the customer_phone_e164 field), so use that one to look up the contact. In Zapier you can also add a “Formatter → Phone Number → E.164” step before the lookup.
The recording link stops working after a while
A recording link is valid for 7 days from the moment it was generated, and it can stop working sooner. An expired token answers 403; a deleted recording answers 410. Re-sending the same event does not renew the link, because it carries the same address. If you need the recording later, open the call in Heilo after signing in. If you copy the file into your CRM, handle the call.deletion_scheduled and call.recording.deleted events too — the link expiring does not delete the copy you already hold.
A message about exceeding the operation limit
One Heilo call is usually a handful of steps in the tool: find the contact, create or update an activity. Do not convert that into a universal number of calls — after the first real event, open your Zap or scenario history and read how many tasks or credits that one run spent. That is what every further call will cost in your setup.

Roadmap (v1.1+)

What we're planning for upcoming releases. This list isn't a guarantee — priorities follow your feedback.

  • Dedicated recipes for more CRMs: Salesforce, Bitrix24, Livespace
  • One-click templates (Zapier/Make) for the most popular CRMs
  • Copy-paste deduplication and field-mapping rules
  • More event types to map (e.g. deal and task events, sms.received)

Missing your CRM or scenario? Email support@heilo.io — we prioritize by real-world need.

Manage keys and webhooks in the panel

Generate API keys, add webhook subscriptions and watch the delivery log after signing in.