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

# @adrper79-dot/crm

> Lead tracking, conversion events, and customer health view.

# @adrper79-dot/crm

CRM primitives: lead upsert, subscription conversion tracking, and a customer health view with churn risk scoring. Depends on `@adrper79-dot/neon` and `@adrper79-dot/analytics`.

## Installation

```bash theme={null}
npm install @adrper79-dot/crm
```

## Database

```typescript theme={null}
import { CREATE_CRM_LEADS_TABLE } from '@adrper79-dot/crm';
await db.execute(CREATE_CRM_LEADS_TABLE);
```

Schema:

```sql theme={null}
CREATE TABLE IF NOT EXISTS crm_leads (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id     TEXT NOT NULL,
  app_id      TEXT NOT NULL,
  email       TEXT,
  status      TEXT DEFAULT 'lead',
  mrr         NUMERIC DEFAULT 0,
  source      TEXT,
  metadata    JSONB DEFAULT '{}',
  created_at  TIMESTAMPTZ DEFAULT now(),
  updated_at  TIMESTAMPTZ DEFAULT now(),
  UNIQUE (user_id, app_id)
);
```

## API

### `trackLead(db, opts)`

Upserts a CRM lead. Idempotent on `(user_id, app_id)` conflict.

```typescript theme={null}
function trackLead(db: Db, opts: {
  userId: string;
  appId: string;
  email?: string;
  status?: 'lead' | 'trial' | 'active' | 'churned';
  source?: string;
  metadata?: Record<string, unknown>;
}): Promise<CrmLead>;
```

### `trackConversion(db, opts, analytics?)`

Updates a lead's status and MRR. Optionally fires a `subscription.converted` event to `@adrper79-dot/analytics`.

```typescript theme={null}
function trackConversion(
  db: Db,
  opts: {
    userId: string;
    appId: string;
    status: 'trial' | 'active' | 'churned';
    mrr: number;
  },
  analytics?: AnalyticsClient,
): Promise<CrmLead>;
```

### `getCustomerView(db, userId)`

Returns a unified customer view: lead + subscriptions + recent events + churn risk score.

```typescript theme={null}
function getCustomerView(db: Db, userId: string): Promise<CustomerView>;

interface CustomerView {
  lead: CrmLead;
  subscriptions: Subscription[];
  recentEvents: FactoryEvent[];
  churnRisk: 'low' | 'medium' | 'high';
}
```

Churn risk is computed from days since last event and subscription status.

## Example

```typescript theme={null}
import { trackLead, trackConversion, getCustomerView } from '@adrper79-dot/crm';

// On sign-up
await trackLead(db, {
  userId: user.id,
  appId: 'focusbro',
  email: user.email,
  status: 'lead',
  source: 'google-ads',
});

// On subscription
await trackConversion(db, {
  userId: user.id,
  appId: 'focusbro',
  status: 'active',
  mrr: 29,
});

// Admin dashboard
const view = await getCustomerView(db, user.id);
console.log(view.churnRisk); // 'low' | 'medium' | 'high'
```
