HubSpot duplicate deals being created / Need help with filtering logic

I am using n8n cloud to connect Respond.io to HubSpot.

Currently, whenever a contact updates or interacts in Respond.io, the workflow automatically creates a new deal in HubSpot. This includes internal/support communications like Aircall logs or existing contacts checking back in. Because it creates a deal for every single interaction, it’s messing up our reporting and making it impossible to see how many genuine new leads we generate each week.

I need help implementing logic to:

  1. Exclude Aircall/Specific Sources: Filter out any incoming Respond.io webhooks/events that originate from Aircall or non-lead channels.

  2. Check HubSpot First: Check HubSpot to see if the contact already has an open/existing deal. If they do, update the deal or skip creation; if they don’t, only then create a new deal.

What I need help with
I’m not sure how to configure the IF Node or Switch Node to check the incoming Respond.io payload data for fields like “channel” or “source” to filter out Aircall.

Additionally, what is the best practice for using the HubSpot Search CRM node right before deal creation to verify if a deal already exists for that contact? Any example logic or node layouts would be incredible.

@Daryllim28

You can try this and modify for your use case

Important Setup Instructions:

  1. Credentials: After pasting, you will need to select your HubSpot Credentials in the three HubSpot nodes.
  2. Respond.io Payload: I have used {{ $json.body.channel }} as the filter. If Respond.io sends the channel name in a different field (e.g., {{ $json.body.source }}), simply double-click the Filter node and update the expression.
  3. Deal Stages: In the “Find Open Deal” node, I have used a placeholder filter. You will need to enter your actual HubSpot Internal Stage IDs (e.g., appointments_scheduled) to define what “Open” means for your pipeline.

@Daryllim28 This is a classic ‘upsert’ logic problem. By setting your ‘If’ node to validate the ‘source’ key first, you clean your data stream. Then, by performing a query in HubSpot to see if an active deal exists for that contact, you can map the existing Deal ID into your subsequent ‘Update’ node. It’s much cleaner than allowing the creation to happen and cleaning it up later.

To filter by source, add a Filter node right after your Respond.io webhook trigger with the condition: {{ $json.channel }} does not equal aircall (or whatever value Respond.io sends for Aircall events - check the raw webhook payload to confirm the exact field name and value).

For the HubSpot check-before-create part: after the Filter node, use the HubSpot node in “Get” or “Search” mode to look up the contact’s existing deals. If HubSpot returns a deal, route to an Update node; if it returns empty, route to a Create node. You can wire this with an IF node checking {{ $json.results.length > 0 }} on the HubSpot search response.

The key thing to nail first is the exact field name Respond.io sends for the channel/source - log a raw webhook hit with a test trigger to see the full payload structure before writing the filter condition.

For the duplicate-deal issue, the dangerous part is searching for open deals globally and branching from the count. Resolve the HubSpot contact from the Respond.io email or phone first, then use that contact’s associated deal IDs and keep only deals whose stage is still open. Otherwise an unrelated open deal can make n8n skip creation or update the wrong record.

Start by saving one raw Respond.io event from an Aircall log and one real lead event, then make the first Filter compare the exact field/value from those two payloads. If you paste a redacted event plus the HubSpot Search output shape, the IF expression should be deterministic.

The core issue is that your workflow treats every Respond interaction as a reason to create a deal, with no guard for “does a deal already exist” or “is this even a sales interaction.” So support replies, Aircall logs, and returning contacts all spawn duplicates. Two filters fix most of it.

First, filter by interaction type before you create anything. If Respond sends a channel or source field, branch on it so internal/support comms (Aircall logs, existing-contact check-ins) never reach the create-deal node. Only genuine new-inquiry channels continue.

Second, and this is the real dedup, do a lookup before the write. Search HubSpot for an open deal on that contact (or company) first, and only create a deal if none exists, otherwise update the existing one. Creating-without-checking is what turns one contact into ten deals. An upsert pattern (search, then create-or-update) is the durable fix, not just a tighter filter, because filters catch the wrong-type case but not the same-contact-twice case.

Worth adding a dedup key you control too, like the Respond conversation ID stored on the deal, so even if two events arrive close together you can match and skip the second. What does Respond put in the payload that uniquely identifies the conversation? That is your dedup anchor.

Thank you will, give use this workflow. Much appreciated.