Back to BlogIndustry Guides

Lead Capture to CRM for Real Estate: Build and Test the Funnel

Rupak Amin

Founder & Lead Engineer, RAITHub

9 min read

The expensive failure in a real-estate lead funnel is not routing; it is a lead that reaches the web form or portal but never lands in the CRM. Build the capture-to-CRM path to ingest every source into one inbox, deduplicate near-identical leads, map each source's fields to your model, and deliver each lead exactly once even when a portal retries its webhook.

If you would rather have it built and tested for you, see how RAITHub would build this below. This post is about the funnel between capture and the CRM; what happens to a lead once it is in (round-robin, territory, SLA) is real-estate lead routing, and building the CRM itself is building a real-estate CRM SaaS.

Where do real-estate leads actually come from?

From many places, in different shapes: your own website forms, portal enquiries (a buyer clicking "contact agent" on a listing site), paid-ad lead forms, phone-call logs, WhatsApp and email. Each arrives with its own fields and its own quirks, and each can send the same enquiry more than once. The job of the capture layer is to turn all of that into one normalised lead record, reliably, before anything downstream touches it.

SourceArrives asWatch out for
Website formA direct POST to your APIDouble submits, spam bots, missing consent
Portal enquiryA webhook or email from the portalRetries, out-of-order delivery, their field names
Paid-ad lead formA webhook from the ad platformDelayed delivery, test leads, duplicates
Phone / WhatsAppEntered or forwarded by a personTypos, partial data, the same person logged twice

How do you ingest leads so none is dropped or doubled?

Make ingestion idempotent. Give every inbound lead a key derived from its source and the source's own id (or a hash of its fields when there is no id), and reject a second arrival with the same key. A portal that retries its webhook three times then creates one lead, not three. This is the same exactly-once discipline used for payment webhooks, explained in idempotency in API design and what webhooks are and how to build them.

-- Each inbound lead carries a source-scoped idempotency key.
CREATE TABLE lead_intake (
  id              uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id       uuid NOT NULL,                 -- the agency
  source          text NOT NULL,                 -- 'website' | 'portal_x' | 'ads'
  source_event_id text NOT NULL,                 -- the source's id, or a field hash
  payload         jsonb NOT NULL,                -- raw, kept for debugging
  received_at     timestamptz NOT NULL DEFAULT now(),
  UNIQUE (source, source_event_id)               -- a retry cannot insert twice
);

Store the raw payload too. When a lead looks wrong, the raw body is how you tell whether the source sent bad data or your mapping dropped a field.

How do you deduplicate leads from different sources?

A buyer often enquires twice: a portal enquiry and then your own form, minutes apart. That is two intake rows but one person, and creating two CRM contacts splits the history and annoys the agent. Deduplicate on a normalised identity, typically email or phone after cleaning, within a time window, and merge into one contact with both enquiries attached. Be conservative: a false merge (two different people combined) is worse than a missed one, so match on strong signals and flag uncertain cases for a human rather than merging blindly.

How do you map each source's fields to your model?

Keep the mapping as data, not code. Each source has a small, reviewable mapping from its field names to yours, with a default when a field is missing. When a portal renames a field or adds one, you change a mapping row, not a deployment. Validate the mapped result against your lead schema and quarantine anything that fails, with the raw payload, so a malformed lead is visible and recoverable instead of silently lost.

How do you deliver a lead into the CRM reliably?

Treat the hand-off to the CRM (whether your own or a third party's) as a step that can fail and must be retried safely. Write the normalised lead first, then deliver; if delivery fails, retry with backoff, and because the CRM write is keyed on the lead's id, a retry updates rather than duplicates. Record the delivery outcome, so you can always answer "did this lead reach the CRM, and when?". A lead that cannot be delivered after retries raises an alert, not a shrug.

// tests/leads/idempotent-intake.spec.ts
import { test, expect } from 'vitest'
import { ingest, leadsFor } from '@/lib/leads'

test('a portal retrying the same enquiry creates one lead', async () => {
  const event = { source: 'portal_x', source_event_id: 'ENQ-9001', email: 'buyer@test', listing: 'L-42' }
  await ingest(event)
  await ingest(event) // the retry
  await ingest(event) // and again

  const leads = await leadsFor('buyer@test')
  expect(leads.length).toBe(1) // exactly one, not three
})

How do you test the funnel before agents rely on it?

  • Idempotency: a repeated source event yields one lead (above).
  • Deduplication: two sources, same person, one merged contact; two different people stay separate.
  • Mapping: each source's sample payload maps to a valid lead; a missing or renamed field is handled, not crashed on.
  • Delivery: a failed CRM write retries and does not duplicate; an undeliverable lead alerts.
  • Isolation: one agency's leads never appear in another's inbox (row-level security for multi-tenant Postgres).
  • Spam and consent: bot submissions are filtered; consent is captured where required.

Buy, build or hire?

OptionWhat you getChoose this when
A CRM's native web formsCapture and delivery in one toolAll your leads come through forms the CRM provides
A no-code automation (Zapier-style)Source-to-CRM wiring without codeLow volume and simple mapping, and you accept its retry behaviour
A custom capture layerIdempotent ingestion, deduplication, mapping and reliable delivery across every sourceLeads come from many sources and dropping one costs real money
A managed QA team on your buildIdempotency, dedup and delivery suites gated in CIYou have the funnel but "no lead is dropped or doubled" isn't proven

How long does it take to build yourself?

Idempotent intake, a dedup rule and delivery to one CRM are roughly a week for an engineer who has built webhook ingestion before; adding each new source and its mapping is incremental after that. The main risk of doing it yourself is non-idempotent intake: a portal's retries create duplicate leads, or a silent delivery failure drops them, and you only notice when an agent asks where the enquiry they were promised went.

Why RAITHub for this

RAITHub built BlockEstate, a multi-tenant listing and inquiry platform with lead routing, in a 6-week MVP, and runs idempotent, retry-safe webhook ingestion across TheSkinProof (the founder's own venture, 217 endpoints, 750+ tests). The ingestion and data model sit inside the API and backend development service and SaaS development. See the real-estate industry page.

When you don't need us

  • All your leads come through one CRM's own forms. Use its native capture.
  • Volume is low and mapping is simple. A no-code automation may be enough.
  • Your procurement requires a SOC 2 or ISO 27001 vendor. RAITHub is not certified, though it builds the controls your auditor tests.

How RAITHub would build this

  • Scope: a capture layer with idempotent ingestion across your sources, deduplication into one contact, data-driven field mapping with quarantine, reliable keyed delivery to your CRM, and per-agency isolation.
  • Timeline: part of a 4 to 6-week fixed-scope first release; many sources and integrations push it into the 6 to 12-week range, in phases.
  • What you receive: the idempotency, dedup and delivery suites gated in CI, runbooks, the product on accounts you own, IP assigned to you and an NDA as standard.
  • Ways to buy it: a fixed-scope build, a dedicated monthly team, or a QA plan if you only need the funnel test suite.
  • Next step: a free 15-minute technical audit, then a fixed written quote.

See the QA as a Service page, or book the free 15-minute audit.

Documentation checked on 10 October 2026.

Frequently asked questions

Why do real-estate leads get lost between capture and the CRM?

Usually because ingestion is not idempotent or delivery is not retried safely. A portal retries its webhook and creates duplicates, or a CRM write fails silently and the lead is dropped. The fix is a capture layer that keys each inbound event and delivers each lead exactly once.

How do you stop duplicate leads from a portal's retries?

Give every inbound event a source-scoped idempotency key, from the source's own id or a hash of its fields, and make it unique in the database. A second arrival with the same key is rejected, so three retries of one enquiry create one lead, not three.

How do you deduplicate the same buyer from two sources?

Match on a normalised identity, usually cleaned email or phone, within a time window, and merge into one contact with both enquiries attached. Match conservatively on strong signals, because wrongly merging two different people is worse than missing a duplicate; flag uncertain cases for a human.

How should field mapping between sources and the CRM work?

Keep it as data, not code: a small, reviewable mapping per source from its field names to yours, with defaults for missing fields. When a source renames a field, you edit a mapping row instead of deploying, and anything that fails validation is quarantined with its raw payload rather than lost.

Is this the same as lead routing?

No. Lead capture to CRM is about getting every lead in cleanly and exactly once. Lead routing is what happens next: deciding which agent gets it, by round-robin, territory or SLA. They are separate concerns, and this funnel feeds the routing step.

real estate lead capturecrm integrationlead deduplicationidempotencyproptechwebhook ingestion

Ready to discuss your project?

Book a free 15-minute technical audit with our engineering team.