All Docs
FeaturesNurtureHubUpdated March 20, 2026

Building the Foundation: Lead & Contact Data Schema in v1.0.1

Building the Foundation: Lead & Contact Data Schema in v1.0.1

v1.0.1 ships the core data layer for NurtureHub. Before any email can be generated, any lead score computed, or any CRM sync triggered, the platform needs a well-defined model for contacts, their categories, and how they relate to the CRMs agents already use. That is what this release delivers.


The 12-Category Contact Model

Every contact in NurtureHub is assigned to one of twelve predefined categories. These categories reflect the full range of relationships a UK property agent manages:

CategoryDescription
sellerProspective seller not yet on market
buyerProspective buyer not yet under offer
landlordProspective landlord not yet managed
tenant_applicantApplicant not yet in a tenancy
property_investorInvestor prospect
btl_investorBuy-to-let investor prospect
active_sellerVendor with a live instruction
active_buyerBuyer with an accepted offer or under offer
managed_landlordLandlord with a managed property
active_tenantCurrent tenant in an active tenancy
active_property_investorInvestor with an active relationship
active_btl_investorBTL investor with an active relationship

Contacts are additionally grouped as either prospect or existing_client, allowing segmentation across all twelve categories.

Assignment History

Category assignments are not simply a field on the contact record. They are stored in a dedicated lead_category_assignments table with full history. When a contact is re-categorised, the previous assignment is marked with supersededAt and a new active assignment is created. This means you always have a complete audit trail of how a contact's status evolved over time.


GDPR-First Contact Storage

Every contact record carries:

  • consentStatus — one of granted, denied, unknown, or pending
  • consentTimestamp — when consent was recorded
  • importSource — how the contact entered NurtureHub (crm_sync, manual, apify, or land_registry)

The contacts.delete tRPC procedure performs a hard delete of all personal data and is restricted to org admins only. This is the compliance endpoint for handling data subject erasure requests under UK GDPR.

The raw CRM payload is preserved on the contact record for debugging and re-sync purposes, but is never exposed in URLs or logs.


CRM Integration Architecture

The release introduces two tables that together model the full lifecycle of a CRM integration.

crm_providers — The CRM Registry

This table is a static registry of the CRM platforms NurtureHub supports. The initial seed populates five entries:

ProviderAuth Type
agentOSAPI key / webhook push
ReapitOAuth2
AltoAPI key
StreetAPI key
LoopAPI key

Each provider record includes its supported auth type and webhook path.

crm_connections — Per-Tenant Connections

When an agent connects their CRM, a row is created in crm_connections scoped to their tenantId (organisation). Key fields:

  • encryptedCredentials — API keys or OAuth tokens stored AES-256 encrypted at rest.
  • syncCursor (jsonb) — Provider-specific delta cursor, enabling incremental sync rather than full re-fetches.
  • status — One of active, paused, error, or pending.
  • Error tracking — Last error message and timestamp for surfacing connection health in the UI.

crm_writeback_queue — Outbound Activity Sync

NurtureHub writes engagement events back to the source CRM so that agents have a full view without leaving their existing tool. The queue handles six event types:

  • email_sent
  • email_opened
  • email_clicked
  • lead_score_update
  • journey_completed
  • hot_alert_fired

Each queue entry tracks its own processing status (pendingprocessingsuccess / failed), enabling retries and observability.


The contacts tRPC Router

All contact operations are exposed through a type-safe tRPC router. Here is a summary of every available procedure:

contacts.list             — paginated list with filters
contacts.get              — single contact + active assignment
contacts.create           — create with optional category assignment
contacts.update           — update; handles re-categorisation history
contacts.assignCategory   — explicit (re)assignment
contacts.getCategoryHistory — full assignment history
contacts.delete           — GDPR hard-delete (admin only)
contacts.listCrmProviders — available CRM providers for connection UI

Every mutation produces an audit log entry. The delete procedure is guarded by adminProcedure — only org owners and admins can invoke it.


Running the CRM Provider Seed

After running your initial database migration, populate the crm_providers table with the five supported integrations:

npx tsx src/db/seeds/crm-providers.ts

This is a one-time operation. The seed is idempotent — running it again will not create duplicate rows.


What This Enables

With this data layer in place, subsequent releases can build directly on top of it:

  • Nurture sequence generation — the AI email writer reads the contact's category to tailor content.
  • Lead scoring — score updates are written back through the crm_writeback_queue.
  • Hot lead alerts — the hot_alert_fired event type is already wired into the writeback queue.
  • CRM sync — incremental sync via syncCursor keeps contact data current without full re-fetches.