Yardi Voyager to HubSpot through a Microsoft Fabric lakehouse

A five-brand senior living operator in Atlantic Canada

  • Senior living
  • Atlantic Canada
  • Integration, live since 2026
Contacts under sync
72,000
Deals in the acquisition pipeline
45,000
Timeline events sent
260,000
Properties reconciled each run
133
Live since
August 2026

A senior living operator running five brands kept every prospect, tour and resident record in Yardi Voyager, and ran marketing in HubSpot with none of it. We built a two way sync through a Microsoft Fabric lakehouse inside the operator’s own tenant. It reads Yardi every 6 hours, writes website leads, contact edits and marketing activity back hourly, and keys every record on the Yardi identifier rather than on the email address.

01 The Challenge

The operator runs five brands across a portfolio of senior living communities. Every enquiry, tour, move in and move out is recorded in Yardi Voyager. The marketing team worked in HubSpot and could see none of it, so campaign lists were built by hand from exports, and nobody on either side could tell whether a person had already been contacted.

The source system decided most of the design. The senior housing interface is SOAP, and it is a fixed contract: it exposes the operations it exposes, and there is no route around them. It has no webhooks and no changed since push, so anything in the middle has to poll and remember where it read up to. Email cannot be the identity key, because family members share an address and the source system opens a second card when a person returns after a gap. The interface has no operation that sets a prospect’s status, so the sync could move data in both directions but could never move a prospect through the funnel from the HubSpot side. And the source carries fields that must never reach a marketing system, so we built the mapping outward from an exclusion list rather than from everything the schema offers.

02 The Solution

We put a lakehouse between the two systems and gave it all the work. Inbound, a pipeline runs four notebooks in a fixed order, each gated on the one before it: property configuration first, then prospects, then residents, then the move events. Raw responses land in a bronze layer exactly as the source returned them, a silver layer holds the cleaned rows, and a gold layer holds the sync’s own memory. That memory is the watermarks, the ledgers, the property routing table and a conflicts table.

Contacts are upserted on the Yardi contact identifier and deals on the prospect identifier, both held as unique HubSpot properties. Prospect status sets the deal stage on creation only, so a consultant who moves a deal keeps that move. Lead status passes through as the source writes it, and a value that does not fit a HubSpot dropdown lands in the conflicts table for review, rather than failing the run or being dropped in silence.

Outbound is a staged pipeline. Three feeds collect work: submissions to forms on an allow list, edits to watched fields on contacts already linked to Yardi, and the marketing email sends and form fills that belong on the prospect card. Each change is staged in a queue, transformed, then put through four gates before anything is written. The gates are a master write switch, an environment lock that refuses a live write from the test path, a blackout guard for the source system’s maintenance window, and a portal gate that refuses HubSpot writes unless the connected portal is the production one. Every write is read back to confirm it landed, then recorded in a ledger, and that ledger is what makes a re-run safe.

03 The Outcome

The sync has run in production since August 2026. It holds about 72,000 contacts and about 45,000 deals in the resident acquisition pipeline, and it has sent about 260,000 timeline events, which is the prospect and tour history that used to exist only inside the property system. Each inbound run reconciles 133 properties against the marketing routing list, so a community that appears in the source but has not been classified for marketing shows up as drift rather than as silence.

Which communities sync is a row in that routing table, not a line of code, so switching one on is a configuration change and the next scheduled run picks it up. The first run for a new community brings its whole history at once, which is worth planning for. The conflicts table has proved the most useful of the gold tables in practice. It is where a vocabulary change in the source system surfaces, before anyone notices a gap in a report.

How the lifecycle runs now

  1. Enquiry

    Website forms in HubSpot

    1. Source: Website form submitted in HubSpot Next: Allow-listed form collected for the outbound queue.
    2. Allow-listed form collected for the outbound queue Next: Staged in the queue and transformed, in Lead in Yardi.
  2. Lead in Yardi

    Outbound, hourly, through four gates

    1. In Microsoft Fabric: Staged in the queue and transformed Next: All four safety gates pass?.
    2. Decision: All four safety gates pass? Yes: Prospect created in Yardi, read back to confirm. No: Refused before anything is written.
    3. In Yardi Voyager: Prospect created in Yardi, read back to confirm Next: The write is recorded in the ledger.
    4. The write is recorded in the ledger Next: New Yardi ids stamped back on the contact.
    5. In HubSpot: New Yardi ids stamped back on the contact Next: Lakehouse reads Yardi every 6 hours, in Prospect in HubSpot.
    6. Refused before anything is written
  3. Prospect in HubSpot

    Lakehouse, inbound every 6 hours

    1. In Microsoft Fabric: Lakehouse reads Yardi every 6 hours Next: Contact merged on the Yardi contact id.
    2. In HubSpot: Contact merged on the Yardi contact id Next: Deal upserted on the prospect id, stage set once.
    3. In HubSpot: Deal upserted on the prospect id, stage set once Next: Lead status fits the HubSpot dropdown?.
    4. Decision: Lead status fits the HubSpot dropdown? Yes: Lead status passes through as written. No: Value held in the conflicts table for review.
    5. In HubSpot: Lead status passes through as written Next: A consultant moves the deal, and the move holds, in Tours and marketing.
    6. Value held in the conflicts table for review Next: A consultant moves the deal, and the move holds, in Tours and marketing.
  4. Tours and marketing

    Consultants and the marketing team

    1. A consultant moves the deal, and the move holds Next: Tour and activity history as timeline events.
    2. In HubSpot: Tour and activity history as timeline events Next: Marketing email sends written to the prospect card.
    3. In Yardi Voyager: Marketing email sends written to the prospect card Next: Resident status, move in date and care level, in Move in.
  5. Move in

    Residents and move events, inbound

    1. In HubSpot: Resident status, move in date and care level Next: Move in arrives as a timeline event.
    2. Outcome, done: Move in arrives as a timeline event
The map follows a senior living enquiry from a website form, through the prospect it creates in Yardi Voyager, to tours, marketing email and move in. The lakehouse is the only component that talks to both systems.

04 Stack

The integration runs entirely inside the client’s own Microsoft Fabric tenant, on a small node pool with a dynamic executor range. The code is PySpark notebooks, with one shared utilities notebook loaded first by every other. That one holds the SOAP client, the HubSpot client, the credential fetch and the portal gate. Credentials come from a key vault at run time, so rotating one takes effect on the next run with no code change.

The inbound chain runs 4 times a day and takes about 2 hours. Activity history runs nightly on its own schedule, because its pulls are long. Outbound runs hourly, and skips cleanly inside the blackout windows. Operations is a two minute look at the run history, then the ledgers, because a run can report success without having moved anything.

05 Architecture and data model

The source is read on a schedule into the lakehouse, which is the only component that talks to both sides. Inbound, the lakehouse upserts contacts and deals and writes move and activity history as timeline events. Outbound, website form submissions and contact edits stage in a queue, pass the gates, and are written to the source, with every attempt and its result recorded in a ledger. Neither end calls the other directly, which is what lets either side be paused for maintenance without losing work. On resume, the watermarks say where to read from and the ledgers say what has already been sent.

Source objectHubSpot objectDirectionMatch key
Prospect contactContactTwo waySource contact identifier
ProspectDealTo HubSpotSource prospect identifier
ResidentContact fieldsTo HubSpotSource contact identifier
Move in, move out, transferTimeline eventTo HubSpotEvent reference in the sent ledger
Prospect activityTimeline eventTo HubSpotActivity reference in the sent ledger
Website form submissionProspectTo sourceThe integration’s own external reference
Contact editContact recordTo sourceSource contact identifier
Marketing email sendProspect activityTo sourceEngagement reference

A lead created in the source from a HubSpot form is stamped straight back onto the HubSpot contact with its new identifiers, which is what stops the sync manufacturing duplicates. The next inbound run then merges it instead of creating a second record.

The object model maps 72 contact and deal properties in total. The 10 that carry the prospect and resident story are below. Phone numbers are written as digits only, because the source accepts a formatted number and stores nothing.

Source fieldHubSpot propertyObjectDirection
ContactIDYardi contact idContactTwo way
ProspectIDYardi prospect idDealTwo way
PropertyCodeYardi property codeContactTo HubSpot
ProspectStatusDeal stageDealTo HubSpot, on creation only
SourceNameLead sourceDealTo HubSpot
HomePhoneNumberPhoneContactTwo way, digits only on write
MobilePhoneNumberMobile phoneContactTwo way, digits only on write
StatusResident statusContactTo HubSpot
MoveInDateResident move in dateContactTo HubSpot
CareLevelCodeResident care levelContactTo HubSpot
The property management system is read every 6 hours into a layered lakehouse. The lakehouse upserts HubSpot contacts on the source contact identifier and deals on the prospect identifier, and writes move and activity history as timeline events. Website leads and contact edits stage in an outbound queue, pass four safety gates, and are written back hourly. A ledger records every write, so nothing is sent twice.
Hosting
Microsoft Fabric, inside the client's own tenant
Runtime
PySpark notebooks over a lakehouse, chained by a pipeline
Cadence
Inbound every 6 hours, activity history nightly, outbound hourly
Pattern
Polling on a watermark
HubSpot
Marketing Hub Enterprise, Content Hub Enterprise, Data Hub Professional
Fields mapped
72

72 contact and deal properties in the object model, keyed on the source contact identifier for contacts and the prospect identifier for deals.

What we would do again, and differently

What we would do again

  • Key every object on the source identifier, never on the email address.
  • Keep the list of which communities sync in a table the operator can edit, so adding one is a configuration change and not a release.

What we would do differently

  • Prove every field end to end in a test environment before mapping it, rather than trusting the published schema.
  • Agree which contact relationships are in scope before the first full load, not after it.

Systems in this build

Partnerships and accreditations

  • HubSpot Elite Solutions Partner
  • HubSpot Custom Integration Accreditation
  • HubSpot CRM Data Migration Accreditation
  • HubSpot Solutions Architecture Design Accreditation

HubSpot accreditations

  • HubSpot Onboarding Accreditation
  • HubSpot Academy Accreditations

Industry specializations

  • HubSpot Healthcare Industry Specialist
  • HubSpot Financial Services Industry Specialist
  • HubSpot Manufacturing Industry Specialist
  • HubSpot Quote to Cash Capability

Partnerships

  • Google Partner
  • Anthropic Solutions Partner

Have a system in the same shape?

Send the systems, the direction and the record volume. We reply with a scope and an effort band within one business day.

Press Enter or comma to add another system.

Add more detail (optional)

Optional. A sentence or two about the workflow is enough.

We use these answers to scope the work and for nothing else. Privacy policy.

Prefer to talk it through first? Book a technical scoping call

Talk to the people who build these

  • Nijat
  • Sean
  • Prash

Book a scoping call