> ## Documentation Index
> Fetch the complete documentation index at: https://rollout.mintlify.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Have a Follow Up Boss Integration? Start Here

### Add Every Real Estate CRM Integration in \< 1 Hour (While Keeping Your Existing FUB Code)

**Scenario:** Your product currently talks to **Follow Up Boss (FUB)** *directly* via the FUB API. You want to add other CRMs (e.g., **BoldTrail, Lofty, Brivity, CINC, Sierra Interactive, Real Geeks, HubSpot, HighLevel**, etc.) without refactoring your data model.

**Outcome:** In under an hour, Connect more CRMs using **Rollout**. Keep your FUB integration as‑is. Rollout normalizes the rest; you add a thin adapter that maps Rollout records to your existing FUB‑shaped objects and routes webhook events into your current pipelines.

### What You’ll Build

1. **Credential connect UI** to add non‑FUB CRMs.

2. **Routing switch** that calls Rollout for non‑FUB tenants.

3. **One webhook endpoint** for Rollout events that feeds your existing FUB upsert logic.

Keep using native FUB API for FUB customers. Use Rollout only for the *other* CRMs.

### Prereqs (5–10 min)

* Create a Rollout app (Client ID/Secret). Add:

  * `ROLLOUT_CLIENT_ID`, `ROLLOUT_CLIENT_SECRET`

  * `ROLLOUT_WEBHOOK_SECRET`

* Small table for Rollout connections: `tenant_id`, `connection_id`, `category='crm'`, `provider`, `status`.

### Step 1 — Drop in the Connect UI (5–10 min)

Render Rollout’s credential manager on your Integrations page so customers can add CRMs beyond FUB.

```typescript theme={null}
// IntegrationsPage.jsx
<CredentialsManager
  apiCategories={{ crm: true }}
  onConnected={(conn) => saveConnection({ tenantId, connectionId: conn.id, category: 'crm' })}
/>
```

* Store `connection_id` when the OAuth/API‑key flow finishes.

### Step 2 — Update your FUB Code for Reads/Writes (10–15 min)

Keep your **FUB** client for FUB tenants. For *other CRMs*, call **Rollout** with the **same FUB‑shaped payloads** and responses.

```typescript theme={null}
// peopleService.ts (minimal, no client wrapper)
import fetch from 'node-fetch';

async function rolloutHeaders(tenantId: string, credentialId: string) {
  const authToken = await getRolloutToken(tenantId); // server-side per Rollout docs
  return {
    Authorization: `Bearer ${authToken}`,
    'x-rollout-credential-id': credentialId,
    'Content-Type': 'application/json'
  };
}

export async function listPeople(tenantId: string) {
  const cred = await getActiveCrmCredential(tenantId);
  if (cred?.provider === 'fub') return fubClient.listPeople();
  const res = await fetch('https://crm.universal.rollout.com/api/people', {
    headers: await rolloutHeaders(tenantId, cred.credentialId)
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data.error || 'Failed to fetch people');
  return data.items; // same shape you already use
}

export async function createPerson(tenantId: string, fubPayload: any) {
  const cred = await getActiveCrmCredential(tenantId);
  if (cred?.provider === 'fub') return fubClient.createPerson(fubPayload);
  const res = await fetch('https://crm.universal.rollout.com/api/people', {
    method: 'POST',
    headers: await rolloutHeaders(tenantId, cred.credentialId),
    body: JSON.stringify(fubPayload)
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data.error || 'Failed to create person');
  return data; // same shape
}
```

Tip: Keep your domain types untouched. Just pick the right client based on the active connection.

### Step 3 — One Webhook for All Non‑FUB CRMs (10 min)

Subscribe connections to Rollout webhooks. Verify HMAC, fetch via `uri`, then run your existing FUB upsert.

```typescript theme={null}
// webhooks/rollout.ts
app.post('/webhooks/rollout', verifyHmac(ROLLOUT_WEBHOOK_SECRET), async (req, res) => {
  const { event, uri } = req.body; // e.g., peopleCreated, peopleUpdated, peopleDeleted
  // Identify the tenant & related credential for this webhook (use your own mapping)
  const { tenantId, credentialId } = await resolveTenantAndCredential(req);

  const resp = await fetch(uri.startsWith('http') ? uri : `https://crm.universal.rollout.com${uri}`, {
    headers: await rolloutHeaders(tenantId, credentialId)
  });
  const data = await resp.json();
  if (!resp.ok) throw new Error(data.error || 'Fetch via webhook URI failed');

  for (const item of data.items) {
    await upsertPerson(item); // same handler you use for FUB
  }
  res.sendStatus(200);
});
```

Event names and payloads match your FUB expectations for CRM entities.

### Optional — Extra Entities (5 min)

If you already support FUB **tasks/notes/emails/calls**, enable them for Rollout connections and reuse the same code paths (same shapes & event names).
