How to Detect and Hold Duplicate Customers Across BigCommerce and NetSuite
Build a Spojit workflow that scans new BigCommerce customers against your NetSuite records, uses Miraxa to flag likely duplicates, and routes any proposed merge through human approval before anything changes.
What This Integration Does
When a storefront and an ERP both hold customer records, the same person often ends up entered twice: once in BigCommerce when they check out, and once in NetSuite when finance creates an account. Slightly different spellings, a work email versus a personal email, or a company name typed two ways all defeat exact-match lookups. This workflow runs on a schedule, pulls newly created BigCommerce customers, searches NetSuite for records that could be the same person or company, and asks the intelligent layer across your automation to judge how confident a match is. Strong duplicates are held for a person to review in the Approvals inbox, so no record is ever merged or edited automatically.
The workflow is started by a Schedule trigger (for example every weekday morning). On each run it lists recent BigCommerce customers, loops over each one, queries NetSuite for candidate matches, and runs a Miraxa-powered Connector node in Agent mode that returns a structured verdict. Only when the verdict is a likely duplicate does the run pause at a Human node for sign-off. The workflow leaves your data unchanged: it produces a reviewed decision and a notification, not a silent edit. Because each scheduled run only looks at customers created since the last window, re-runs do not re-process the same records, and a held customer that is rejected simply continues as a normal new account.
Prerequisites
- A BigCommerce connection with permission to list and read customers. See the BigCommerce connector article.
- A NetSuite connection that can read customers and run SuiteQL queries. See the NetSuite connector article.
- At least one approval target (a User, Role, or Team) configured in your workspace so the Human node has someone to route to.
- A shared idea of what counts as a duplicate for your business (same email, same company name, same phone) so you can tune the matching prompt.
Step 1: Start the workflow on a schedule
Add a Trigger node and set its type to Schedule. Provide a 5-field cron expression and an IANA timezone. To run every weekday at 9am Sydney time, use:
0 9 * * 1-5
Australia/Sydney
The trigger output is {{ scheduledAt }}, which you can use later as the upper bound of your scan window. A single Schedule trigger can hold several schedules if you want to scan more than once a day.
Step 2: List new BigCommerce customers
Add a Connector node in Direct mode, choose your BigCommerce connection, and select the list-customers tool. Use its paging fields (page and limit) to pull a manageable batch, and keep the batch small while you tune the matcher. A typical first configuration:
{
"limit": 50,
"page": 1
}
Name the output variable something clear like bcCustomers. Each returned customer carries fields such as email, first_name, last_name, company, and phone, which are the fields you will compare against NetSuite.
Step 3: Loop over each customer
Add a Loop node in ForEach mode and point it at the customer list, for example {{ bcCustomers.data }} (adjust the path to match the shape your Direct mode call returns). Inside the loop body you will reference the current customer as the loop item, for example {{ customer.email }} and {{ customer.company }}. Everything in Steps 4 through 6 lives inside this loop so each BigCommerce customer is checked independently.
Step 4: Find candidate matches in NetSuite
Inside the loop, add a Connector node in Direct mode using your NetSuite connection and the run-suiteql tool. A SuiteQL query lets you cast a wider net than an exact lookup, matching on email or a company-name prefix so near-duplicates still surface. Pass the current customer's email and name into the query q field:
SELECT id, entityid, companyname, email, phone
FROM customer
WHERE email = '{{ customer.email }}'
OR companyname START_WITH '{{ customer.company }}'
Name the output nsCandidates. If you prefer a simpler approach, you can instead use the NetSuite list-customers tool with an email filter, but SuiteQL gives you the fuzzy company-name match that catches the cases exact lookups miss. When the result is empty, there is nothing to compare and the loop iteration can move on.
Step 5: Let Miraxa judge the match (Agent mode)
Add a Connector node in Agent mode so the intelligent layer across your automation can weigh the BigCommerce customer against the NetSuite candidates and explain its reasoning. Agent mode supports a Response Schema, which forces a predictable JSON verdict you can branch on. Write a prompt that hands it both records and asks for a confidence score:
Compare this new BigCommerce customer to the existing NetSuite candidates and
decide whether they are the same person or company. Weigh email, name, company,
and phone. Do not treat a shared free-email domain as a match on its own.
New customer: {{ customer }}
NetSuite candidates: {{ nsCandidates }}
Set a Response Schema that returns the fields you need to act on:
{
"isDuplicate": "boolean",
"confidence": "number",
"netsuiteId": "string",
"reason": "string"
}
Name the output verdict. You now have {{ verdict.isDuplicate }}, {{ verdict.confidence }}, and {{ verdict.reason }} to drive the rest of the workflow. To understand when Agent mode is the right choice over Direct mode, see the guide on choosing between Agent and Direct mode.
Step 6: Branch only on likely duplicates
Add a Condition node that checks both the duplicate flag and the confidence, so weak guesses do not bother a reviewer. For example, send the true branch onward when:
{{ verdict.isDuplicate }} is true AND {{ verdict.confidence }} is over 0.8
The false branch ends the iteration quietly: the BigCommerce customer is treated as genuinely new and the loop moves to the next record.
Step 7: Hold the merge for human approval
On the true branch, add a Human node so a person decides before anything is merged. Set a clear Label and a Message that surfaces the evidence, using variables in the notification body so the reviewer sees the specifics at a glance:
Possible duplicate: {{ customer.email }} matches NetSuite {{ verdict.netsuiteId }}.
Confidence {{ verdict.confidence }}. Reason: {{ verdict.reason }}.
Add at least one approval slot (a User, Role, or Team); approval completes only when every slot is satisfied. Optionally set a Timeout in minutes and turn on Email approvers. Approvers respond in the Approvals inbox at /approvals. When the request is approved the node outputs { approved: true, outcome: "APPROVED" } and the run continues; if it is rejected or times out the run halts for that record. You can follow the approval with a Connector node (for example NetSuite update-customer) to record the confirmed link, or a Send Email node to notify your data team. Note that "on reject do X" branching is not supported, so reject simply means no change is made.
Tips
- Keep the BigCommerce
limitsmall at first. A 50-record batch keeps each scheduled run fast and limits AI cost while you tune the matching prompt. - Raise or lower the confidence threshold in the Condition node to trade reviewer effort against missed duplicates. Start strict (0.85+) and loosen only if real duplicates slip through.
- Use the Response Schema verdict's
reasonfield directly in the Human node message so reviewers can decide without opening both systems. - Tell the agent explicitly not to treat a shared free-email domain (such as gmail.com) as evidence of a match. This single instruction removes a large class of false positives.
Common Pitfalls
- Quoting in SuiteQL: wrap string values in single quotes and make sure a customer's name does not itself contain a stray quote. Test the query in a single Direct mode run before wiring it into the loop.
- Pagination:
list-customersreturns one page at a time. If you have more new customers than yourlimit, increasepageacross runs or raise the limit, otherwise later customers are never scanned. - Empty candidate lists: when NetSuite returns no matches, do not pass an empty list into a merge step. The Condition node guards this, but confirm your agent prompt handles the no-candidate case gracefully.
- Approval routing: a Human node with no reachable approver will stall. Confirm the User, Role, or Team in each slot exists in your workspace before enabling the schedule.
Testing
Before turning the schedule on, switch the trigger to Manual (or keep Schedule but run it with the Run button) and point BigCommerce list-customers at a tiny batch with limit set to 5. Seed one obvious duplicate in NetSuite that matches a test BigCommerce customer, run once, and watch the execution log: confirm the SuiteQL query returns the candidate, the Agent mode verdict reports isDuplicate: true with high confidence, the Condition branches true, and a request appears in the Approvals inbox. Approve and reject it once each to see both outcomes, then restore your real limit and schedule.