Skip to content

The moment it happens

Add new leads to HubSpot without duplicates

Leads from your forms, bookings and shop become HubSpot contacts, matched by email so nobody is added twice.

Runs
The moment it happens
Apps
HubSpot
AI
Not needed
Access
crm.objects.contacts.read, crm.objects.contacts.write

Send this to your assistant

Create a ClickRuntime automation: give me a link my booking tool can send new bookings to. For each one, find the HubSpot contact by email, create it if it doesn't exist, and add a note with the meeting time.

Paste it into your assistant. It tells you what it will send or change before it runs live.

First time? Adding ClickRuntime to your assistant takes about two minutes.

Why use it

Why teams use this automation

Leads arrive from too many places: a Typeform on the pricing page, a Calendly booking, a Shopify checkout, a webinar registration, a partner's CSV. Each tool has its own way of storing the person, so the same buyer appears in HubSpot three times with slightly different details, or not at all because nobody built the connector. Sales reps then email someone who is already a customer, and marketing sends a welcome series to a contact who unsubscribed last year.

Each lead sent to the link is checked against HubSpot by email address. If the contact exists, its fields are updated, and if not, a new contact is created. A note or ticket can be added for context, such as the message from the form.

It is meant for small businesses and agencies that collect leads in several tools and want one clean list in HubSpot.

What you get out of it

  • One record per person

    Searching by email before creating avoids duplicates, so reps see the full history of a person in one place instead of three partial records. That also keeps reporting honest, because each person is counted once.

  • Leads reach the CRM within moments

    No more exporting a CSV every Friday. A booking or form fill appears in HubSpot as it happens, so follow-up is quicker. Speed matters for form fills and bookings, where the person is still thinking about you.

  • Existing information is kept

    Updating an existing contact preserves its history and ownership. A returning customer does not turn into an anonymous new lead. Sales sees the whole story of a customer, including purchases and earlier enquiries, which prevents embarrassing cold-style messages.

  • Better email and consent hygiene

    With one contact per address, unsubscribes and preferences are tracked in one place, which reduces the risk of emailing someone who opted out. Fewer duplicate records also means fewer accidental sends, which protects your reputation and respects people's choices.

  • Context travels with the lead

    An optional note or ticket carries the message or order details, so the rep knows what the person asked before the first reply. That removes the need for a rep to open the originating tool just to understand what the lead wanted.

How it works

What happens, step by step

  1. The moment a form is submitted, a call is booked or your shop sends a new lead, ClickRuntime receives the details.

  2. It searches HubSpot for a contact with the same email address.

  3. If one exists, the contact is updated; if not, a new contact is created, so nobody is added twice.

  4. If you want, a note such as "Meeting booked for 22 Oct" is added to the contact as well.

Before it runs live

  • Connect HubSpot
  • Paste your automation's link into the other app's webhook settings

What you get

RunsThe moment it happens
Add new leads to HubSpot without duplicatesjust nowSucceeded
HubSpot · Contact noteMeeting booked for 2026-10-22 15:00 UTC · on contact dana@northwind.example (new contact)

Build your own

Need it to work differently?

This template is a starting point. Copy the prompt and change it, or describe a different automation from scratch, with your own conditions, more apps or an AI step. Your assistant writes it, tests it and runs it on ClickRuntime.
  • Your own rules

    Conditions, thresholds, filters and exceptions, written the way you'd explain them to a colleague.

  • Any number of apps

    One automation can read from one app and update several others. Over 248 apps are ready, and anything with an API works too.

  • AI where it helps

    Summaries, classification, extraction and drafts use ClickRuntime's built-in AI, with no extra key.

  • On a schedule or on each event

    Runs as often as every 5 minutes, or the moment an app sends an event, and keeps track of what it already handled.

Changes you can ask for

  • Act only on the items that match your own rules, and skip the rest
  • Add an AI step, for example to summarize, sort or draft a reply
  • Add another app, for example also send it to Slack
  • Combine it with other HubSpot steps into one automation

Under the hood

The code that runs this automation

This is the Python script that runs each time the automation starts. Your AI assistant writes it for you when you send the prompt, and you allow each step in your assistant. To change how the automation works, describe the change to your assistant and it updates the script for you. For every function it can use, see the script reference.
create_or_update_contacts_from_incoming_leads.pyexample
from datetime import datetime, timezone
from clickruntime import on_event, credentials, http

API = "https://api.hubapi.com/crm/v3/objects"

@on_event
def handle(event):
    booking = event.json or {}
    headers = {"Authorization": f"Bearer {credentials.connection('hubspot', 'work')}"}
    found = http.post(f"{API}/contacts/search", headers=headers, json={"filterGroups": [{"filters": [
        {"propertyName": "email", "operator": "EQ", "value": booking["email"]}]}]}).json()["results"]
    props = {"email": booking["email"], "firstname": booking.get("first_name", ""),
             "lastname": booking.get("last_name", ""), "company": booking.get("company", "")}
    if found:
        contact_id = found[0]["id"]
        http.patch(f"{API}/contacts/{contact_id}", headers=headers, json={"properties": props}).raise_for_status()
    else:
        contact_id = http.post(f"{API}/contacts", headers=headers, json={"properties": props}).json()["id"]
    http.post(f"{API}/notes", headers=headers, json={
        "properties": {"hs_note_body": f"Meeting booked for {booking.get('start_time')}",
                       "hs_timestamp": datetime.now(timezone.utc).isoformat()},
        "associations": [{"to": {"id": contact_id}, "types": [
            {"associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 202}]}]}).raise_for_status()
    return {"contact": contact_id, "created": not found}

FAQ

Common questions

What if the same email is sent twice?
The second request finds the existing contact and updates it instead of creating another. That is the point of searching first, so repeated submissions from a form do not clutter your database. No duplicates, and the latest values win.
Which tools can send leads to the link?
Anything that can make a web request: form builders, scheduling tools, shop webhooks, automation connectors or your own site. The payload only needs an email address and whichever fields you want stored. Setting up the link takes only the destination URL.
Can I set the lifecycle stage or owner on creation?
Yes. Fields such as lifecycle stage, lead status and owner can be set when a contact is created, and you can choose different values per source. Describe the rule and approve the change. Values can differ depending on where the lead came from.
What permissions does it need?
crm.objects.contacts.read, crm.objects.contacts.write. Nothing else in your accounts is used.
Does it need AI?
No. It runs without AI, so it doesn't use your plan's AI allowance.
How many runs does it use?
One run for each event the app sends.
Can I change how it works?
Yes. Describe the change to your AI assistant, for example a different schedule, channel or filter. It updates the automation, tests it and tells you what changed.

Set up “Add new leads to HubSpot without duplicates” from your assistant

Copy the prompt above, or connect your assistant first. Free plan, no credit card.