# CRM Integration

Rox relies on its System of Record (SOR) — a unified knowledge graph — to interface between our agents and a customer’s public and private data. In practice, the most business-critical private data already lives in the customer’s CRM. Because we meet customers where they are, we integrate deeply with their CRM and build our SOR by performing entity resolution across both public and private sources.

Rox treats any CRM as just another data source feeding the SOR. Today we support Salesforce and HubSpot; the platform is designed to extend to other CRMs with configuration.

### Why We’re Doing This

Rox is taking over revenue operations for Global 2000 enterprises. To earn that right, we meet customers in their existing stack—CRMs synced to a warehouse of choice—and we write back anything enriched or edited in Rox to their CRM. We’re confident because the value our agents deliver is immediate and compounding. As that value becomes obvious, customers will graduate to a warehouse-native future where Rox writes directly to their warehouse (and let’s be honest: in that future, Rox is the CRM).

### Requirements

We support two essential capabilities:

1. **Ingest → SOR.** Bring data from the external CRM into Rox’s SOR.
2. **Write-back → CRM.** Persist enriched/edited data from Rox to the customer’s CRM (which, at large enterprises, is also mirrored into their data warehouse).

### Challenges (What Makes This Non-Trivial)

1. **Initial ingestion resolution.** Reconcile CRM data with existing public + private sources in Rox (including Rox-native entities).
2. **Agentic CRM behavior.** Users must be able to:
   * Edit data in Rox and have it **bi-directionally** synced to the CRM.
   * Write back agent-generated enrichments so data stays consistent without users babysitting their legacy CRM.
3. **Change-origin disambiguation.** When CRM changes come back through ingestion, we must deterministically identify whether each change originated in the CRM or in Rox (and was written back) for resolving duplicates across both data systems.
4. **Always-current “latest state.”** Sellers and agents should see the most recent, accurate value across Rox and the CRM **without any manual refresh**.
5. **Read-only insights, written back.** Customers often want Rox read-only columns (e.g., **Clever Columns**, firmographics) materialized into CRM fields for warehouse consistency—requiring reliable **bulk** write-backs.
6. **CRM-agnostic mapping.** Provide a mapping layer so admins can map Rox fields to CRM fields with uni- or bi-directional sync semantics.

### How We Do It

> See our SOR overview for ingestion fundamentals and graph construction; this section focuses on how write-backs and sync semantics integrate with that layer.

We organize the integration in **three layers**:

1. **Mappings Layer** — field mapping + validation + sync direction.
2. **Real-Time Layer** — transactional write-back for user edits, with automatic latest-state resolution.
3. **Batch Layer** — high-volume, uni-directional write-backs (e.g., Clever Columns) and safe reconciliation of pending real-time writes.

<figure><img src="https://2986926806-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUBzWA2LkfjbdaffIaGlN%2Fuploads%2FIprey7sQHvzYJa4EOYCR%2FScreenshot%202025-09-16%20at%203.31.52%E2%80%AFPM.png?alt=media&#x26;token=f1aac79e-628a-4f3e-a074-cc4d6105aebe" alt=""><figcaption></figcaption></figure>

#### 1) Mappings Layer

Admins configure mappings from **Rox fields → CRM fields** (or create an equivalent Rox field to mirror an existing CRM field). Each mapped field carries:

* **Sync Direction:**
  * *Read-only in Rox → CRM* (uni-directional; Rox is source of truth)
  * *Bi-directional* (edits in Rox and CRM propagate both ways)
* **Validation Rules (type-aware):** Some examples include (extending to more complex rules in the future)
  * `NUMBER`: min/max;
  * `TEXT`: max length;
  * `nullable`: allowed or not.

When users update mapped fields, Rox enforces these validations for any update intended to persist **both** in Rox and the CRM.

#### 2) Real-Time Layer (Automatic Latest-State)

**The clock problem.** CRM and Rox clocks are not guaranteed to be synchronized, so raw timestamps aren’t a safe basis for “who wins.” We avoid NTP-style clock alignment and instead implement an **optimistic concurrency** strategy that does not depend on wall clocks.

**Pre-write latest-state check (automatic).** On every UI write:

1. **Live fetch from CRM.** Rox fetches the current CRM object relevant to the fields being changed.
   * If the fetch **fails**, we treat Rox as the latest state, accept the user’s change in Rox, and immediately attempt the CRM write. If the CRM write still fails, we mark the change as a **pending CRM write** (see below).
   * If the fetch **succeeds**, we compare the CRM’s current state against the user’s **edit base** (the values the user just saw in Rox’s UI). We compute and store a **snapshot** of the CRM object (or a stable hash of the mapped fields) alongside the user’s intended change.
2. **Conflict handling:**
   * **Mismatch (CRM changed since the user last saw it):** We **abort** the write, show the freshly fetched CRM values, and implicitly offer **force-overwrite** over the freshly fetched latest state.
   * **Match (CRM unchanged):** We proceed with a real-time write to the CRM.
3. **Real-time commit semantics:**
   * **CRM write succeeds →** commit in Rox and record an audit entry.
   * **CRM write fails (validation, 429/5xx, network) →** we do **not** discard the user’s intent. We persist the change in Rox with a **“pending CRM write”** status and enqueue it for the batch reconciler together with the CRM **snapshot** captured at edit time.

**Why this is safe without clock sync.** We never rely on “which timestamp is newer.” We rely on **read-check-write**: if the CRM object has changed since the user formed their edit, we don’t apply a stale overwrite. If the user explicitly chooses **force-overwrite** on the freshly fetched state, Rox will attempt to prevail, and if the real-time write still fails, the batch process will take over (see below).

#### 3) Batch Layer

This layer handles two things:

1. **Uni-directional write-backs**—especially **Clever Columns** generated by Rox agents—which must be materialized into CRM fields so customers have them in their warehouse.
   1. **Bulk CRM APIs.** We use the CRM’s bulk endpoints to stay well below org-level rate limits.
   2. **Cell-level sync timestamps.** Rox tracks a per-cell “last synced at” time. We select all cells updated since last sync, transform via mappings, and build bulk payloads.
   3. **Large payload safety.** For heavy `TEXT` columns (KBs per cell), we batch DB reads to bring **only** what’s required into memory and avoid OOM at scale.
   4. **Auditable write-backs.** Every write-back is recorded in our **sharded Postgres** within the same transaction—capturing both code exceptions and CRM-returned validation errors.
   5. **Cadence & retries.** A CRON runs nearly **every 15 minutes**. Double-writes aren’t harmful in this path — we prioritize **eventual consistency** and reliability since CRM-side changes aren’t expected to conflict with Rox-sourced enrichments.
2. **Reconciliation of pending real-time writes** (from point (**3)** from the Real-time Layer):
   1. When the user attempted the edit, Rox stored a **snapshot** of the CRM object (or a hash of the mapped fields) and the intended change.
   2. On the next batch run we **fetch the current CRM object** and compare it to the stored snapshot:
      1. **Different from snapshot →** **skip** the pending write. This is equivalent to “had the real-time write succeeded at the moment of the user’s edit, a later CRM change would have superseded it anyway,” so we do not clobber the newer CRM change.
      2. **Identical to snapshot →** **apply** the pending write via the CRM’s bulk/standard API and update audit logs accordingly.

> This gives us “exactly-once(ish)” semantics for user edits without clock synchronization: a pending write only lands if the CRM record is still the same record the user edited against.

### Operational Guarantees (At a Glance)

* **Optimistic concurrency** for real-time writes (snapshot/compare, not clock-based)
* **Best-effort atomicity** *plus* **no-loss semantics**: when CRM rejects a real-time write, the user’s intent is preserved in Rox and reconciled by batch
* **Deterministic reconciliation** of pending writes using snapshot comparison
* **Idempotent ingestion** via explicit **Rox ↔ CRM ID mappings** stored in both systems
* **Scalable batch write-backs** with per-cell checkpoints and safe replays
* **CRM-agnostic** mappings + validations

### Security & Data Stewardship

* **Least-privilege CRM scopes** for read/write.
* **Audit logs** for every write-back (who/what/when/result), retained in Rox’s sharded Postgres.
* **Warehouse-native posture**: customers can move toward Rox writing directly to their warehouse as trust grows.

### Admin UX Summary

* Configure **field mappings** (Rox ↔ CRM) and **sync direction** per field.
* Define **validation** (min/max, length, nullable).
* Choose which **Clever Columns**/read-only fields should be **materialized** in CRM.
* Monitor **write-back health** via audit logs and per-field sync status, including **pending CRM writes** and reconciliation outcomes.

### Closing

This architecture came from hard-won iteration with customers. If you want to help us evolve this into the definitive system for revenue operations—and build software that delivers real value—join us via our [company](https://www.rox.com/company) page.
