Monthly HubSpot contact refresh with Lusha and a Sheets audit log

By General Input

On the 1st of every month, refresh stale or incomplete HubSpot contacts with Lusha, write the fresh data back, and log every change to a Google Sheet.

Integrations

  • HubSpot
  • Lusha
  • Google Sheets

Type

Deterministic Code

Categories

  • Sales
  • Operations

Build a code workflow that runs on the 1st of every month at 2am and refreshes stale or incomplete HubSpot contacts with Lusha, logging every batch to a Google Sheet. There are no LLM steps. Everything is deterministic: search, batch enrich, batch upsert, append row.

Trigger: cron, 1st of each month at 2am in the user's timezone.

Step 1. HubSpot Search Contacts. Use a filter group that matches contacts where email is empty OR phone is empty OR lastmodifieddate is older than 6 months ago. Page through results 100 at a time using the cursor returned in paging.next.after. Request the properties: email, firstname, lastname, phone, mobilephone, jobtitle, company, website, linkedinbio (or whatever the org's LinkedIn URL property is called, fall back to no LinkedIn if unset), lastmodifieddate.

Step 2. For each page of up to 100 contacts, call Lusha Enrich People (Bulk). Build one contact object per HubSpot record using the best identifier available, in this priority order: (a) existing email, (b) full name plus company domain derived from the website or email domain, (c) full name plus LinkedIn URL. Skip any HubSpot record that has none of those. Lusha's bulk endpoint caps at 100 per request, which matches the HubSpot page size exactly, so one HubSpot page maps to one Lusha bulk call.

Step 3. From the Lusha response, build a HubSpot Batch Upsert Contacts payload matched on the email property. For each Lusha hit, write back: email (the Lusha-verified one), mobilephone (Lusha mobile), jobtitle, company. Skip the upsert entry for any contact Lusha did not return data for. Keep a running count for the batch: processed, updated, not_found, credits_used (sum of isCreditCharged === true across the response objects).

Step 4. After each batch (immediately after the upsert), call Google Sheets Append Values on the configured audit-log spreadsheet and tab. Append one row with these columns in order: run_timestamp (ISO 8601), batch_index (1-based), contacts_processed, contacts_updated, contacts_not_found, credits_consumed. Use valueInputOption USER_ENTERED so the timestamp parses as a date in the sheet.

Loop until HubSpot search returns no paging.next. Respect rate limits: HubSpot Search is 4 requests per second per token, and Lusha enrichment is 25 requests per second. Add a short pause between batches if needed. On a 429 from either side, honor Retry-After and continue.

Setup inputs the workflow should ask for: the Google Sheets spreadsheet ID, the tab name for the audit log, and the staleness threshold in months (default 6). Everything else uses the defaults above.

Related prompts

Explore more prompts
Call overdue Xero customers with an AI collections agentLocal listing health board for every location you manageLet support send one-off Loops emails without an engineerTurn Mailjet email clicks into ranked HubSpot follow-upsStop cold emails to anyone with a live deal in PipedriveiMessage campaign console with pre-flight checks and delivery boardA searchable RFP answer library your bid team drafts fromLinkedIn Ads budget pacing dashboard for every client accountSee which target accounts just started advertising on LinkedInFront desk appointment confirmation board for the next 3 days