# Review every enriched contact before it lands in HubSpot

> Open one bench after every enrichment run to compare what came back against your CRM, approve row by row, and escalate only what is still missing.

- Workflow type: app
- Services: Boomerang, HubSpot, Google Sheets
- Categories: Sales, Operations
- Published: 2026-08-21

## What it does

- Opens on a queue of finished enrichment runs, so you pick one and see every contact it returned in a single review table
- Shows the email and mobile that came back beside what your CRM already holds for that person, flagged as new information, a conflict, or a duplicate
- Approve or reject rows one at a time or in bulk: approved contacts save straight to HubSpot, rejected ones collect in a spreadsheet as your second pass list
- Keeps a separate escalation view for contacts still missing details, with the cost of each search level shown before you spend anything

## What you'll need

- A Boomerang account with an API key and enough credits to run searches
- A HubSpot login with permission to view and update contacts
- A Google account and a spreadsheet where rejected rows should collect
- At least one finished enrichment run in Boomerang to review

## Prompt

Build me a review bench that sits between Boomerang enrichment and my CRM, so nothing lands in HubSpot unchecked. The app opens on a queue of enrichment jobs read from Boomerang List Requests, filtered to the email finder, mobile finder and profiles job types. Each queue card shows the job type, when it was submitted, its status, the row count, and the credits actually used. Completed jobs are the ones I work. Jobs still in queue stay visible with their status so I know what is coming, and jobs that ended with an issue are grouped separately.

Clicking a completed job opens the review table. The app calls Get Request Status for that job, takes the signed output CSV link off the response, downloads it, and renders it row by row. Each row shows the contact name, company and profile identifier from the source list, the email that came back, the mobile that came back, and which waterfall tier produced the result. Show credits used from the status response rather than the credits reserved at submit, because reserved is only an estimate and used is the real charge. The signed link is time limited, so if a download fails, re-poll Get Request Status for that job to mint a fresh link instead of showing an error.

Beside every returned value I want to see what HubSpot already holds for that person. For each row the app looks the contact up with HubSpot Search Contacts, matching on the enriched email first and falling back to name plus company, and shows the existing email, phone, company and owner in a side by side column. Classify each row as new information when HubSpot has no contact or the field is empty, as a conflict when HubSpot holds a different non-empty value, and as a duplicate when the values already match. Badge each row accordingly and let me filter the table down to just conflicts, just new information, or just duplicates.

Bake in this Boomerang behaviour: the found-email and found-phone fields stay null on some completed jobs until Boomerang's data team populates them, so a blank on a completed job is not the same as a miss. Render those rows as Pending, never as a failure. Pending rows cannot be approved or rejected, are excluded from the escalation candidates so I never pay to re-run a lookup that already succeeded, and get a Refresh action that calls Get Request Status again and re-reads the CSV to pick up values that have since landed.

I approve or reject rows individually with buttons on the row, or in bulk by selecting rows or acting on the current filter, for example approving every new-information row in one go. Approved rows go to HubSpot with Batch Upsert Contacts, matched on email, chunked into batches of one hundred, writing the enriched email and mobile onto the contact record. Rejected rows append to a Google Sheet with Append Values as my second-pass worklist, one line each carrying the contact, the job id, the tier that was attempted, what came back, and my rejection reason. After each push show a result summary including any rows HubSpot refused, and keep those rows in the table so I can fix and retry them.

The escalation view is the other half of the app. It lists every unresolved row across jobs, meaning rows where a field is genuinely empty after a completed tier and never Pending rows, grouped by what is missing. From there I select rows and resubmit them: Submit Email Finder Enrichment at the next tier up for missing emails, Submit Mobile Finder Enrichment for missing mobiles, and Submit Profiles Enrichment where the underlying profile data is thin, building each job's CSV from the rows I selected. Show the credits each tier charges next to its button, plus a running total for the current selection, so I can see what escalation actually costs before I commit.

Enforce the waterfall rule in the interface: a row only becomes eligible for a more expensive tier once the cheaper one has come back empty. Essential must have run and returned nothing before pro is offered, and pro before ultimate. Grey out the higher tier with a short explanation of what still needs to run rather than hiding it, so it is obvious why the option is not available yet.

Any single stubborn row can be retried inline without waiting for a bulk job. A Retry button on the row calls Find Single Mobile Number for that profile and writes the result straight back into the row, with its credit cost shown on the button. Jobs submitted from the escalation view appear back in the queue on the front page with their status, so the loop closes and I can review them the same way when they finish.

Persist my approve and reject decisions per row, keyed by job and row, so I can leave a job half reviewed and come back without losing my place, and so a second reviewer sees what has already been decided and who decided it. Show review progress on each queue card: rows reviewed, approved, rejected and still pending. Boomerang rate limits at sixty requests a minute, so batch the HubSpot lookups and space out the Boomerang polling rather than firing one request per row when the table loads.

## How to customize

- Choose which run types appear in the queue (email searches, mobile searches, profile lookups) and how far back the queue looks
- Change the rules that decide whether a row counts as new information, a conflict, or a duplicate, such as whether a different mobile already on file should always be treated as a conflict
- Point rejected rows at a different spreadsheet or tab, and pick which columns get written
- Set how far a contact is allowed to escalate before someone has to sign off on the spend

## FAQ

### Why do some contacts show as "pending" instead of a result?

Boomerang can mark a run finished before its data team has filled in every email and phone field, so a blank on a completed run does not always mean nothing was found. The bench marks those rows as pending and keeps them out of the reject pile, with a refresh button to pick up values once they land. Without that rule, reviewers throw away good contacts.

### Will approving a row overwrite what is already in HubSpot?

Contacts are matched on email address, so approving updates the existing record instead of creating a second one. Where an enriched detail disagrees with something already on file, you see both values side by side and decide before anything saves.

### How do I know what escalating a contact will cost?

Each search level shows the credits it charges next to the button, plus a running total for whatever you have selected. The figures come from what past runs were actually billed rather than the estimate taken when a job is submitted, so they reflect real spend.

### Can it spend credits on searches I have not asked for?

No. Nothing escalates on its own. A contact only becomes eligible for a more expensive search level after the cheaper one has come back empty, and it only runs when you press the button.

### How many contacts can I approve at once?

You can select as many rows as you like. HubSpot accepts one hundred contacts per save, so the app splits larger selections automatically and reports back on anything HubSpot refused so you can retry just those rows.

Use this prompt in General Input: https://www.generalinput.com/prompts/review-every-enriched-contact-before-it-lands-in-hubspot