AI & Automation

How I Built a Lead-Enrichment Webhook That Fires on Every New CRM Contact

There is a gap between the moment a contact is created and the moment a rep works it, and that gap is exactly where enrichment and scoring should happen - automatically, before a human ever opens the record. Most teams fill it with a Zapier zap or a Clay table, renting the glue and paying per task. I needed it for StackSwap and built the handler instead. This is the build log: a webhook on contact-create that enriches, scores, and queues the lead, so a rep never sees a bare email address. It is a small piece of the signal-scoring pipeline - the front door that gets a new entity scored the moment it appears.

The flow in one line

The CRM fires a contact-created webhook. A thin handler validates it, enriches the contact via Apollo, writes the firmographics back to the record, computes a fit score, and drops the lead into the scored queue. Start to finish in a couple of seconds, with no human in the loop until there is a decision worth a human - which is the whole idea.

The handler

The endpoint itself is deliberately boring: receive the event, check it is real, dedupe on the contact id so a double-fire does not double-process, call the enrichment provider, map its fields onto your schema, upsert the record, and enqueue it for scoring. Idempotent on the contact id from the first line, because webhooks retry and you will get the same event twice. Thin, direct, one file - the same philosophy as keeping any connector a small wrapper you can repair in isolation when the API shifts.

Why own it instead of renting Zapier or Clay

Rented glue is the right call when the logic is generic and the volume is low. It stops being the right call when the logic is yours and the volume meters. Which fields you enrich, which provider you trust for which data, the confidence threshold below which you flag instead of write, what counts as a duplicate - those decisions are your data quality, and your data quality is what every downstream score depends on. Renting them back inside a per-task tool means paying a growing bill to keep your own judgment in someone else's format. The handler is the part worth owning; Apollo and your CRM are the rails worth renting.

What broke

Double fires. The webhook retried and the same contact enriched twice, burning two credits and racing two writes. The idempotency key on contact id fixed it - process-once, ignore the retry. Rate limits. A burst of new contacts hit the enrichment API's ceiling and calls started failing. A queue with backoff turned a hard failure into a short delay; nothing is lost, it just drains a little slower. Silent bad matches. Early on the handler wrote whatever the provider returned, including low-confidence guesses, and those wrong firmographics quietly skewed scores for weeks. Now anything below a confidence threshold is flagged for review instead of written. A blank field is honest; a wrong one is expensive.

Where this leaves you

Once new contacts arrive enriched and scored, the rest of the motion gets easier: the signal-scoring pipeline has clean inputs, and reps open records that are already qualified. GTM OS runs this enrichment-on-create flow out of the box from one place, and the Operator Playbook has the skills to build the handler against your CRM. If you want to see how much of your stack is glue you could consolidate, a StackScan audit is the place to start.

Frequently asked questions

Do I need Zapier or Clay for this?

No. A webhook on your CRM's contact-create event plus a small handler is the whole thing. Zapier and Clay rent you the glue and meter it per task; if enrichment-on-create is core to your motion, owning the handler is a few dozen lines and removes a recurring cost that grows with volume.

Which CRMs can fire a webhook on a new contact?

HubSpot (a workflow webhook or the CRM API's webhook subscriptions), Pipedrive, Attio, and most modern CRMs expose a contact-created event. The handler does not care which one fires it.

What do you enrich with?

Apollo for firmographics and contact data over its API. The handler is provider-agnostic - swap the enrichment call and everything downstream stays the same, which is the point of owning the seam.

What if enrichment fails or the contact is junk?

Queue and retry with backoff, and flag low-confidence matches for review instead of writing them. A webhook that silently writes bad data is worse than no webhook - it poisons every downstream score that trusts the field.