---
name: debt-recovery-platform
description: "Build and operate a Texas-based debt recovery platform — customer portal, AI claim scoring, online notarization, certified mail service, DocuSeal signing, payment processing, case management, and Agent D.R.E chatbot. For Debt Recovery Experts (DRE) at debtrecoveryexperts.com."
version: 2.5.0
author: Sho'Nuff
tags: [dre, debt-recovery, texas, portal, ai-scoring, ron, docuseal, letterstream, stripe, chatbot]
related_skills: [infrastructure-automation, email-workflows, cloudflare-dns-and-domains, docker-service-deployment, hermes-agent]
---

# Debt Recovery Platform (DRE)

Build a zero-touch debt recovery platform where contractors/owner-operators submit claims, DRE reviews and collects on their behalf via Limited Power of Attorney, and disburses funds minus fees.

## Architecture

### Core Data Model

- **Claim ID**: `DRE-YYYY-NNNN` (auto-incrementing)
- **Client ID**: `CLT-YYYY-NNNN` (per-account, linked to claims)
- Both generated on first submission of a new claim or signup

### Tiered Recovery Progression

| Tier | Action | Timeline | Service |
|------|--------|----------|---------|
| 1 | Soft Touch — email + ACH link | Day 1-5 | Stripe |
| 2 | Formal Demand — certified mail | Day 7-14 | LetterStream |
| 2.5 | Lien Threat — pre-lien notice (construction only) | Day 15-21 | DRE-led, attorney if filed |
| 3 | Escalation — final notice, intent to litigate | Day 21-30 | LetterStream |
| 4 | Legal Action — referral to partner law firm | Day 30+ | Referral agreement |

**Texas mechanic's lien:** Pre-lien notice alone resolves ~70% of construction claims. Deploy as Tier 2.5.

### Fee Structure

| Tier | DRE Fee | Client Gets | When |
|------|---------|-------------|------|
| 1 | 20-25% | 75-80% | Early resolution |
| 2 | 30% | 70% | After certified mail |
| 2.5 | 30% (+ attorney if lien filed) | 70% | Construction claims |
| 3 | 33% | 67% | After escalation |
| 4 | 10% DRE + 25% law firm | 65% | Litigation |

- Texas has **no statutory cap** on contingency fees
- Costs (notary ~$25-35, certified mail ~$8.34, filing fees $250-400) passed to debtor or deducted from proceeds
- **All fees must be disclosed in the LPOA** per Texas Finance Code

### AI Claim Analysis

**Per claim, not per client.** Each claim gets independent:

- **Score** (0-100) based on documentation quality, debtor profile, legal standing, amount reasonableness
- **Debtor Research** via ScrapingAnt + Sherlock — LinkedIn, property records, legal history, asset scan
- **Weakness Analysis** — what could go wrong during recovery
- **Case Comparison** — matched against similar TX civil cases, avg recovery rate (72%), median time (34 days)
- **Re-analysis on new document upload** — score updates, logged in change history

**Private data NEVER exposed to chatbot:**
- AI scoring methodology, skip tracing techniques, internal weakness criteria, approval workflow, specific case data, partner firm names, fee negotiation limits, FDCPA/TDCA compliance strategy, internal notes

### Case Binders (on close)

| Binder | Contents | Recipient |
|--------|----------|-----------|
| **DRE Internal** | Intake, evidence, AI analysis, AI debtor research, AI weakness, internal notes, signed/notarized LPOA, certified mail receipts, comm log, settlement, fee disclosure, payment confirmation | DRE archive |
| **Client Package** | Claim summary, signed/notarized LPOA, settlement/payment confirmation, fee statement. **EXCLUDES AI analysis, debtor research, internal notes** | Client portal |
| **Evidence Archive** | Original uploaded documents (ZIP) | Both |

### Audit Trail (Change Tracking)

Any edit to a claim tracks: what field changed (old→new), who made it, when, and why. Visible in internal dashboard. Closed claims are read-only. Included in DRE Internal Binder.

### Approval Workflow

Two distinct approval workflows exist in the DRE platform:

#### 1. Case-Level Approval (claim progression)

- **Approvers:** Germaine + Tony — both must sign off before case moves forward
- **Anita:** Read-only access — can view pending approvals but cannot approve
- **Reminders:** Every 60 minutes, pending approval notification sent to both approvers
- Status badges: "Pending Germaine · Pending Tony · Acknowledged ✓"

#### 2. Demand Letter Approval (letter lifecycle)

- **Anita:** Authors letters, submits for approval (draft → needs-approval)
- **Tony** or **Germaine:** Single-approver model — either can approve (needs-approval → ready-to-send) or reject (→ back to draft)
- **Letters authored by Tony/Germaine:** Auto-approved, skip the needs-approval step entirely
- Rejection requires a written reason; status returns to draft for revision

See `references/demand-letter-management-page.md` for the full demand letter lifecycle details.

## Portal Mockups

All mockups at `/root/portal-mockup/`, served via `core.itpropartner.com/capabilities/` and `portal.debtrecoveryexperts.com/`. Client-facing light theme (amber accents), internal DRE dark theme (navy/amber).

| Page | Live URL | Purpose |
|------|----------|---------|
| Internal Dashboard | `/dre-dashboard.html` | Claims queue, AI analysis, change history, case aging |
| Claim Intake | `/debt-recovery.html` | Split-screen ScrapingAnt-inspired form with Agent DRE chatbot |
| Client Dashboard | `/dre-client-dashboard.html` | Active/past claims, tier progress, Agent DRE chat, stats |
| Fee Calculator | `/dre-fee-calculator.html` | Live 4-tier fee projection widget |
| Case Aging | `/dre-case-aging.html` | Stalled case monitor with target vs actual timing |
| Debtor Pay | `/dre-pay.html` | Clean payment landing page for debtors |
| Login Page | `/login.html` | SSO-only client portal login with Cloudflare Access redirect |
| Demand Letters | `/letter-queue.html` | Letter composer with AI chat sidebar, approval workflow (Anita → Tony/Germaine), sent history |
| Analytics | `/dre-analytics.html` | AI Score Distribution, Recovery by Tier, Monthly Trend, Top Performers |

All served live at `portal.debtrecoveryexperts.com/`. Source files at `/root/portal-mockup/` and deployed copies at `/var/www/capabilities/`. Both locations must be kept in sync.

### Access Architecture

DRE pages use a **layered access model** combining Cloudflare Access (front) and Caddy (back):

| URL | Auth Layer | Accessible To |
|---|---|---|
| `portal.debtrecoveryexperts.com/` | None (public) | Anyone |
| `crm.debtrecoveryexperts.com/` | Cloudflare Access (email + optional 2FA) | Germaine, Tony, Anita |
| `internal.debtrecoveryexperts.com/` | Cloudflare Access (email + optional 2FA) | Germaine, Tony |

**Cloudflare Access** sits in front of the subdomains and intercepts all traffic before it reaches Caddy. The user logs in via email (one-time code), and Cloudflare forwards authenticated requests to the origin server.

**Role-to-Access mapping:**

| Person | Email | Can Access |
|--------|-------|-----------|
| Germaine | g@germainebrown.com | CRM + Internal |
| Tony | browo955@yahoo.com | CRM + Internal |
| Anita | anitabrownwrites@gmail.com | CRM only |

**Setup notes:**
- Subdomains must have Cloudflare proxy enabled (orange cloud) for Access to work
- Policies are email-based: each user's email is explicitly allowed
- CRM policy includes all three; Internal policy excludes Anita
- Caddy basic_auth was replaced by Cloudflare Access (no server-side auth config needed)

### Demand Letter Management Page

Single-file mockup (`/root/dre-demand-letters.html`) covering the full demand letter lifecycle: draft → needs approval → ready to send → sent. Internal DRE tool (not client-facing).

**Layout:** Two-column grid: letter composer (left) + AI chat sidebar (right, 360px).

**Approval workflow (different from case-level workflow):**

| Role | Demand Letter Permission |
|------|-------------------------|
| **Anita** | Authors letters, submits them for approval |
| **Tony** | Approves/rejects letters authored by Anita |
| **Germaine** | Approves/rejects letters authored by Anita |

Letters authored by Tony or Germaine auto-skip approval and go directly to "Ready to Send".

**AI Chat Sidebar:** Agent DRE chat with intent detection — "Make it firmer" shortens payment window and strengthens language, "Add lien language" appends UCC-1 clauses, "Shorten to 1 paragraph" condenses, "friendlier" softens. Each intent modifies letter content in the composer.

### "+ New Letter" Modal Flow (added Jul 8)

A modal-driven flow starting the letter creation from client/claim selection instead of a default placeholder. The modal has two screens:

**Screen 1 — Client Selection:**
- Searchable list of all claims with debtor name, claim #, client company name, amount, and stage badge
- Search filters by debtor name, claim #, or client company name
- Stage badges (color-coded): soft-touch=blue, formal-demand=amber, lien-threat=orange, escalation=red, legal-action=purple
- Clicking a client row advances to template selection

**Screen 2 — Template Selection:**
- Shows only templates appropriate for the client's current stage (always progresses forward, never backward)
- Stage-template mapping:
  - soft-touch → formal-demand
  - formal-demand → lien-threat, escalation
  - lien-threat → escalation, legal-action
  - escalation → legal-action
  - legal-action → (no templates — claim referred to legal counsel)
- Each template displays its icon, name, and a "Generate a [current stage] → [next stage] letter" description
- Selecting a template replaces `[CLAIM]`, `[DEBTOR]`, `[AMOUNT]`, `[CLIENT]` placeholders, appends signature, creates a new draft letter entry, pre-fills the composer, and scrolls to it
- "Back to client list" header button returns to screen 1
- Legal-action stage shows a disabled state: gavel icon + "This claim has been referred to legal counsel" message

See `references/demand-letter-management-page.md` for the legacy spec, data model, status badge conventions, chat intent patterns, and dark theme.
See `references/new-letter-modal-flow.md` for the complete "+ New Letter" modal spec including template content, client data model, stage badge CSS, and template-to-stage JS progression logic.
See `references/dre-letter-templates.md` for all 8 canned letter templates (1-3 auto, 4-8 queued for approval) with sender address, workflow instructions, and payment link.
See `references/dre-imap-poller-spec.md` for the unified inbox IMAP poller — polls dre@ + collections@ every 60s, writes to /var/www/internal/data/dre-mails.json, surfaced via /inbox.html (Inbox nav link on all pages as of Jul 8).

### Design Conventions

All use Tailwind CSS. **Client-facing:** light bg (#f1f5f9), amber accents, white cards. **Internal:** dark theme (#0f172a bg, #1e293b cards). **Tooltips:** pure CSS `[data-tip]` attribute. **Responsive:** Tailwind mobile-first.

### Navigation Header Convention

Every DRE portal page MUST have a consistent nav header at the top. **Client-facing pages** (intake form, client dashboard, fee calculator, pay page): amber logo + "Debt Recovery Experts" brand name, white header, amber hover on nav links. **Internal pages** (claims dashboard, case aging): dark header matching body theme, amber-600 logo, amber hover on nav links.

Nav link pattern (client-facing):
```css
.nav-link { font-size:13px; color:#64748b; text-decoration:none; transition:color 0.15s; }
.nav-link:hover { color:#d97706; }
.nav-link.active { color:#d97706; font-weight:500; }
```

Nav link pattern (internal):
```css
.nav-link { font-size:13px; color:#94a3b8; text-decoration:none; transition:color 0.15s; }
.nav-link:hover { color:#f59e0b; }
.nav-link.active { color:#f59e0b; font-weight:500; }
```

**All pages must cross-link to the other portal pages** — no `href="#"` placeholders. Every nav link should point to a real path under `portal.debtrecoveryexperts.com/`.

### Persistent Navigation Header — All Pages

**Every DRE portal page MUST have the same nav header at the top.** The nav bar is always visible regardless of which page the user is on. Never omit it — even landing pages, error pages, and single-purpose pages get the same header.

**6 nav links as of Jul 8, 2026 — "Clients" removed, "Analytics" added:**
1. Home → `/index.html`
2. Claims → `/dre-dashboard.html`
3. Aging → `/dre-case-aging.html`
4. Letters → `/letter-queue.html`
5. Inbox → `/inbox.html`
6. Analytics → `/dre-analytics.html`

**"Clients" removed** — DRE team doesn't use this nav link. The client view page (`dre-client-dashboard.html`) is a reference preview, not a nav destination. If you add it, the user will tell you to remove it.

**"Analytics" added** — AI Analysis & Metrics dashboard with mock data. Includes AI Score Distribution bar chart, Recovery by Tier table, Monthly Recovery Trend bars, and Top Performers table. Uses the same dark theme (#0f172a bg, #1e293b cards, #f59e0b amber accent) as other internal pages. New pages get added to the nav on ALL existing pages in one batch.

**Active page:** Set `class="nav-link active"` on the link matching the current page. Only one page is active at a time.

**Nav link CSS:**
```css
.nav-link { font-size:13px; color:#94a3b8; text-decoration:none; transition:color 0.15s; }
.nav-link:hover { color:#f59e0b; }
.nav-link.active { color:#f59e0b; font-weight:500; }
```

**DRE Logo** — Concept C with colored pipe separators:
```html
<div class="w-8 h-8 rounded-lg flex items-center justify-center font-bold text-base tracking-tight bg-[#1e293b]">
  <span class="text-amber-500">D</span><span class="text-gray-400">|</span><span class="text-slate-400">R</span><span class="text-gray-400">|</span><span class="text-amber-500">E</span>
</div>
```

Logo color rules:
- **D** = amber-500 (#f59e0b) — always
- **|** (pipes) = gray-400 (#9ca3af) — always, on both light and dark
- **R** = slate-400 (#94a3b8) on dark bg; slate-500 (#64748b) on white bg
- **E** = amber-500 (#f59e0b) on dark; amber-600 (#d97706) on white bg
- Logo background: bg-[#1e293b] on dark; bg-white on light

Brand text next to logo: "DEBT Recovery Experts" (full caps DEBT, normal case Recovery Experts).

**Dark theme header** (most internal pages):
```html
<header class="bg-[#0f172a] border-b border-[#1e293b] px-4 md:px-6 py-3 flex items-center justify-between">
```

**Light theme header** (client-facing pages):
```html
<header class="bg-white border-b border-gray-200 px-4 md:px-6 py-3 flex items-center justify-between">
```

Light theme nav-link colors use gray-500/amber-600 instead of gray-400/amber-500 for contrast on white.

### Empty-State Pattern

When a page currently has test/demo data rows, the actual page should default to an empty state:
- Centered column with a relevant SVG icon
- "No [items] yet" heading
- "Claims will appear here once..." explanation text
- CTA button linking to the intake form or the action that creates the first item
- Zero-value stat cards below the empty table
- Remove any badges/widgets tied to test data (e.g. "AI Analysis Ready: 5", "1 Pending Approval")
- Also purge: company name references (e.g. "Smith Contracting"), fake claim numbers, fake dollar amounts, and logged-in greeting text with hardcoded demo data

#### Internal Portal Landing Page

The internal portal (`internal.debtrecoveryexperts.com`) serves the staff dashboard pages (Claims Dashboard, Case Aging, Letter Queue, Client View) behind Cloudflare Access. After authenticating, users land at the root (`/`) — which was returning a blank page because no `index.html` existed.

**Fix:** Create `/var/www/internal/index.html` as a dark-themed landing page with 4 card links to the main tools. After Cloudflare Access passes the user through, Caddy serves this file via `try_files`.

The `/var/www/internal/` directory contains these files:
- `index.html` — landing page with 4 navigation cards
- `dre-dashboard.html` — claims dashboard with AI analysis
- `dre-case-aging.html` — case aging monitor
- `dre-client-dashboard.html` — client view preview
- `letter-queue.html` — demand letter queue
- `dre-analytics.html` — AI analysis & metrics dashboard
- `inbox.html` — unified inbox (IMAP polled from dre@ + collections@)

**Caddy config (verified working):**
```
internal.debtrecoveryexperts.com {
    header Access-Control-Allow-Origin "*"
    root * /var/www/internal
    try_files {path} {path}.html /index.html
    file_server
}
```

The `try_files` directive ensures:
- `/dre-dashboard` → tries `/dre-dashboard` → `/dre-dashboard.html` → succeeds
- `/` → tries `/` → `/index.html` → succeeds
- No 404 for clean paths

#### 8 Canned Letter Templates (Jul 8)

All 8 letter templates are documented at `references/dre-letter-templates.md`:

| # | Letter | Sender | Auto/Queued |
|---|--------|--------|-------------|
| 1 | Onboarding — "Received your claim" | dre@ | Auto |
| 2 | Under Review — "Being analyzed" | dre@ | Auto |
| 3 | Accepted + LPOA — Instructions + LPOA attachment | dre@ | Auto |
| 4 | Soft Touch — Friendly 1st demand | collections@ | Queue → Approve |
| 5 | Formal Demand — 14-day demand letter | collections@ | Queue → Approve |
| 6 | Lien Threat — Texas mechanic's lien notice | collections@ | Queue → Approve |
| 7 | Escalation — Final notice before legal | collections@ | Queue → Approve |
| 8 | Legal Action — Lawsuit referral notice | collections@ | Queue → Approve |

Items 4-8 flow through the "+ New Letter" modal: select client → stage-gated template → pre-fills composer → edit → Anita → Tony + Germaine approve → send via collections@.

**Notifications:** Anita should get a prompt when a letter draft lands in the queue needing her attention. Payment links (`pay.debtrecoveryexperts.com`) must be included in every letter body.

### Portal Server Config

**Caddy config for DRE subdomains** (live at `/etc/caddy/Caddyfile`):

```caddyfile
portal.debtrecoveryexperts.com {
    header Access-Control-Allow-Origin "*"
    root * /var/www/capabilities
    try_files {path} /debt-recovery.html
    file_server
}

internal.debtrecoveryexperts.com {
    header Access-Control-Allow-Origin "*"
    root * /var/www/internal
    try_files {path} {path}.html /index.html
    file_server
}
```

**`portal.debtrecoveryexperts.com`** — serves the public-facing intake form, login page, fee calculator, and pay page from `/var/www/capabilities/`. The `try_files` fallback to `debt-recovery.html` ensures:
- `/` → serves intake form (right for new visitors)
- `/login` → tries `/login` → `/login.html` → succeeds
- Any unknown path → falls back to intake form

**`internal.debtrecoveryexperts.com`** — serves the staff dashboard pages from `/var/www/internal/` behind Cloudflare Access. The `try_files` chain ensures clean URL resolution.

**Two separate docroots by access level:**
| Directory | Serves | Auth |
|-----------|--------|------|
| `/var/www/capabilities/` | Public pages (intake, login, fee calc, pay) | None |
| `/var/www/internal/` | Staff pages (dashboard, aging, letters) | Cloudflare Access |

The internal pages (`dre-dashboard.html`, `dre-client-dashboard.html`, etc.) exist only in `/var/www/internal/` — they are NOT deployed to the public web root. Source files in `/root/portal-mockup/` hold the originals; deploy to the appropriate docroot.

**To add a new page:** create the HTML in `/root/portal-mockup/`, then copy to `/var/www/capabilities/` if public or `/var/www/internal/` if staff-only. No Caddy changes needed unless adding a new subdomain.

### Subdomain-First Link Strategy

All nav links in DRE portal pages use **full paths** rooted at the serving domain. Internal pages link to `portal.debtrecoveryexperts.com/` for the intake form (not a relative `/debt-recovery.html` which doesn't exist in the internal docroot). Cross-links between public and internal domains use absolute URLs.

## Link Audit Checklist

Before deploying portal pages, verify:
- Every `<a href="#">` has been replaced with a real path or anchor
- Phone numbers use real values, not `(---) --- ----`
- Submit/CTA buttons are wrapped in `<a>` if they navigate
- Chatbot footer links point to real pages
- Nav links cover all portal pages: dashboard, intake, fee calculator, pay
- Root URL (`/`) either has an index file or a Caddy try_files fallback to a sensible default
- Chatbot response text contains no hardcoded demo claims or fake debtor names
- Nav avatar initials match the company (e.g. "DR" for Debt Recovery Experts, not "SC" for Smith Contracting)

**The subagent fix pass left test data in client dashboard (Smith Contracting references, Acme Corp chatbot responses, fake stats). Always re-check after a subagent completes a purge — don't assume it caught everything.**

**Dashboard pages must NOT be served publicly.** The internal dashboard, client dashboard, and case aging pages contain mockup data and should not be accessible without auth. Only the intake form, fee calculator, and debtor pay page should be in the public web root. Keep source files in `/root/portal-mockup/` and only deploy the public subset to `/var/www/capabilities/`.

### Client Portal Dashboard Elements That Must Be Purged

When building or cleaning the client dashboard (`dre-client-dashboard.html`), these locations harbor test data:

1. **Header greeting:** Replace "Welcome back, {CompanyName}" with "Welcome to Your Dashboard"
2. **Subtitle:** Replace "Member since...N claims filed...N recovered" with generic subtitle about tracking claims
3. **Stat cards:** Replace 1/3/$45,650/$12,450 with 0/0/$0/$0
4. **Active claims section:** Replace sample claim rows with empty state
5. **Past claims section:** Replace recovered/uncollectible rows with empty state
6. **Recovery stats card:** Replace 4/3/75%/38 days/$45,650 with 0/0/—/—/$0
7. **Chatbot response text:** Replace hardcoded "Acme Corp" responses with generic tier explanations
8. **Console.log stubs:** Replace `{subject, message, claim:'DRE-2026-0042', client:'Smith Contracting'}` with `{subject, message}`
9. **Nav avatar initials:** Replace company-specific initials (e.g. "SC", "AC") with "DR"

Chatbot Identity — Registered Debt Collector

Agent DRE must identify itself as **a Texas-registered debt collection agency**. The welcome message and any "what is DRE" response must include "registered with the state of Texas" and "FDCPA compliant". This was a user-directed correction — apply to every chatbot implementation.

**Coverage checklist:**
- Public intake form chat widget (portal.debtrecoveryexperts.com) — welcome message AND "what is DRE" answer
- Letter queue composer sidebar — welcome message AND `'about'` chat intent
- Client dashboard chat widget — same treatment

**Internal Agent DRE** on the letter queue page uses an `'about'` intent that catches: "who are you", "what is DRE", "registered", "collection agency", "about DRE", "your name", "who is". Always include this when deploying Agent DRE to a new page.

### Chatbot Header Naming

The frontend chatbot widget header text is **"Agent DRE"** — not "DRE Assistant", "DRE Chatbot", or "Agent D.R.E" in the header. The sub-status is simply "Online" (no "Powered by AI" suffix).

### Client Portal Views

**New visitor** → Chat shows **lead capture form** (name, company, email, phone required).
**Logged-in client** → Chat shows **Agent DRE greeting** with generic welcome text (no hardcoded claim data).
**Lead data** → Stored in session (production: send to CRM/email).

## DRE Roles Guide

A reference guide for Tony and Anita at `references/dre-roles-guide.md` covers CRM access levels, email address purposes, and key URLs. Update this file when access changes.

## Agent DRE — Chatbot

**Do NOT confuse Agent DRE with Tony's Hermes agent.** Agent DRE is the frontend chatbot widget on the DRE portal pages (client dashboard, intake form). Tony's Hermes is a separate AI agent running on a VPS at 87.99.159.142. They are unrelated.

### Answer Style: Elevator Pitch First

When "How does this work?" is asked, the response must be a **soft elevator pitch** — not a bullet-point process dump. The user explicitly corrected this: "when asking Agent DRE how this works, it provides too much detail." Pattern:

| Trigger | Old (too detailed — DON'T) | New (right — DO) |
|---------|-------------------|-------------|
| "how it works" | "Step 1: Submit with docs, Step 2: We send soft email, Step 3: Certified letter, Step 4: Escalate, Step 5: Law firm" | "We handle recovery from start to finish — submit your claim, we send a demand, and keep escalating until you get paid. Most claims resolve in 15-30 days." |
| "fees" | "20-25% early, 30% standard, 33% escalated, 10%+25% litigation" | "We only get paid when you do. Fees start at 20% for early resolution and scale up based on effort." |
| "documents needed" | "1) Contracts, 2) Invoices, 3) Correspondence, 4) Proof of delivery" | "The basics: contract, invoices, and any correspondence about the unpaid amount. Submit what you have and we fill in the gaps." |

**Rules:**
- One conversational sentence, not a numbered list
- End with a follow-up question ("Want to hear about fees?") to keep the conversation going
- Use contractions ("we're", "you'll", "don't") for a natural spoken tone
- No numbered steps inside response text
- No percentage breakdowns in first mention of fees — offer the fee calculator link instead
- Lead with benefit ("You get paid faster") not process ("We send a demand letter")

### Personality

- **Empathetic** — Acknowledge frustration. "I understand how stressful unpaid invoices can be."
- **Kind but firm** — Supportive without apologizing for DRE's process or fees
- **Engaging** — Ask leading questions. "How long has this been outstanding?"
- **No promises** — Never guarantee outcome, timeline, or amount. Use "typically," "most claims," "can be."
- **Escalate gracefully** — Out of scope → invite claim submission

### Tone Reference

| Do | Don't |
|---|---|
| "I understand — unpaid invoices create real pressure." | "Sorry about that." |
| "Most claims at your level resolve within 30 days." | "We'll get your money back in 30 days." |
| "Our process typically recovers 70%+." | "You'll definitely get paid." |
| "Ready to get started? We can review your case." | "You should definitely use us." |

### Default / Catch-All Response

When the user asks something the chatbot doesn't have a specific answer for, lead with what you CAN help with:

"Good question. I can tell you about <b>fees</b>, the <b>process</b>, what <b>documents</b> you'll need, or <b>timelines</b>. What sounds most helpful?"

This avoids the hard sell and invites a real conversation.

### Message to Team

Logged-in clients can send structured messages through the chat. Subject options: Question about my claim, New information about the debtor, Payment received / want to stop recovery, Update my contact info, Complaint or concern, Other. Messages log to case file and notify DRE team.

### Knowledge Base Separation

**Public (chatbot can answer):** Fee structure (elevator pitch only), required docs, timelines, Texas construction liens (general), LPOA process, ACH payments, how to submit, contact info, fee calculator link, TOS/Privacy link.

**Private (NEVER):** AI scoring methodology, skip tracing techniques, internal weakness criteria, approval workflow, specific case data, partner firm names, fee negotiation limits, compliance strategy, internal notes.

## Email Configuration

| Address | Display Name | Purpose |
|---------|-------------|---------|
| hello@debtrecoveryexperts.com | Debt Recovery Experts | Sales, general inquiries |
| collections@debtrecoveryexperts.com | Debt Recovery Experts | Debtor-facing — demands, payment links |
| dre@debtrecoveryexperts.com | Debt Recovery Experts | Client-facing — DocuSeal, status updates, fee receipts, portal links |

All hosted on MXroute (heracles.mxrouting.net:993 IMAP, :587 SMTP). Outgoing email sent via mail.germainebrown.com:2525 relay (MXroute SMTP blocks our netcup IP). Passwords stored in ~/.hermes/.env as DRE_EMAIL_* variables.

**IMAP pitfalls:**
- The MXroute SMTP server (`heracles.mxrouting.net:587`) does NOT accept connections from the netcup (Core) IP when connecting as an SMTP relay. Connections hang or time out. ALWAYS use the germainebrown.com SMTP relay (`mail.germainebrown.com:2525`) as the outgoing MTA for DRE mailboxes. IMAP reading works fine against heracles.mxrouting.net:993.
- Gmail enforces SPF strictly. When sending via the germainebrown.com relay, Gmail recipients will get 550 5.7.26 blocks because our relay IP isn't in apextrackexperience.com's SPF record. For domains where our IP isn't authorized, use the domain's own SMTP server instead of our relay.
- The WP Mail SMTP plugin on WordPress stores passwords in PHP-serialized/encoded format in the database. The raw password from the *.env file won't work for smtplib login against SiteGround's SMTP — it's encrypted. You need the real plaintext password.
- To test DRE mailbox IMAP from Core: connect to `heracles.mxrouting.net:993`, NOT `mail.debtrecoveryexperts.com:993` (which doesn't resolve from our network).

## Domains

| Service | URL | Status |
|---------|-----|--------|
| Signing | sign.itpropartner.com | ✅ Caddy → DocuSeal |
| API/JSON | core.itpropartner.com | ✅ Caddy → static files |
| Portal | app.itpropartner.com | ✅ Caddy → portal mockups |
| Main site | debtrecoveryexperts.com | ✅ WordPress on wphost02 |
| Cloudflare DNS | debtrecoveryexperts.com | ✅ Zone managed via API (token stored) |
| Portal subdomain | portal.debtrecoveryexperts.com | ✅ A record → netcup Caddy, serves DRE pages |

**DNS note:** `itpropartner.com` is hosted at **SiteGround**, which offers no API key. Subdomain records added manually. `debtrecoveryexperts.com` is on Cloudflare and managed via API.

## DocuSeal on Core

**Location:** `~/docker/docuseal/`  
**URL:** https://sign.itpropartner.com  
**API token:** info@itpropartner.com (Settings → API Tokens)

### Deployment pitfalls

- **Omit DATABASE_URL** — container defaults to SQLite at /data. Setting it wrong crashes the container. Just mount `/data` volume.
- **Crash loop detection:** Catch logs immediately with `docker logs docuseal` — container restarts silently every few seconds.
- **Initial setup is browser wizard** — first visit creates admin account. API token generated after.
- **Caddy vs Tailscale Serve on port 443:** Run `tailscale serve off` before starting Caddy. This disables Tailscale HTTPS proxy.
- **DNS must resolve BEFORE starting Caddy** — Caddy auto-provisions Let's Encrypt certs on first request.

### Dual Use

- **Family:** sign.itpropartner.com UI — upload, sign, drag-and-drop
- **API (portal):** REST API for automated signing envelopes, webhooks for signed events

## Key Integrations

| Service | Role | Cost | Status |
|---------|------|------|--------|
| **DocuSeal** | Digital signatures | Free (AGPL) | ✅ Deployed |
| **Cloudflare Turnstile** | Bot protection | Free | ✅ Deployed |
| **OneNotary** | Online notarization | ~$25/session | ❌ Need account |
| **LetterStream** | Certified mail | ~$8.34/letter | ❌ Need account |
| **Stripe Connect** | ACH payments | 0.8% capped $5 | ❌ Need keys |
| **TwentyCRM** | DRE CRM | Free self-hosted (AGPL) | 🟡 Installing |
| **Partner law firm** | Litigation | 25% fee split | ❌ Need agreement |

### DRE CRM (TwentyCRM)

Selected CRM for Debt Recovery Experts. Deploying on Core VPS (8C/15G). Docker Compose stack: twenty-server (NestJS), twenty-front (React), PostgreSQL, Redis, worker. ~2GB RAM allocated.

- **Repository:** twentyhq/twenty (52K GitHub stars)
- **API:** GraphQL + REST + MCP server — can integrate with existing DRE portal
- **Custom objects:** Define debtors, claims, case stages as TypeScript code or via UI
- **Email:** Built-in IMAP integration — connect dre@ and collections@ mailboxes
- **Webhooks:** Included — trigger actions on claim status changes
- **Cloud:** $9-19/user/mo if self-hosting overhead becomes undesirable
- **Branding:** Name the instance "DRE CRM" — branded for Debt Recovery Experts
- **Access:** Initially localhost only; will need subdomain and Caddy route when ready
- **Custom data model:** See `references/twentycrm-custom-data-model.md` for the complete DRE data model (Debtors, Claims, Case Notes, Payments) with field definitions, types, and API creation patterns
- **Deployment pitfalls:**
  - After `docker compose up -d`, the `SERVER_URL` in `.env` defaults to `http://localhost:3000`. This must be changed to the real domain (e.g. `https://crm.debtrecoveryexperts.com`) before the frontend can make API calls from a browser.
  - The server container also needs `REACT_APP_SERVER_BASE_URL: ${SERVER_URL}` in the `docker-compose.yml` environment section. TwentyCRM's inject-runtime-env.sh injects this into the frontend build at startup. Without it, the frontend tries to call `localhost:3000` even when accessed via the real domain.
  - After changing `.env` or `docker-compose.yml`, run `docker compose down && docker compose up -d` — the containers need a full restart, not just a restart of the server container, because the frontend build is baked into the server image and runs the inject script at startup.
  - DNS propagation for new subdomains can take 5-15 minutes. Use Cloudflare proxied (orange cloud) mode for instant resolution if the straight A record doesn't resolve from the user's browser.
  - **REST API field creation:** The `/rest/metadata/fields` endpoint creates TEXT, CURRENCY, SELECT, DATE_TIME, RICH_TEXT, and other non-relation field types. The REST API does NOT accept `relationCreationPayload` — it returns `"Relation creation payload is required"`. For RELATION-type fields (foreign keys), use the **GraphQL mutation endpoint** (`POST /metadata`) with `createOneField` mutation. See `references/twentycrm-custom-data-model.md` → "Relations" section for the exact mutation format, required payload fields, curl examples, and the DRE data model relations table.
  - **Reserved field names:** Avoid using `address` as a field name — TwentyCRM rejects it. Use `physicalAddress` or `mailingAddress` instead.
  - **SELECT field colors:** Available color values for option palettes: blue, turquoise, green, yellow, orange, red, purple, pink, grey, sky.

## Legal Documents

All marked **DRAFT — Texas Attorney Review Required:**

- `/root/TERMS_OF_SERVICE_DRAFT.md` — 17 sections, binding arbitration (Travis County), FDCPA compliance
- `/root/PRIVACY_POLICY_DRAFT.md` — 15 sections, data categories, third-party sharing, retention schedule, TX Data Privacy Act, Cloudflare Turnstile Privacy Addendum
- `/root/DRE_Compliance_Manual.md` — 22 sections (v1.1 includes Section 22: Mechanic's Lien & Bond Claims)
- `/root/Texas_Mechanics_Lien_Research_Report.md` — 18 sections with deadlines

### Key Legal Findings

| Topic | Finding |
|-------|---------|
| TDCA | DRE = debt collector; bond/registration required; $4K/violation penalty |
| LPOA | Must be written, notarized (TX Estates Code 751.0021), enumerate collection powers |
| Fee caps | No statutory cap in Texas; must disclose in writing |
| SOL | 4 yrs written contracts; 2 yrs tort |
| Service | Certified mail (restricted delivery) valid under TRCP 106(a)(2) |
| FDCPA | 15 USC 1692: validation notice 5 days, Mini-Miranda, cease comms |
| Lien deadlines | GC: 4th month; Sub: 3rd month; Foreclosure: 1 year |

## Competitor Website Research

When researching competitor debt collection agency websites for design direction, load 3-5 competitor homepages, capture visual analysis and structural snapshots, then compile reports with: layout map, color palette, imagery types, messaging, trust signals, unique elements, cross-site comparison table, and strategic takeaways.

See `references/competitor-website-research.md` for the full July 7, 2026 research output covering IC System, Cadex Solutions, Chaser, TSI, and Caine & Weiner.

## Logo

**Selected: Concept C — Industrial Separated** (D | R | E with pipe separators). Bold amber D and E, slate gray R. Subtitle "DEBT RECOVERY EXPERTS" in small caps with 5px letter spacing. Full mark is 340px wide with subtitle.

**Specs:**
- D: #f59e0b (Amber 500), weight 800
- R: #94a3b8 (Slate 400), weight 700
- E: #f59e0b (Amber 500), weight 800
- Pipes: #cbd5e1 on light, #475569 on dark
- Typography: Inter (or similar sans-serif), bold 800 for D/E, bold 700 for R
- Minimum size: 140px (mark only) / 220px (with subtitle)

See `references/dre-logo-and-brand.md` for SVG code and usage examples.

## Pitfalls

- **File permission gotcha: 403 after Cloudflare auth passes**

When creating new static HTML files in the webroot with `write_file`, they default to `-rw-------` (600, root-only). Caddy runs as a non-root user and returns **403 Forbidden** — the user sees "There's a problem with this site" even though Cloudflare Access authenticated them successfully. Always `chmod 644 newfile.html` immediately after creation. Run `ls -la /var/www/<dir>/` to verify all files are world-readable before telling the user to test. This applies to BOTH `/var/www/capabilities/` (public) and `/var/www/internal/` (staff) directories.

### Cloudflare API token in cron scripts: redacted `***` baked into script

The `service-health-check.sh` script had the literal string `***` replacing the actual token value in the curl Authorization header. This happens because when the script is written/copied, Hermes secret redaction replaces the real token with `***` in the fetched content, and the literal `***` becomes part of the saved file — so every future run sends `Bearer ***` instead of the real token.

**Fix pattern for scripts that need credentials from cron:**
1. Use `${VARIABLE:-$(grep ...)}` — checks the session environment first, falls back to .env for cron environments (which have no session env vars)
2. Always verify the credential is also stored in `/root/.hermes/.env` when a script needs to run from cron
3. When outputting a command with a credential to save as a script file, use the variable reference (e.g. `$CLOUD_TOKEN`) not the inline value — inline values get redacted and `***` ends up in the file

### Tailwind `@apply` in inline `<style>` tags is silently broken

`@apply` is a Tailwind build pipeline directive (processed by `tailwindcss` CLI or PostCSS plugin). When used inside a raw `<style>` tag with the TailwindCDN script, `@apply` is silently ignored — the browser sees it as invalid CSS and falls back to defaults. The page renders without the intended styling and there's no console error to clue you in.

**Fix:** Always write plain CSS in `<style>` tags when using TailwindCDN. Convert `@apply` rules to equivalent real CSS:
- `@apply p-4 rounded-lg` → `padding: 1rem; border-radius: 8px;`
- `@apply text-gray-400 hover:text-white` → `color: #9ca3af;` + `.class:hover { color: white; }`
- `@apply fixed inset-0 bg-black/60 z-50` → `position: fixed; inset: 0; background: rgba(0,0,0,0.6); z-index: 50;`

### SPF: Silent Email Drops From WordPress SMTP Relays

When WordPress sends via an external SMTP relay, the receiving MTA checks the relay IP against the domain's SPF. If unauthorized, email is silently dropped — no bounce, no error in WordPress, no log entry.

**Debugging checklist:**
1. Test SMTP direct from server — if OK, credentials work
2. Test `wp_mail()` via WordPress — if TRUE, plugin is active
3. **Check SPF for the sending domain** — is the relay IP listed?
4. MX through spam-filtering service? → they enforce SPF strictly
5. `wpforms_logs` only tracks migrations, not delivery — don't rely on it

**Fix:** Add the relay IP to the SPF TXT record:
```text
v=spf1 +a +mx ip4:35.212.86.161 include:domain.com.spf.auto.dnssmarthost.net ~all
```
- **Lead capture before chatbot** — new visitors must provide name + email before unlocking
- **Logged-in greeting includes case context** — claim number, current tier, status. Never generic.
- **No promises** — never guarantee timeline or outcome. "Typically," "on track."
- **Two binders on close** — DRE internal includes AI analysis; client excludes it
- **Change history on every edit** — who, what (old→new), when, why. No silent edits.
- **Both partners approve** — Germaine → "Pending Tony ✓" → Tony → moves forward
- **Hourly reminders** — pending approval notifications to both approvers
- **PII verification** — Agent DRE asks for claim number before sharing case-specific details
- **Fee calculator = estimates** — actual fees in signed LPOA. Not a quote.
- **Mockup data must be purged after subagent fix pass** — always double-check stat cards, chatbot responses, header greeting, nav initials, and console.log stubs for remaining test data
- **Caddy routes must use `handle_path` not `path` for subdirectory serving** — `path /capabilities` matches only the literal "/capabilities" path, not subpaths. Use `handle_path /capabilities*` or `handle_path /capabilities/*` to serve subpaths like `/capabilities/dre-dashboard.html`
- **Document upload zone must have real `<input type="file">`** — a decorative `<div>` with drop-zone styling is not enough. The HTML needs: (1) a hidden `<input id="file-input" type="file" multiple accept=".pdf,.jpg,.jpeg,.png,.doc,.docx">`, (2) JS click handler on the drop zone that triggers `fileInput.click()`, (3) drag/drop event listeners (`dragover`, `dragleave`, `drop`) with visual feedback (amber border, amber-50 bg), (4) file list rendering with remove buttons, (5) 20MB size limit with alert. Without all five, the upload "works" visually but does nothing.
- **Login page: SSO-only gateway pattern** — for unauthenticated client access, build a single-page login at `/login.html` with: (1) Cloudflare logo/brand, (2) "Sign in with SSO" button linking to the internal subdomain (`internal.debtrecoveryexperts.com`), (3) "or" separator, (4) "New customer?" link back to the intake form. No username/password fields — the SSO provider handles auth. Root URL (`/`) fallback (Caddy try_files) should serve the intake form, not the login page, so new visitors see the claim form first.

**Payment link requirement:** Every demand letter body (auto and queued) MUST include `Payment can be made at: https://pay.debtrecoveryexperts.com` before the debt collector notice. This was added to the `+ New Letter` modal templates and the 4 static mock letters in the `letters[]` array.

**Anita notification on new draft:** When a new letter draft lands in the queue, a notification bar appears (amber bg, dark text): "✏️ Anita — a new letter draft is ready for your review and authoring." Auto-fades after 15s, dismissible via X button. Implemented in `/var/www/internal/letter-queue.html` via `showAnitaNotification()`.

**Agent DRE identity:** Must identify as "a Texas-registered debt collection agency" and "FDCPA compliant" in welcome message AND "what is DRE" answer. The `'about'` chat intent catches: "who are you", "what is DRE", "registered", "collection agency", "about DRE", "your name", "who is". Apply to every chatbot deployment.

**Agent DRE answer style — Elevator Pitch First:**
- One conversational sentence, not a numbered list
- End with follow-up question
- Use contractions
- No percentage breakdowns in first mention of fees — offer fee calculator link
- Lead with benefit, not process

**Analytics page pattern:** 4 metric cards → AI Score Distribution bar chart (pure CSS) → Recovery by Tier table → Monthly Recovery Trend bars → Top Performers table. Static mock data only. Same dark theme. 6th nav link (analytics between Inbox and nav end on all pages simultaneously).

|| `references/dre-credit-reporting.md` | FCRA/Texas legal analysis: DRE can report consumer debts, NOT corporate B2B to consumer CRAs; use Dun & Bradstreet for business credit |
