Skip to content
//Real Estate · AI Automation

Workflow automation across CRM, listings, e-sign and WhatsApp for a brokerage

An event-driven automation layer across CRM, listing portals, e-signature and WhatsApp that replaced manual copy-paste between five systems with one live transaction status visible to everyone.

01The challenge

Agents juggled listings, CRM, and document tools manually. Leads went cold and transaction steps slipped through the cracks.

02What we did
  • 01Shadowed agents and admin staff for three weeks, timing every manual handoff step across the four highest-frequency transaction types.
  • 02Prototyped one event type end to end, proving idempotency and retry behaviour under deliberately duplicated and reordered webhooks.
  • 03Forged the full event catalogue, all five integrations, and the WhatsApp template set through approval.
  • 04Hardened with chaos testing against the retail APIs and shipped replay tooling and a runbook the client's ops team can run alone.

Results

MetricBeforeAfterWindow and source
Hours reclaimed across the team0~30 hours / weekAcross the brokerage's agent and admin headcount · measured as below
Manual copy-paste steps between toolsFully manual, multi-tool handoff on every transaction0Process map, before and after
Transaction status visibilityPer-agent, in each agent's own systemSingle live status per transactionPipeline dashboard, all offices

Events processed and failed-events volume are not in this table. They live in the event store and dead-letter queue and we have not yet pulled and dated an export for this page — they will be added once sourced, not shown as a placeholder or estimated.

How the 30 hours was measured. Not a survey, and not a vendor's saved-time calculator. Two steps:

1. Time-and-motion on the mapped steps. In the Blueprint phase we timed each manual handoff step with the people who actually do it — three observed repetitions per step, median taken, across the four highest-frequency transaction types.

2. Multiplied by observed frequency. Step time × the actual count of that step per month, taken from the CRM and e-sign systems, not from estimates.

Reclaimed hours = the sum of the median times for the steps that no longer exist. The tilde is doing real work in "~30 hours": this is a median-times-frequency figure with a stated method, not a stopwatch on every agent. We would rather publish the method and let you discount it than publish a precise-looking number with no method at all.

What we are not claiming. No revenue figure. No claim about deals closed, commission, or listing volume. Thirty hours reclaimed is not thirty hours of new selling — some of it is people going home on time, and that is a legitimate outcome we are not going to dress up as pipeline.

The question this engagement had to answer

*Why does a brokerage need three people to know whether a deal is actually progressing?*

The brokerage was not short of software. It had a CRM, portal feeds, an e-signature tool, an accounting package and WhatsApp, and every one of them worked. The failure was between them. State lived in five places, drifted, and the only reconciliation mechanism was a person retyping — which meant the true status of any transaction was whatever the most recent human remembered to update.

What was hard

Retail-grade tools with no transactional guarantees. These are consumer and SMB APIs. They rate-limit unpredictably, deliver webhooks twice, occasionally deliver them out of order, and sometimes not at all. Any automation that assumes exactly-once delivery across that surface will corrupt data within a week.

WhatsApp is a regulated channel, not a message queue. Business-initiated messages outside the 24-hour customer service window require pre-approved templates. You cannot free-text a client at 9pm because a milestone fired.

The rejected alternative. The brokerage wanted a no-code automation builder — one scenario per integration. We rejected it. Point-to-point scenarios between five systems is a graph, not a list: every new tool multiplies the connections, no scenario knows what another has done, and there is no shared idea of what a transaction *is*. It demos in a day and becomes unmaintainable in a quarter. We built a single event spine instead. It took longer and it is why the failure count is small enough to fit in a table.

What broke. A portal webhook fired twice for one price change. Our first version processed both, sent two WhatsApp notifications to the same client, and posted two CRM activity entries. Nobody was harmed and it was fixed in a day — but it is exactly the failure that erodes trust in an automation, and it is why the whole system is now keyed on idempotency.

Architecture: before and after

flowchart LR
    subgraph BEFORE["Before — agent as the integration layer"]
        A1["Portal enquiry"] --> A2["Agent reads email"]
        A2 --> A3["Retype into CRM"]
        A3 --> A4["Retype into<br/>listing sheet"]
        A4 --> A5["WhatsApp client<br/>by hand"]
        A5 --> A6["Retype into<br/>e-sign"]
        A6 --> A7["Email admin<br/>for accounting"]
        A7 --> A8["Admin retypes<br/>into accounting"]
    end

    subgraph AFTER["After — event spine"]
        B1["Portal / CRM / e-sign<br/>webhooks"] --> B2["Ingress<br/>verify, dedupe, persist raw"]
        B2 --> B3[("Event store — Postgres<br/>append-only")]
        B3 --> B4["Router<br/>event type to handlers"]
        B4 --> B5["CRM handler"]
        B4 --> B6["Notification handler<br/>WhatsApp templates"]
        B4 --> B7["Accounting handler"]
        B4 --> B8["Pipeline projection<br/>one live status"]
        B5 --> B9{"Success?"}
        B6 --> B9
        B7 --> B9
        B9 -->|No| B10["Retry — exponential<br/>backoff, capped"]
        B10 --> B4
        B9 -->|"Exhausted"| B11["Dead-letter queue<br/>alert to ops"]
    end
Architecture diagram — Mermaid source (not rendered)

The agent is no longer a node in the diagram. That is the whole change.

Idempotency and replay

sequenceDiagram
    participant S as Source system
    participant I as Ingress
    participant E as Event store
    participant H as Handler
    participant T as Target system
    S->>I: webhook (may repeat, may reorder)
    I->>I: verify signature, compute event_key
    I->>E: INSERT ... ON CONFLICT (event_key) DO NOTHING
    alt Duplicate
        E-->>I: 0 rows — already seen
        I-->>S: 200 OK, no side effects
    else New
        E-->>I: stored
        I->>H: dispatch
        H->>T: write with idempotency_key = event_key + handler_id
        alt Transient failure
            T-->>H: 5xx / rate limit
            H->>H: retry with backoff, then dead-letter
        else Success
            T-->>H: ok
            H->>E: record handler outcome
        end
    end
Architecture diagram — Mermaid source (not rendered)

Event key. Derived from source system, source event ID and a content hash — so a redelivered webhook and a replayed one collapse to the same key. Deduplication happens at insert, in the database, not in application logic where a race can slip through.

Replay is safe by construction. Every handler is keyed on event_key + handler_id. Replaying the entire event store re-runs nothing that already succeeded. That property is what makes it possible to fix a handler bug and reprocess a week of history without spamming clients.

Ordering. Handlers do not assume it. State transitions are validated against the current projection — a "price changed" event carrying older data than the projection holds is recorded and discarded, not applied.

Retries. Exponential backoff with jitter, capped attempts, then dead-letter with an alert. Rate-limit responses are honoured with their retry-after header rather than retried blindly.

Nothing silently disappears. Every event ends in one of three states: applied, deliberately discarded with a reason, or dead-lettered with an alert. There is no fourth state, and that is the property that lets a broker trust the pipeline dashboard.

The data model and WhatsApp handling

One transaction is the aggregate root. Property, client, agent, documents and milestones hang off it. Every source system's identifier is stored as an external reference against the transaction rather than the transaction being duplicated per system, so "which record is real" has one answer. The pipeline dashboard is a projection rebuilt from the event store, which means it can be regenerated from scratch and never drifts from the events that produced it.

Client notifications run on pre-approved WhatsApp Business Platform templates, one per milestone type, with variables bound from the transaction aggregate. Free-text replies from clients are handled inside the 24-hour service window and routed to the owning agent — the system never free-texts outbound. Opt-out is honoured at the transaction level and persisted, so a re-run or replay cannot resurrect a message to someone who opted out. Quiet hours are enforced in the notification handler, not left to whenever an event happens to fire.

What we did

1. Blueprint. Three weeks shadowing agents and admin staff, timing each manual step, and mapping the four highest-frequency transaction types end to end. This produced both the architecture and the baseline the 30 hours is measured against. Scope and price locked here.

2. Prototype. One event type — portal enquiry — through the full spine to CRM and notification. Built to prove idempotency and retry behaviour under deliberately duplicated and reordered webhooks, not to look good in a demo.

3. Forge. Full event catalogue, all five integrations, the transaction aggregate, the pipeline projection, and the WhatsApp template set through approval.

4. Harden. Chaos testing against the retail APIs — forced duplicates, forced reorder, forced rate limits, forced outage. Dead-letter alerting, replay tooling, and a runbook the client's own ops person can execute without us.

How we evaluated it

The test set. Not an ML eval — a correctness eval. A fixture set target of 200–400 recorded real webhook payloads across every event type — our standard coverage target for an event-spine build of this kind, not a number drawn from this engagement's own Blueprint documents, and not a count of live production events — replayed against the system in three modes: clean, duplicated, and shuffled out of order. The system must reach identical final state in all three. That is the acceptance criterion the build was signed off against.

The metric that mattered. Zero divergent final states across the three replay modes. Not throughput, not latency. A brokerage does not care whether a milestone propagates in 200ms or 3 seconds; it cares enormously whether the CRM and the accounting system disagree about a deal.

Latency, where it matters. Client-facing notifications are measured p50 and p95 from source event to WhatsApp delivery receipt. Internal propagation is measured but has a loose budget, deliberately — spending engineering effort on internal speed here would have been optimising the wrong axis.

Failure modes, and what happens when the system is wrong.

*Duplicate webhook.* Collapsed at insert. No duplicate message, no duplicate CRM entry. This is the bug that bit us and it is now structurally impossible.

*Out-of-order events.* Validated against the projection; stale updates recorded and discarded with a reason, never applied.

*Target system down or rate-limiting.* Backoff, retry, then dead-letter with an alert. The event is never lost, and it can be replayed once the target recovers.

*Bad data from a source system.* Handler rejects, event is dead-lettered with the validation error attached. Wrong data does not propagate to four systems.

*A handler bug that already wrote wrong data.* Fix the handler, replay the affected range. Idempotency keys mean correct writes are not repeated and the wrong ones are corrected.

*Notification sent in error.* This is the residual risk we cannot fully engineer away. Mitigated by quiet hours, template constraints and a per-transaction notification log an agent can inspect before they call a client back.

What we would do differently

We would have built replay tooling in the Prototype phase. We built it in Harden. Between those two points we fixed handler bugs by hand-writing correction scripts, which is slow and is exactly how you introduce a second bug.

We should have run the WhatsApp template approvals in week one. Template review is an external dependency with its own clock, and we discovered its lead time when it was on the critical path rather than before.

The 30-hour baseline should have been agreed in writing with the client before we timed anything. We timed it properly, but "which steps count" was settled after the measurement rather than before, and that is the wrong order for a number that ends up on a public page.

We over-built the pipeline projection's filtering. Agents use three views. We built eleven. That was us designing for a brokerage we imagined rather than the one in front of us.

What is still running

Live across all offices. Open workstreams: filling the event-volume and dead-letter figures on this page from the event store, adding leasing renewals as an event type, and moving the dead-letter runbook fully to the client's ops team so they stop needing us for replays.

//Results

The numbers

0
manual copy-paste steps between tools, down from a fully manual, multi-tool handoff on every transaction
Single
live transaction status per deal, up from per-agent status in each agent's own system

Verification —Every mechanism here is in the running system and can be demonstrated live, including a replay against the recorded fixture set. The 200–400 fixture-set target is our standard house engineering target for an event-spine build of this kind, not a number drawn from this engagement's own Blueprint documents — replace it with this engagement's real figure if and when it can be sourced. The ~30 hours/week figure is a time-and-motion estimate — mapped steps × observed frequency — and is withheld from the headline results until the agent and admin-staff headcounts it is computed across are confirmed; the process map and timing method are documented artefacts and can be shared under NDA. The client-side figures this page does not state — agent and admin headcount, event volumes — will be filled from the event store and the client's own records, or left out.

//Tech stack
  • Postgres (event store)
  • Durable queue (exponential backoff)
  • Webhook ingress
  • WhatsApp Business Platform
  • HubSpot

Engagement led by

Prabal Bhandari

Founder & Chief Executive Officer · MS Computer Science, Georgia Tech

· Updated

//FAQ

Questions, answered straight

It scales with the number of manual handoffs per transaction and your transaction volume, not with headcount. A small team doing high volume across many disconnected tools reclaims more than a large team with two well-integrated systems. We map and time it before quoting anything.

//Next step

Start with a Blueprint.
Lock the scope. Ship the system.

Contact the studio

Not sure you're ready? Take the Build Readiness Scorecard