Daily Salesforce lead hygiene with RocketReach enrichment

Every morning at 5am, find Salesforce leads missing email, phone, or title, fill in the gaps from RocketReach, and mark each row enriched so it never gets touched twice.

Deterministic Code
SalesforceRocketReachSalesOperationsLead EnrichmentData Sync

Build me a deterministic daily lead-hygiene workflow that fills in missing contact data on Salesforce leads using RocketReach. This should be a code workflow (discrete nodes, no agent reasoning) because the inputs and outputs are fully known.

Trigger: cron, every day at 5:00am in my org's timezone.

Step 1. Call Salesforce Get Many Leads with a SOQL filter that returns leads created or modified in the last 24 hours where Email IS NULL OR Phone IS NULL OR Title IS NULL, and where the custom field enrichment_status is NULL (so we never re-process rows we already touched). Select at minimum Id, FirstName, LastName, Email, Phone, Title, Company, LinkedIn URL (or whatever custom LinkedIn field exists on the Lead object), and enrichment_status. Cap the result set at 200 leads per execution to stay inside RocketReach's 5 requests-per-second soft limit.

Step 2. For each returned lead, call RocketReach Person Lookup with whatever identifier is available, in this priority order: (a) LinkedIn URL if present, (b) full name plus Company, (c) the partial email. Use lookup_type=enrich first to try cached data without consuming a fresh credit; if that returns no result, fall back to lookup_type=standard. Honor the rate limit by spacing requests (5 per second max) and respect the Retry-After header on any 429 response.

Step 3. For each lead where RocketReach returns a match, call Salesforce Update Lead and write back: email (only if the Salesforce Email field is currently blank), mobile_phone into Phone (only if blank), current_title into Title (only if blank), linkedin_url into the LinkedIn custom field (only if blank), and current_employer into a notes or custom field. Also set enrichment_status to "enriched" and stamp a timestamp field (enrichment_completed_at) with the current time.

Step 4. For each lead where RocketReach returns no match, call Salesforce Update Lead and set enrichment_status to "not_found" with the same timestamp. This guard prevents the workflow from re-attempting the same dead-end lookup tomorrow.

Step 5. Exit cleanly with a single log line summarizing the run: how many leads were scanned, how many were enriched, how many had no match, and how many RocketReach lookup credits were consumed.

Important constraints: never overwrite a non-empty field on the Salesforce lead. Never call RocketReach for a lead that already has a non-null enrichment_status. If RocketReach returns a 401 or 403, fail fast and surface the error. If Salesforce returns REQUEST_LIMIT_EXCEEDED on the update step, stop processing the remaining batch and log how far we got.

Additional information

What does this prompt do?
  • Scans Salesforce every day for leads created or updated in the last 24 hours that are missing an email, phone, or job title.
  • Looks each lead up in RocketReach using whatever you already have on file: a LinkedIn URL, a name and company, or a partial email.
  • Writes the verified email, mobile phone, current title, LinkedIn URL, and current employer back to the matching Salesforce lead.
  • Stamps a custom status field on every lead it touches, so tomorrow's run skips anything it already enriched or already tried.
What do I need to use this?
  • A Salesforce login with permission to read and update leads.
  • A RocketReach account on a plan that includes API access and lookup credits.
  • A custom field on the Salesforce Lead object to track enrichment status (the workflow will write values like enriched or not_found into it).
How can I customize it?
  • Change the schedule. The default runs at 5am every day, but you can move it to a different hour, a different timezone, or only on weekdays.
  • Tighten or loosen the filter. Add other missing fields you care about, restrict to a specific lead source, or only enrich leads owned by certain reps.
  • Cap the daily volume. The default tops out at 200 leads per run to stay within RocketReach's rate limits and protect your credit balance. Raise or lower this to match your plan.

Frequently asked questions

Will this overwrite data my reps already entered?
No. The workflow only fills in fields that are blank on the Salesforce lead. If a rep has already typed an email or phone, that value stays untouched.
What happens to leads RocketReach can't find?
They get marked with a not_found status so the workflow doesn't waste a lookup credit on them tomorrow. You can re-run them manually later by clearing the status field.
How does this protect my RocketReach credits?
The workflow first tries to pull cached contact data, which doesn't consume a fresh credit, before falling back to a full lookup. It also caps each run at 200 leads to keep your daily spend predictable.
Do I need to build a custom field in Salesforce first?
Yes. Add a simple text field on the Lead object (something like enrichment_status) before turning the workflow on. The workflow uses it to remember which rows it has already processed.
Can I send the daily summary somewhere?
Yes. By default the workflow logs how many leads were enriched, how many had no match, and how many credits were used. You can extend it to post that summary to a Slack channel or email it to the revops team.

Stop letting half-filled leads sit in your CRM.

Connect Salesforce and RocketReach once, and Geni keeps your new leads enriched every morning before your reps log in.