Files
dre/docs/letter-queue-scope.md
T
root 7a62b0b340 Backend Aug 22-25: AI analysis, welcome-packet templating, LetterStream, DocuSeal, staff RBAC + tier gate
- analysis.py: deterministic claim scorer + /analyze /approve /letter /advance-tier endpoints (auto-runs on intake)
- packet.py + packet_fields.json: welcome-packet templating engine (6 onboarding docs, field catalog)
- letterstream.py + letters.py: certified-mail send pipeline + letter lifecycle (webhook verified)
- docuseal.py: DocuSeal signing integration
- staff.py/models.py/schema.sql/auth.py: approval actor from staff key, tier gate (APPROVED+ACTIVE+onboarding docs), onboarding_docs table
- frontend/: dependency-free static portal (intake, magic-link login/verify, dashboard)
- landing-mockups/: 4 design-stance mockups + favicons
- legal/: aup/privacy/sms-terms/terms HTML
- docs/: letter-queue scope, letterstream API contract, 6 welcome-packet templates
- review-dre-landing-2026-08-21.md: 3-variant landing feedback sprint
- compliance/DRE_Compliance_Manual.md: updated

Source synced from deployed /opt/dre-portal/app/ (was 4 days ahead of git).
2026-08-26 02:26:33 -04:00

6.9 KiB

Letter Queue - Backend Scope (draft)

Date: 2026-08-24 Author: Sho'Nuff (draft for Germaine review)

Current state (ground truth, verified 2026-08-24)

  • Send pipeline is BUILT and verified end-to-end. Two new modules:
    • /opt/dre-portal/app/letterstream.py — LetterStream integration (auth, account status, send single/batch, preauth/doauth, tracking, signature, proof).
    • /opt/dre-portal/app/letters.py — router: letters queue lifecycle, PDF render (fpdf2 2.8.8), LetterStream send, and the callback receiver.
  • Live DB tables exist: letters and letter_events (added to schema.sql, applied on restart).
  • LetterStream API contract fully recovered from the user-supplied api_fulfillment.pdf (21 pages): POST https://www.letterstream.com/apis/ with a=api_id, h=hash, t=unique_id. Hash = md5(base64_encode(last6(t) . api_key . first6(t))). Verified three ways: PDF formula + live AUTHOK + user screenshot sample vector.
  • Account live-verified: balance $100.00, testmode=disabled. No documents submitted, no charges incurred.
  • Letter content still lives on claims (subject/body/tier) and is generated by analysis.recommend_letter() (deterministic, no LLM) — the letters queue is the physical-send layer on top.
  • Queue UI is wired and smoke-tested (2026-08-25, task ls7). letter-queue.html is a functional queue: filterable list, status badges, Approve / Price & Queue / Confirm & Mail / Track actions, and a New Letter modal (claim picker + structured recipient + mail class). GET /api/staff/letters now joins claims to expose claim_number. Verified via live API lifecycle + Node render harness + 9-page HTTP smoke (all 200).
  • LetterStream callback is NOT enabled yet — the receiver is live and tested, but the callback toggle in the LetterStream dashboard stays off until after the first real send is confirmed.

Target state

A durable letter queue: many letters per claim, each with a lifecycle (draft -> approved -> queued -> sent -> delivered/failed), sendable via LetterStream certified mail, with FDCPA-compliant content and a full audit trail.

1. Data model - new letters table

Column Type Purpose
id TEXT PK uuid4
claim_id TEXT FK -> claims.id owning claim
letter_type TEXT demand / escalation / validation / custom
tier TEXT tier the letter was generated for
subject TEXT letter subject
body TEXT letter body (markdown or plain)
status TEXT draft / approved / queued / sending / sent / delivered / failed
recipient_name TEXT debtor or registered-agent name
addr1, addr2, city, state, zip TEXT mailing address
letterstream_job_id TEXT LetterStream job reference (nullable)
tracking_number TEXT USPS tracking (nullable)
sent_at TEXT ISO timestamp (nullable)
delivered_at TEXT ISO timestamp (nullable)
error TEXT last send error (nullable)
created_by TEXT RBAC actor name
created_at / updated_at TEXT ISO timestamps

Migration: keep the four claim columns as the "current draft" during transition, then deprecate them once the queue is live. No destructive drop until the queue is proven in production.

2. API

  • GET /api/staff/claims/{n}/letters - list letters for a claim
  • POST /api/staff/claims/{n}/letters/generate - generate a draft row via recommend_letter() (inserts, does not overwrite the claim columns)
  • PUT /api/staff/letters/{id} - edit a draft
  • POST /api/staff/letters/{id}/queue - mark queued (requires full mailing address)
  • POST /api/staff/letters/{id}/send - call LetterStream, store job_id + tracking
  • POST /api/letters/webhook - LetterStream status callback (delivered / failed)
  • GET /api/staff/letters - global queue across claims (feeds letter-queue.html)

Idempotency: send is guarded by status (only queued -> sending), so a double-click cannot mail a letter twice. Store letterstream_job_id before marking sent.

3. Send pipeline (LetterStream)

Order of work:

  1. Verify the existing LETTERSTREAM_API_KEY against their API (is it valid, what account, what products are enabled).
  2. Map their REST surface: auth method, endpoint shape, certified vs first-class, PDF upload vs HTML/plain rendering, return address handling, tracking + status webhook. Do not assume - confirm from their docs or a test call.
  3. PDF generation: render the letter body (reportlab or weasyprint) with DRE letterhead, or pass content to LetterStream to render.
  4. Address handling: return address (DRE office / PO box) and debtor mailing address must both be resolved before send.
  5. Status sync: webhook or poll updates status, tracking_number, delivered_at.

4. Compliance (FDCPA)

  • Every first-contact letter MUST carry the 1692g validation notice: amount of debt, creditor name, 30-day dispute right, right to request verification.
  • No false, deceptive, or misleading language (1692e); no threats of action DRE does not intend to take.
  • Human sign-off gate: a letter cannot move to queued until status is approved (actor recorded).
  • Full immutable audit log (actor + timestamp + old/new) - the audit_log pattern already exists and extends here.

5. UI (letter-queue.html) — DONE 2026-08-25 (ls7)

/var/www/internal/letter-queue.html is live:

  • filterable list (status) with per-status summary chips
  • Approve (DRAFT) / Price & Queue (APPROVED/PREAUTH/ERROR) / Confirm & Mail (PREAUTH) / Reject / Cancel / Track actions
  • status badges (DRAFT/APPROVED/PREAUTH/SENT/REJECTED/CANCELLED/ERROR)
  • tracking timeline (USPS scan events) in the detail panel
  • New Letter modal: claim picker (prefills debtor name), structured recipient, mail class

Reject/cancel implemented 2026-08-25: POST /api/staff/letters/{id}/reject (requires reason) and POST /api/staff/letters/{id}/cancel (optional reason) move DRAFT/APPROVED/PREAUTH/ERROR letters to REJECTED/CANCELLED, persist the reason in letters.note, and write an audit_log row. Guards return 409 for SENT/REJECTED/CANCELLED and 422 for a missing reject reason. Queue UI has Reject (red) / Cancel buttons for all non-terminal states.

6. Decisions locked (2026-08-24)

  1. LetterStream key — valid and live; $100.00 balance, testmode=disabled.
  2. Return address — Germaine provides it 2026-08-25. Set as LETTERSTREAM_RETURN_ADDRESS in .env; drafting does NOT block on it, only send does (clean 409 until configured).
  3. SignatoryDebt Recovery Experts LLC (no named individual). Default in code; overridable via LETTERSTREAM_SIGNATORY in .env.
  4. Address verification / NCOA — none. Pull debtor/return-address data from the client's claim info + Super Search.
  5. FDCPA 1692g notice — mandatory on first contact (DRE is a third-party debt collector). Content is generated by analysis.recommend_letter(); final validation- notice wording vs welcome-packet copy still to be finalized (task ls8).