# Fill in missing mobile numbers on HubSpot contacts overnight

> Every night at 2am, find contacts with no mobile number, look them up in bulk, and write the results back so your reps stop hitting dead ends.

- Workflow type: code
- Services: Boomerang, HubSpot, Google Sheets, Slack Bot
- Categories: Operations
- Published: 2026-08-21

## What it does

- Every night it pulls the contacts in your CRM that are missing a mobile number and still sit in the lifecycle stages you care about, such as lead or sales qualified lead.
- It sends that batch off for a bulk mobile number lookup, waits for the job to finish, and writes any numbers that come back onto the right contact records.
- Every contact it tried gets stamped with the date it was attempted, so future runs skip it and you never pay twice to look up the same dead record.
- Every result, found or not found, is appended to a Google Sheet so you can watch your hit rate over time, and a short summary lands in Slack before you start the day.

## What you'll need

- A HubSpot account with permission to read and update contacts.
- A Boomerang account with enough credits to cover the records you look up each night.
- A Google Sheet with a tab set aside for the enrichment log.
- A Slack workspace and a channel for the overnight summary.
- One extra date field on your HubSpot contacts to record when enrichment was last attempted. Your admin can add this in a minute.

## Prompt

Every night at 2am on a cron schedule, fill in the missing mobile numbers on my HubSpot contacts so my reps stop hitting dead ends. This is a deterministic pipeline with a fixed query, a fixed enrichment job, and a fixed write-back. There are no judgement calls anywhere in it.

Step 1, select the records. Use HubSpot Search Contacts to pull contacts where the mobile phone property (mobilephone) is empty, the lifecycle stage (lifecyclestage) is one of the stages I care about (default to lead and salesqualifiedlead, and make the list configurable), and my enrichment attempted date property has never been set. Cap the result at a fixed number of records per run, default 500, exposed as config so credit spend per run is predictable. Request the properties needed to identify a person for lookup: first name, last name, email, company, and LinkedIn URL if I store one. If the search returns zero contacts, post nothing and end the run quietly.

Step 2, build the CSV. Turn those rows into a CSV with a header row and one column per identifying field, plus a hubspot_contact_id column carrying the record id. Keep an in-workflow map from row to HubSpot contact id as well, keyed on record id and on email as a fallback, so matching back does not depend on Boomerang echoing the id column. Because the CSV has a header row, omit columnMappings and let Boomerang auto-detect the columns. If the submit returns a 400 with code COLUMN_MAPPING_ERROR, read details.missingRequired, details.outOfRange and details.duplicateColumns, report them in the Slack failure message, and make no CRM writes.

Step 3, submit the job. Use the Boomerang Submit Mobile Finder Enrichment operation, which is a bulk multipart form upload with the CSV as the file part. Configure whether US numbers and UK numbers should be included, as two booleans, defaulting both to true. Capture the returned request id and the credits reserved at submit time.

Step 4, poll for completion. Call Get Request Status on that request id until the status leaves "In queue". The status values are exactly "In queue", "Completed" and "Issue", capitalized and with a space, so compare against them exactly. Poll about once a minute with a sensible backoff and an overall timeout of roughly two hours, after which treat the run as failed. Boomerang is rate limited to 60 requests per minute and 600 per hour per key, so if any call returns 429, honor the Retry-After header value in seconds before retrying.

Step 5, on "Completed", download the output. Read outputUrl off the status response and fetch the CSV. That link is a time-limited signed URL, and outputUrlExpiresAt gives its expiry, so if it has expired just re-poll the request to mint a fresh one. Important: do not read the emailFound or phoneFound fields to decide whether the run worked. Those stay null even on completed jobs until Boomerang's data team populates them. The output CSV is the source of truth for what was found.

Step 6, write the numbers back. Match each output row back to its HubSpot contact id using the id column and the fallback map, then write the mobile numbers in with HubSpot Batch Update Contacts, chunked into batches of at most 100 records per request, which is that operation's limit. Only set the mobile phone property on rows that actually came back with a number.

Step 7, stamp everything that was attempted. Set the enrichment attempted date property (for example mobile_enrichment_attempted_date) to today's date on every contact that was submitted, including the ones that came back empty. This is the part that matters most: it is what makes the step 1 query skip them next time, so I never pay twice to look up the same dead record. Fold these into the same batches of 100 where possible.

Step 8, log every result. Append one row per submitted contact to a Google Sheets audit tab using Append Values, whether or not a number was found. Include the run date, contact id, name, company, lifecycle stage, a found or not found flag, and the number that was written when there is one. This is what lets me see hit rate over time and work out which segments are worth enriching.

Step 9, summarize to Slack. Use Slack Bot Send a Message to post a short overnight summary to my chosen channel with records submitted, numbers found, hit rate as a percentage, and credits used. Take credits used from creditsUsed on the status response, which is the amount actually charged, rather than the credits reserved at submit.

Failure path. If the job comes back with status "Issue", or the submit fails with a 402 meaning insufficient credits or an exhausted free-tier cap, or the poll times out, post that to Slack instead of the summary and make no CRM writes at all. Do not stamp the attempted date on contacts that were never actually looked up, because that would silently blacklist them from every future run.

Build the nightly batch rather than a per-contact version. The mobile finder is a bulk CSV job with batch pricing and a per-key hourly request budget, so firing one asynchronous job per newly created contact burns the budget and wastes the batch rate. A HubSpot triggered per-contact variant is a reasonable customization to mention, but the scheduled batch is the shape to build.

## How to customize

- Change the run time or the nightly record cap to control exactly how much you spend per run.
- Choose which lifecycle stages get enriched, and whether US and UK numbers are included in the lookup.
- Log more than the basics by adding columns to the audit tab, such as owner or original source, so you can see which segments actually return numbers.
- Run it per contact instead of nightly. HubSpot can tell the workflow the moment a contact is created, though the nightly batch is usually cheaper and easier on lookup limits.

## FAQ

### Will this overwrite mobile numbers I already have?

No. Only contacts whose mobile number field is completely empty are picked up, so anything your team has already collected or verified is left alone.

### What happens to contacts where no number is found?

They still get stamped with the date they were attempted and logged in your audit sheet as not found. That stamp is what stops the next run from spending credits on the same dead record again.

### How do I keep the cost predictable?

The workflow only ever submits a fixed number of records per run, so your worst case each night is capped. The Slack summary also reports the credits used, so you can see the real spend every morning.

### What if the lookup fails or I run out of credits?

The workflow posts the problem to Slack and makes no changes to your CRM at all. It will not half-write a batch or stamp contacts it never actually looked up.

### Will this work on HubSpot Free?

Yes. It uses standard contact search and contact updates, which are available on free and paid HubSpot plans. You will need to add one date field to your contacts, and the lookups themselves are billed by Boomerang rather than HubSpot.

### Why does it run overnight instead of the moment a contact is created?

Bulk lookups are priced and rate limited as batches, so one nightly job is cheaper and far less likely to hit limits than firing a separate job per new contact. You can switch to per contact if you prefer speed over cost.

Use this prompt in General Input: https://www.generalinput.com/prompts/fill-in-missing-mobile-numbers-on-hubspot-contacts-overnight