All Docs
FeaturesNurtureHubUpdated March 20, 2026

Behind the Build: CRM Integration Schema (v1.0.4)

Behind the Build: CRM Integration Schema

Release v1.0.4 · Engineering Deep-Dive

NurtureHub is built to sync bidirectionally with the CRMs UK property agents already use — agentOS, Reapit, Alto, Street, Loop, and custom providers via open API. Before any of that sync logic can run, the database needs to be able to answer three questions reliably:

  1. Which CRMs is this organisation connected to, and are those connections healthy?
  2. What happened during every sync operation — and if something went wrong, why?
  3. For any given contact in NurtureHub, what is their ID in each external system?

v1.0.4 answers all three by introducing three purpose-built tables.


crm_connections — Connection State

Every time an organisation connects a CRM, a row is written to crm_connections. The provider column is constrained to the six values NurtureHub supports: agentos, reapit, alto, street, loop, and custom. Credentials (API keys, OAuth tokens, etc.) are stored as encrypted JSONB so the schema stays flexible across providers without exposing secrets in plain text.

The status field (active, error, disconnected) gives the sync engine an at-a-glance signal before attempting any operation, and last_synced_at provides a reliable cursor for incremental sync runs.


crm_sync_log — Full Audit Trail

Every sync event — whether NurtureHub is pushing a contact update out to a CRM (outbound) or pulling a new record in (inbound) — writes a row to crm_sync_log. The log captures:

  • Which connection and organisation the operation belongs to
  • The type of entity being synced (entity_type)
  • The record's ID in both the external system (external_id) and NurtureHub (internal_id)
  • The outcome (status) and, on failure, the error detail

This makes debugging a failed sync straightforward: filter by connectionId and status = error and you have a complete picture of what went wrong and for which records.


contact_external_ids — Identity Mapping

A single landlord might appear in both Reapit and agentOS. contact_external_ids solves the identity problem by storing one row per (contact, provider) pair, so NurtureHub can unambiguously resolve a contact's ID regardless of which CRM initiates a sync event. This table is the glue between NurtureHub's internal contact graph and the external world.


What This Enables

This schema work is entirely foundational — there are no user-facing changes in v1.0.4. What it unlocks is the ability to build the live sync engine on top of a solid, auditable data model:

  • Incremental syncs driven by last_synced_at
  • Retry logic informed by the sync log's error column
  • Multi-CRM contacts resolved cleanly via contact_external_ids
  • Health monitoring surfaced to agents via the status field on each connection

The CRM sync features themselves will follow in subsequent releases.