Skip to main content
Catch duplicate companies and people the instant a record is created by matching on a reliable key and flagging collisions for human review. A duplicate is a second record for a company or person you already have. This recipe runs the moment a record is created. It fires on the Record created trigger, looks for an existing match with Find records, and then branches with If. When a match exists, the recipe does three things. It copies safe fields onto the older record with Update record. It drops the new record onto a review list with Add record to list. Then it posts a heads-up through the Slack block, so a person decides what to merge. A new record is the cheapest moment to catch a duplicate. Nothing downstream has attached to it yet.

Prerequisites

Confirm each item before you build. A missing key or an unscoped list turns this recipe into a source of false positives. A false positive is a record the workflow flags as a duplicate when it is not one.
  • Plan: Plus or higher, which runs on the new Workflows engine.
  • Integrations: Slack is connected to the workspace and has a channel the workflow can post to, such as #crm-hygiene.
  • Objects and attributes: the Companies object has a populated domain attribute, or the People object has a populated email attribute. Use whichever one you are deduping. The chosen key must be the field your team actually fills in, not an optional one.
  • A review list: a list named “Possible duplicate” exists on the same object. It needs at least a Matched record reference attribute and a Reviewed? checkbox so a person can clear entries. See Records vs list entries for why a list is the right home for review state.
  • Access: the workflow runs under the workspace’s automation identity. Confirm in Permissions & workflow access that this identity can read the object and write to the list.

How it works

The workflow reads the new record’s dedup key. The dedup key is the one field you trust to tell records apart, such as a company domain. It then searches the same object for any other record that carries that key. It branches on the result. When a match exists, it copies the safe fields onto the older record and flags the new one for human review. When no match exists, the run stops for free. The workflow never auto-merges. A single key collision can still be two different real-world entities. A deliberate split is safer than a wrong merge. The blocks execute in this order:
  1. Record created fires on a new Company or Person.
  2. Find records searches the same object for the matching key and excludes the new record’s own ID.
  3. If checks whether Find returned at least one record.
  4. On the match branch, Update record consolidates fields onto the existing record, Add record to list flags the new record for review, and the Slack block posts a heads-up.
  5. On the no-match branch, the If condition stops the run, which costs nothing.

Build steps

1

Add the Record created trigger

Create a new workflow and add the Record created trigger. Scope it to a single object. Run one copy for Companies and a separate copy for People, because the dedup key differs between them.
2

Find a record with the same key

Add a Find records block. Search the same object for any record whose key equals the new record’s key. Exclude the new record itself so a record never matches on its own value.For People, set the filter to email is {{trigger.record.email}}. Use one exact key, such as acme.com, rather than a partial name match. Normalize the key first, which means strip stray spaces and make the case consistent so two versions of the same value still match.
3

Branch on whether a match exists

Add an If condition that checks whether Find returned anything. When the count is zero, the condition stops the run for free.
4

Consolidate safe fields onto the existing record

On the true branch, add an Update record block that targets the oldest match, which is the first record Find returned. Copy only fields that can safely be added: fill blank fields on the existing record from the new one. Never overwrite a populated field. Never delete the new record automatically.
5

Flag the new record for human review

Add an Add record to list block that puts the new record onto the “Possible duplicate” list with a pointer to its match. This is the safety valve: a person, not the workflow, decides the merge.
6

Post a Slack heads-up

Add a Slack block so the data owner sees the collision in real time rather than discovering it in a report.

Variable mapping

For People runs, the key variable is trigger.record.email, for example jane@acme.com. See Variables & data passing for the full reference syntax.

Credit cost worked example

The Record created trigger, the Find records lookup, and the If condition are all free. A condition that stops the run costs nothing. Only the writes on the match branch cost credits: A clean create with no match costs 0 credits. A flagged duplicate costs 3 credits with the Slack step, or 2 credits without it. Suppose your team creates 800 companies a month and roughly 4% collide on domain, which is 32 flagged duplicates. That works out to 32 × 3 = 96 credits a month, while the other 768 creates cost nothing. Compare that monthly total against your plan’s included allowance on Credits & pricing explained. This recipe adds no AI block, so the cost per run is fixed and easy to predict. You can add a Classify record step later to score fuzzy matches. Fuzzy matches are records that look like the same entity but do not share an exact key. Gate that step behind the If so it runs only on collisions. AI steps cost a variable number of credits based on how much text they process. So sample about 10 records and read the per-run cost in the run viewer rather than assuming a fixed number.

Test plan

  1. Create a known duplicate. With the workflow live, create a new company Acme Corp with domain acme.com while an acme.com record already exists. This fires Record created.
  2. Check Find. In run history, open the Find records step and confirm it returned exactly the older acme.com record and excluded the new one. The value of step2.records.count should be 1.
  3. Check the branch. The If condition should resolve true and continue.
  4. Check the writes. Confirm Update record filled only blank fields on the older record, Add record to list added the new record to “Possible duplicate” with the match pointer, and the Slack block posted to #crm-hygiene.
  5. Send a deliberately failing input. Create Globex Inc with domain globex.io, a domain nothing else uses. The run should reach If, resolve false, and stop. In run history the run shows 0 credits and no writes. If it still flags something, your Find filter is not using a unique key.
  6. Confirm where to look. Open Runs, debugging & publishing to inspect each step’s inputs, outputs, and credit usage for both runs.

Failure modes

Variations

People by email and companies by domain

Run two copies of this workflow. For People, scope the Record created trigger to People and set the Find records filter to email is {{trigger.record.email}}. Everything else stays the same: the If, the flag, and the Slack post. Do not try to handle both objects in one workflow, because the keys and consolidation fields differ.

Flag only, no consolidation

When your team does not trust automated field copying, remove the Update record step. Keep Find records, the If, Add record to list, and the Slack heads-up. This drops the per-flag cost to 2 credits and leaves every merge decision to a person reviewing the “Possible duplicate” list.

See also


Source: Attio Help Center: Workflows overview. Last reviewed against the Attio Help Center, June 2026.