FEUDFIX — PRE-IMPLEMENTATION AUDIT
Read-only audit. No files were modified, created, or deleted. No prompts, UI, security settings, or email behavior were changed.
PART 1 — APPLICATION ARCHITECTURE
Frontend: React 18 + Vite, Tailwind CSS, shadcn/ui, React Router. Single-page app; pages in src/pages/, components in src/components/. Realtime via Base44 entity subscriptions. Voice intake via Web Speech API.
Backend: Base44 BaaS. Server logic lives in two layers:
- Backend functions (
base44/functions/*/entry.ts) — Deno/edge handlers invoked over HTTP. Used for email send/receive, escalation letters, case persistence that bypasses RLS, scheduled tasks, admin tooling. - Shared modules (
base44/shared/*.ts) — imported by functions (SendGrid mail, letter HTML, SSRF guard, US state parsing, fuzzy search, ranking engine).
Database: Base44 managed document store (MongoDB-style). Every entity is a JSON schema in base44/entities/*.jsonc. No external database. Queries use the SDK (base44.entities.X.filter/list/get/create/update/delete). updateMany/bulkUpdate/deleteMany support MongoDB-style operators.
File/document storage: Core.UploadFile (platform storage at media.base44.com / static.wixstatic.com). Core.UploadPrivateFile + CreateFileSignedUrl exist but are not used for case evidence — all case evidence goes to public UploadFile storage (see Part 3).
Authentication: Base44 platform auth (AuthProvider, base44.auth). Email/password, Google OAuth, OTP verification, password reset — all boilerplate pages at standard paths. Sessions/tokens owned by the platform; the app never touches credentials.
Authorization model: Row-Level Security (RLS) declared per entity in .jsonc. Roles: admin, agent, beta_tester, user (default). Ownership pattern uses created_by_id + a mutable owner_user_id field (because created_by_id is immutable and anonymous filings have a platform placeholder). See Part 2.
Email provider: SendGrid (Mail Send API + Inbound Parse). Secrets: SENDGRID_API_KEY, MAIL_DOMAIN, INBOUND_WEBHOOK_SECRET. Outbound mail in base44/shared/sendGridMail.ts. Inbound via receiveInboundEmail webhook. Click/open tracking disabled.
AI providers/models (all via Core.InvokeLLM):
gpt_5_mini— free pre-login mini-analysis (runMiniAnalysis).gemini_3_flash— intake triage/clarify, company dispute-strategy research, similar-cases web search, document extraction from URLs, admin "read document" flow.gemini_3_1_pro(premiumModel()) — full case analysis (reanalyzeCase), the single most expensive call.- Default/
automatic— escalation letters, demand letters, multi-company extraction, product-model validation/trouble reports, recent-model lookups. - File vision supported on
gemini_3_1_proand default; web search (add_context_from_internet) only ongemini_3_flash/gemini_3_1_pro.
Web-search provider: Google web search is bundled into InvokeLLM via add_context_from_internet (Gemini models). No separate search API. fetchPageContent/validateUrl functions fetch arbitrary URLs server-side (with an SSRF guard).
Third-party APIs: Stripe (Payment Links, client-side redirect — no server-side webhook or payment record), SendGrid, Base44 AI/integrations, Unsplash (stock images). No Lob/PostGrid (physical mail). No CRM/connector integrations authorized.
Scheduled/background processes (workflows in base44/workflows/):
- Daily: Resolved Case Detection, Intelligence Index Refresh, Action Due Reminders, Idle Case Reminders, Case Digest Reminders, New Info Reminders, Company Refresh, Resolved Evidence Cleanup.
- Weekly: Orphan Document Scrub.
- Quarterly: Ranking Reports, Escalation Agency Refresh.
Caching: Company.similar_cases_cache (30-day TTL), Company.suggestions (30-day TTL), Case.free_analysis / Case.ai_analysis (cache-on-case), client localStorage for the per-case unlock flag.
Analytics: SessionEvent entity (page views, clicks, errors — admin-only read), IntakeSession (funnel aborts/completions — admin-only read), base44.analytics.track (custom events, minimal).
Payment systems: Stripe via Payment Links stored on Service.stripe_link / ServiceTier.stripe_link. Checkout redirects the browser to Stripe; on return, a localStorage flag unlocks the case. No server-side payment verification, no webhook.
PART 2 — USER AND CASE DATA ISOLATION
RLS summary (verified from entity schemas):
- Case — read/update/delete:
created_by_idORowner_user_idORadminORagent. Agents can read and update every customer's case, not just assigned ones. Create is open (anonymous filing by design). - CaseDocument — read/update/delete:
created_by_idORowner_user_idORadmin. Owner-scoped. - Correspondence — read/update:
created_by_idORowner_user_idORadmin. Delete addsbeta_tester. - Customer — read/update/delete:
created_by_idORdata.user_idORadminORagent. Agents can read and edit every customer profile. - TimeEntry — read:
created_by_idORowner_user_idORadminORagent. Agents see all time entries. - Company — read is
null(public to any authenticated user). Update: admin/beta_tester, or agent whereagent_numbermatches. - CompanyDocument — read is
null(public to any authenticated user). - EscalationAgency, Service, ServiceTier, AppSetting, RankingReport, ResolvedCaseSummary — read
null(public). Writes admin-only. - SessionEvent, IntakeSession — read admin-only.
- ChangeLog, FeatureRoadmap, PromptTemplate — read public or admin; write admin/beta_tester.
IDOR analysis:
-
getCasefunction (MEDIUM RISK). Reads via the service role and returns the full case if the caller is the owner or the case is "unclaimed" (!owner_user_idandcreated_by_idis anonymous). Consequence: any authenticated user who guesses or obtains an unclaimed case's ID can read its full description (which may contain PII) and its analysis. Mitigation: per-case share token, time-box unclaimed readability, or restrict to the same IP/session that created the case. -
saveCaseAnalysis(MEDIUM RISK). Same unclaimed-or-owner pattern. A non-owner could write analysis fields onto an unclaimed case. Field allowlist limits damage; window still exists. -
sendCorrespondence(GOOD). ChecksisCaseOwner, verifies each attachment belongs to the case, restricts attachment URLs to platform hosts + SSRF guard. Strongest of the service-role functions. -
escalateCase(GOOD).isCaseOwnercheck; rejects resolved cases. -
Agent over-read (MEDIUM RISK).
CaseandCustomerread RLS grantsagentunrestricted access to all customers and cases, not just assigned ones. Agents see case metadata + customer PII (name/address/phone/email), though correspondence is not agent-readable. If agents are untrusted or multi-tenant, this leaks PII. Recommend assignment-scoping. -
Company/CompanyDocumentpublic reads (LOW). Public-facing materials; acceptable. -
Client-side entitlement (MEDIUM). The per-case paywall is a
localStorageflag.reanalyzeCaseand the mini-analysis callInvokeLLMdirectly from the browser. A non-entitled user can bypass the paywall and burn credits. Recommend moving the full analysis behind a backend function with server-side entitlement checks.
PART 3 — DOCUMENT SECURITY
Storage location: All case evidence is uploaded via Core.UploadFile and stored as file_url on CaseDocument. Host: media.base44.com / static.wixstatic.com.
Public vs private: UploadFile storage is publicly accessible by URL. Anyone with the URL can download the file without authentication. UploadPrivateFile + signed URLs exist in the platform but are not used for case evidence. This is the most significant document-security weakness: receipts, contracts, and correspondence are publicly retrievable if a link leaks.
Authorization enforcement: The CaseDocument record is RLS-protected, but the file bytes are not — the storage URL is the access control. Once someone has the URL, the bytes are public.
Direct URL access: Yes — works without auth (confirmed by the SSRF/host allowlist in sendCorrespondence).
Scanning/validation: None. No virus scan, no MIME sniff, no content validation. CaseDocumentUploader accepts any file. No type/size filter in code.
Sent to AI providers: Yes. reanalyzeCase passes file_urls to InvokeLLM (Gemini 3.1 Pro, vision). ExtractDataFromUploadedFile sends the URL to the platform extractor. Evidence documents (potentially containing PII, financial info, IDs) go to third-party AI providers.
Retained by AI providers: Governed by the vendor's policy, not by FeudFix. Should be disclosed in a privacy policy.
Deletion: CaseDocument.delete removes the record but does not delete the file from storage. The deleteResolvedCaseEvidence workflow deletes records 30 days post-resolution; file bytes likely persist.
Weaknesses summary:
- Evidence on public storage (anyone with the URL can download).
- No file type/size validation or scanning.
- Deletion does not remove file bytes.
- PII documents sent to AI providers with no user disclosure.
- Private-file option unused despite platform support.
PART 4 — EMAIL ARCHITECTURE
- Address creation: Each case gets a virtual inbox
case-{case_number}@{MAIL_DOMAIN}, created lazily on first send.Case.virtual_emailstores it. - Outgoing:
sendCorrespondence→ SendGrid Mail Send API.from: virtualEmail,fromName: 'FeudFix', HTML vialetterToHtml. Click/open tracking disabled. Attachments fetched as base64. - Incoming: SendGrid Inbound Parse posts to
receiveInboundEmail?secret=.... Validates secret, parses multipart, looks up case byvirtual_email, creates inboundCorrespondence(withowner_user_idbackfilled), forwards a notification to the customer, triggersdetectResolvedCases. - Inbound attachments: Attachment names recorded in
raw_headers; binaries/content NOT stored. Inbound attachments are lost. - Storage: All correspondence in the
Correspondenceentity. No separate email store. - AI processing of email contents: Inbound replies are fed to the LLM on re-analysis. Email contents ARE sent to the AI provider.
- Exact sent message:
Correspondence.contentis the letter text; rendered HTML is regenerated from text + template (not separately stored).
State distinguishability:
Correspondence.status: draft / sent / received / failed.direction: outbound / inbound.in_timeline: whether an outbound letter shows in the timeline.- Limitation: no "customer-edited" vs "AI-original" flag, and no explicit "approved" state separate from "sent". Editing a draft overwrites
contentwith no version history. The AI's original draft is overwritten on edit; no audit trail of AI-produced vs customer-changed text. Asentrecord means the customer approved and sent it.
Automatic sending without approval:
- Demand letters: draft, manual send. ✅ No auto-send.
- Escalation letters: draft, manual send. ✅ No auto-send.
- Freeform replies: customer-written, manual send. ✅
detectResolvedCases: emails the customer (not the company). ✅- Conclusion: the system does NOT automatically send AI-generated dispute correspondence to companies without explicit customer approval. Correct and safe.
PART 5 — EMAIL IDENTITY AND TRANSPARENCY
What the recipient currently sees:
- Sender name:
FeudFix(hardcoded in all outbound). - Sender email:
case-{number}@mail.feudfix.com. - Reply-to: defaults to From (virtual inbox). Company sees only FeudFix as the sender identity.
- Body: AI-drafted letter, signed by the customer's name inside the body, wrapped in a branded FeudFix HTML template with a
FEUDFIXwordmark header and a footer reading "Sent via FeudFix — your consumer dispute resolution platform." - FeudFix identity: prominent. Customer identity: only inside the letter body, not the email envelope.
Could this mislead a recipient? Yes, plausibly. The envelope From name is FeudFix, the domain is FeudFix's, branding dominates. A company could believe FeudFix is the author/sender rather than a conduit, and — because the letter is formal and cites policy — could mistake FeudFix for a law firm acting on the customer's behalf. Nothing in the email says "authored by the customer, sent through FeudFix" or "FeudFix is not a law firm."
Safer implementation (recommended):
- Set
from.nameto the customer's name (or "{Name} via FeudFix"). - Add a prominent in-body disclaimer: FeudFix is not a law firm and sends on the customer's behalf.
- Keep
reply_toon the virtual inbox.
This is a transparency/accuracy issue, not a security vulnerability.
PART 6 — AI ARCHITECTURE
Every AI call:
| Function | Model | Purpose | Web? | Docs? | Prior case info? | Stored? | Auto-sent? | |---|---|---|---|---|---|---|---| | Intake triage/extract/clarify (3 parallel) | flash-class | Title, company, product, clarifying Qs, triage gate | No | No | No | Ephemeral | No | | Multi-company extraction | default | Extract company names | No | No | No | No | No | | Product model validation / trouble reports | default/flash | Validate model, known issues | Yes | No | No | On form/CompanyDocument | No | | Company dispute-strategy research | gemini_3_flash + web | Reusable strategy report | Yes | No | Yes (cached) | Company.suggestions | No | | Similar-cases web search | gemini_3_flash + web | Find complaints/reviews/news | Yes | No | No | Company.similar_cases_cache | No | | Policy download/extract | gemini_3_flash + web / ExtractData | Pull policy text | Yes | Yes | No | CompanyDocument.content | No | | Full case analysis (reanalyzeCase) | gemini_3_1_pro | Structured analysis | No | Yes (file_urls) | Yes | Case.ai_analysis | No | | Free mini-analysis (runMiniAnalysis) | gpt_5_mini | Preview report | No | No | Yes (cached) | Case.free_analysis | No | | Demand letter | default | Draft letter | No | No | Yes (analysis) | Correspondence (draft) | No (manual) | | Escalation letter | default | Draft escalation letter | No | No | Yes | Correspondence (draft) | No (manual) | | Resolution detection (detectResolvedCases) | default | Detect likely resolution | No | No | Yes | Case.resolution_check | No (emails customer) |
AI behaviors that could produce risky claims:
- Resolution probability (0-100%) is LLM-generated, not empirical.
resolution_probabilityis described as "based on cited policy strength and evidence" but is an LLM estimate shown directly to the customer. Risk: unsupported numerical probability of resolution. The Intelligence Index (intel_resolution_rate) is empirical but sample-size-gated; the per-case probability is not. - Escalation letter prompt presupposes "established policy violations." The LLM may assert the company violated the law even when facts only show a dispute. Risk: unsupported legal conclusions.
- Demand-letter prompt is well-constrained (no inventing facts, cooperative tone). Lower risk.
- Case-analysis prompt separates facts from analysis and cites documents — good. But
factsconfidence is LLM self-assessed; "high" could still be a hallucination. - Dispute-strategy prompt guards against allegation-as-fact (VERIFIED/STRONG/MODERATE/WEAK). Good but LLM-enforced, not verified.
- Hallucinated policies: when no company documents are on file, the prompt says "rely on general reasoning" — web-sourced strategy could bleed in as company policy. Moderate risk.
- "Guaranteed to win": prompt forbids claiming guaranteed outcomes, but
resolution_probabilityas a number still implies precision.
No prompts were modified.
PART 7 — CURRENT INTAKE PROCESS
Process (AI-driven conversational chat):
- Opening: "Tell me, in your own words, what happened." (Free text; min-words gate.)
- On send (3 parallel LLM calls): intake extraction (title, company, product, location), clarifying questions (up to 4), triage gate (supported/unsupported).
- Clarifying Q&A loop: up to 4 questions one at a time. Year-clarification and future-date checks enforced deterministically.
- Desired resolution timeframe: "When would you like this resolved by?"
- Product model step: if a physical product detected, ask for model, validate, search known trouble reports.
- Finish: → submit() analysis chain.
Information collected: what happened, company, product/service + model, purchase date (if mentioned), desired resolution (only via clarifying Q), desired timeframe, clarifying answers. Evidence uploads happen later on the case page, not in intake.
NOT explicitly collected:
- Dollar amount / approximate value — not asked.
- Whether the customer already contacted the company — only via clarifying Q priority 3, not guaranteed.
- Current status / duration — not explicitly asked.
- Documentation available — only via clarifying Q, no structured question.
When FeudFix begins consuming paid resources:
- First chat message: 3 LLM calls immediately (cheap model).
- On submit: company extraction LLM, web search for candidates, web fetch + LLM for policy documents, web-enabled LLM for dispute-strategy research (expensive), web-enabled LLM for similar-cases search — all before the customer pays or sees the full analysis. The full
reanalyzeCase(gemini_3_1_pro) is on-demand and paywalled, but the intake research is not.
Cost-prevention opportunities (not implemented):
- No Case Opportunity Score or triage to skip research for low-value cases.
- For a brand-new company, full web research runs at intake unconditionally.
- The intake research chain is not rate-limited per case.
- A two-stage intake (Part 16A) would let cheap cases skip expensive web research.
PART 8 — CURRENT CASE ANALYSIS
Workflow: CaseAnalysisLoader shows "Analyze my case" (or auto-runs if paid). reanalyzeCase builds RAG context from policy docs (version-selected by purchase date), uploaded evidence (file_urls to vision model), the "Cases & complaints" feed, full correspondence history, resolved-case summaries, and the dispute-strategy report. Calls InvokeLLM with gemini_3_1_pro and a JSON schema, applies a deterministic desired-resolution-time penalty, persists to Case.ai_analysis.
Output separation:
- Customer's account →
description+ clarifying Q&A in the prompt; outputsummaryreflects it. ✅ - Documented facts →
facts[]withtext/confidence/citation. ✅ - Company policies →
companyContext; cited infactsandrelevant_policy_citations. ✅ - Third-party information → similar cases / dispute strategy; surfaces in
outcome_prediction. ⚠️ Not cleanly separated. - Similar complaints → "Cases & complaints" feed as
similarContext. ⚠️ Background only, not a distinct output section. - FeudFix analysis →
analysisfield. ✅ - Recommended next steps →
recommended_actions[]+action_timelines[]+improvement_actions[]+immediate_next_step. ✅
Source citations: facts[].citation and relevant_policy_citations carry labels, but no structured source object (URL, title, date accessed, source type) — citations are free-text strings. Source URLs not retained on facts. retrieved_sources is a flat string of doc titles.
Allegation vs fact: Prompt separates FACTS from ANALYSIS, but customer allegations in description are not explicitly flagged as "unverified allegations" in the output schema; they feed analysis and summary. Moderate risk of treating customer claims as established.
Probability of resolution: resolution_probability (0-100) is generated and shown. It is an LLM estimate, adjusted by a deterministic time penalty. Not statistically grounded unless the Intelligence Index has sufficient sample (and even then the per-case number is LLM-synthesized). Main "unsupported percentage" risk.
PART 9 — COMPANY RESEARCH
- Trigger: At intake for each confirmed company; on admin refresh; on scheduled
Daily Company Refresh; onpriorityCaseRefresh. Reused if cache <30 days old. - Sources: Web via
InvokeLLMwithadd_context_from_internet(Google search inside Gemini).fetchPageContentfor direct fetch.validateUrlfor link checks. - Caching:
Company.suggestions+suggestions_updated(30-day TTL);Company.similar_cases_cache+similar_cases_updated(30-day);CompanyDocumentrecords persist policy text. - Policy identification:
refreshCompanyPoliciesdownloads likely policy pages, LLM-extracts text, stores asCompanyDocumentwithdocument_type,effective_date,priority_tier. - Similar complaints: web search returns typed items with
source_url,date,region,article_text,company_confidentflag. Cached toCompany.similar_cases_cache. - Resolutions:
ResolvedCaseSummaryrecords (created on case resolution) aggregated into the Intelligence Index — empirical, sample-size-gated. - Source URLs: preserved on similar-cases items and
CompanyDocument.source_url. Access dates:downloaded_date/verified_date/last_updated. Not a structured per-claim source object. - Research updatability: Yes — admin refresh, scheduled refresh, cache-miss re-run.
- Allegation-as-fact risk: Prompt explicitly guards against it;
company_confidentflag filters mismatches. But the final report is LLM-synthesized and could still present a complaint pattern as company behavior. Moderate residual risk.
PART 10 — CASE TIMELINE AND AUDITABILITY
Records present:
- Case creation ✅ (
Case.created_date) - Intake ⚠️ (
IntakeSessionrecord, not shown in the case timeline) - Documents ✅ (
CaseDocumentin evidence drawer) - Research ⚠️ (not explicitly timestamped in the timeline)
- AI analysis ✅ (
Case.analyzed_date; re-analysis bumpsnotes_edit_count) - Draft emails ✅ (
Correspondencedraft records) - Customer edits: ⚠️ No. Editing a draft overwrites
contentwith no version history. - Customer approval: ⚠️ Inferred from
status: 'sent'— no separate "approved" event/timestamp beyondsent_date. - Sent emails ✅ (
sent+sent_date+external_message_id) - Received emails ✅ (inbound +
received_date) - Case changes ✅ (
updated_date, status transitions, no per-field history) - Case closure ✅ (
resolved_date,resolution_method,resolution_notes)
Editability/deletability: Correspondence records can be updated and deleted by the owner/admin. in_timeline can be toggled. Timeline entries are not immutable — an owner can edit or remove a sent letter's content or delete the record. Case and TrackedAction also editable/deletable.
Admin audit log: No dedicated administrative audit log. SessionEvent tracks client-side page views/clicks, not admin data-access or mutations. ChangeLog is a manually-posted release log. No append-only record of admin viewing/editing customer cases, documents, or emails. These records are all mutable.
PART 11 — DATA RETENTION AND DELETION
| Data | Retention |
|---|---|
| User accounts | Indefinite until platform account deletion (no in-app self-serve deletion) |
| Cases | Indefinite (owner/admin/agent can delete) |
| Case documents | Indefinite record; file bytes on public storage persist after record deletion |
| Emails (Correspondence) | Indefinite |
| Attachments (inbound) | Not stored at all (names only) |
| AI conversations | Ephemeral |
| AI reports | Indefinite on Case.ai_analysis/free_analysis, Company.suggestions |
| Research | 30-day TTL for refresh, not auto-purged |
| Company data | Indefinite |
| Logs | SessionEvent indefinite (admin can delete); no log rotation |
User capabilities:
- Delete documents: ✅ (but file bytes persist)
- Delete cases: ✅
- Close cases: ✅
- Delete account: ❌ No self-serve account deletion in-app (Base44 support)
- Request deletion of their information: ❌ No in-app flow
Actual removal: Deleting a CaseDocument removes the DB record but does not remove the file from media.base44.com — no delete call wired. Deleting a case does not cascade-delete correspondence/documents/time entries. No purge of data sent to AI providers (not under FeudFix's control).
PART 12 — PRIVACY AND SENSITIVE DATA
Collected/stored:
- Customer
full_name,address,phone,email. - Case
description— free text, may contain anything (names, account numbers, transaction details, dates, incident narratives). - Uploaded evidence — any file: receipts, contracts, bills, bank statements, insurance docs, medical bills, ID photos, screenshots. No type restriction.
- Correspondence content (outbound + inbound).
Case.purchase_date,product_or_service,desired_resolution.- User
email,role,agent_number.
Warnings to users not to submit unnecessary sensitive info: No. Intake and uploader do not warn customers to avoid SSNs, medical records, financial credentials, government IDs, etc. Uploader label is simply "Click to upload evidence & contracts."
Sensitive info sent to third-party AI/search providers: Yes, broadly.
Case.descriptionand clarifying answers → LLM at intake.- Uploaded evidence
file_urls → Gemini 3.1 Pro (vision) during full analysis; →ExtractDataFromUploadedFileduring intake. - Inbound company replies → LLM during re-analysis.
- Company dispute-strategy research sends company name + complaint description to web-enabled Gemini.
Data flow: Customer enters complaint → browser → Base44 SDK → LLM provider. Customer uploads file → browser → UploadFile (public storage) → LLM provider (file_urls). Inbound email → SendGrid → webhook → Correspondence → LLM on re-analysis. No redaction/PII-scrubbing before LLM calls.
Risk: Customers may upload documents containing SSNs, medical info, or financial account numbers; these are stored on public storage and sent to AI providers with no warning, no redaction, and no privacy policy disclosure.
PART 13 — ADMINISTRATIVE SECURITY
- Who can access customer cases:
admin(all),agent(all — not assignment-scoped), and the case owner.beta_testeris not granted case read but can delete correspondence and edit companies/prompts. - Who can access uploaded documents: case owner, admin. Agents cannot read
CaseDocumentbut can readCustomerPII. - Who can access emails (Correspondence): case owner, admin. Agents cannot. Good separation.
- MFA: Not enforced by the app. Admin accounts have no app-level MFA requirement.
- Admin action logging: No. No record of which admin viewed or edited which case/customer/document/email.
SessionEventrecords client-side page views/clicks, not admin data-access or mutations.ChangeLogis a release-notes log, not an access audit. - Role-based access: Yes — RLS by role. But the
agentrole is over-broad (all cases, all customers). - All admins see all customer data: Yes. No admin segmentation.
Unnecessary privileges:
- Agents reading all customers (PII) and all cases — should be assignment-scoped.
beta_testerable to delete correspondence and edit companies/prompts — broad for a testing role.- Admin ability to read full correspondence content — necessary for support but unlogged.
PART 14 — CURRENT ANALYTICS
Tracked today:
- Landing-page visits ✅ (
SessionEventpage_view on/) - Intake starts ✅ (
IntakeSessionstatusstarted) - Intake completions ✅ (
IntakeSessionstatuscreated+ abort categories) - Account creation ❌ (not explicitly tracked)
- Document uploads ❌ (no dedicated event; inferable from
CaseDocument) - Full analysis run ⚠️ (inferable from
Case.analyzed_date; no explicit event) - Report completion ✅ (inferable from
Case.ai_analysis) - Email drafting ⚠️ (inferable from
Correspondencedrafts) - Email approval ❌ (no distinct "approved" event; only
sent_date) - Email sending ✅ (
Correspondencesent+sent_date) - Case resolution ✅ (
resolved_date,resolution_method,ResolvedCaseSummary) - Payment ❌ (no server-side payment record; only a
localStorageflag and Stripe redirect — actual purchase completion not captured in-app) - User abandonment ✅ (
IntakeSessionabort categories:free_limit_reached,triage_unsupported,company_not_found,user_aborted_back,user_abandoned_case,intake_error,auth_required_drop)
Unavailable commercial-validation metrics:
- Actual purchase (no server-side payment confirmation — Stripe webhook not wired).
- Baseline intended action (what the customer planned before analysis).
- Post-analysis change in intended action.
- Advocate time saved (
TimeEntryexists but no before/after comparison). - "Did FeudFix find something the customer didn't know" / "did it change the plan" — no survey instrument.
- Willingness-to-pay signal beyond checkout click.
PART 15 — COMMERCIAL VALIDATION REQUIREMENTS
What exists:
- Intake completion ✅, case qualification ✅, document upload ✅ (record-level), full analysis ✅ (record-level), case outcome ✅ (
ResolvedCaseSummary), abandonment ✅. - Advocate time tracking ✅ (
TimeEntrywith billable rate/duration).
What needs to be added for the 6 commercial questions:
- Did FeudFix find something useful the customer didn't know? → post-analysis micro-survey ("Did you learn something new? Y/N" + free text). Not present.
- Did FeudFix change what the customer planned to do? → baseline-intended-action capture at intake + post-analysis follow-up. Not present.
- Did FeudFix save an advocate time? →
TimeEntryexists, but no advocate-reported time-saved estimate. Needs a single field per case. - Did FeudFix contribute to a better outcome? → post-resolution rating from the customer. Not present.
- Would the customer pay? → post-resolution willingness-to-pay prompt. Not present.
- Would an advocate pay? → advocate sentiment capture. Not present.
Minimum additions for the test: a lightweight post-analysis survey (2-3 questions) and a post-resolution survey (2-3 questions), stored on the Case or a small SurveyResponse entity; plus a server-side payment record (Stripe webhook) so "actual purchase" is real.
PART 16 — REQUIRED FUTURE CHANGES (HOW, NOT IMPLEMENTED)
A. Two-stage intake: Add a lightweight intake that collects what-happened, company, desired outcome, approximate dollar amount, already-contacted, current status, duration, documentation available, desired resolution — before the existing chat. Compute an internal Case Opportunity Score from a deterministic function of (dollar amount, documentation, already-contacted, category). Do not show it as success probability. Route: low-score → mini-analysis only; high-score → full research chain. Complexity: Medium. Base44-implementable: Yes.
B. Resource levels (1/2/3): Map the Opportunity Score to a level gating which AI/web steps run. Level 1 = mini-analysis only. Level 2 = mini + cached company intel. Level 3 = full research + gemini_3_1_pro. Complexity: Medium. Base44-implementable: Yes.
C. Remove resolution probability: Either remove resolution_probability from the displayed schema, or replace it with the empirical Intelligence Index intel_resolution_rate (with confidence) and a plain-language estimate ("low/moderate/high"). Complexity: Low. Base44-implementable: Yes.
D. Case report structure: Reorganize the analysis output schema into explicit sections: Customer Account, Documented Facts, Company Information, Similar Complaints, FeudFix Analysis, Possible Next Steps, Missing Documentation. Complexity: Medium. Base44-implementable: Yes.
E. Source attribution: Add a sources[] array to the analysis schema and CompanyDocument: { url, title, source_type, date_accessed, relevant_info }. Persist date_accessed at fetch time. Cite sources by index. Complexity: Medium. Base44-implementable: Yes.
F. Customer approval before sending: Already implemented (manual send). Gap: no explicit "approved" state/timestamp; approval is implicit in status: 'sent'. Strengthen: add approved_at/approved_by on send, and keep an immutable original AI draft (version it, don't overwrite). Complexity: Low–Medium. Base44-implementable: Yes.
G. Transparent case email: Change from.name to the customer's name (or "{Name} via FeudFix"), add a prominent in-body disclaimer that FeudFix is not a law firm and sends on the customer's behalf, keep reply_to on the virtual inbox. Complexity: Low. Base44-implementable: Yes.
H. Audit log: Create an AuditEvent entity (append-only via RLS). Log: case viewed by non-owner admin, case edited, document read, correspondence sent, admin role change, escalation. Log from backend functions. True append-only not natively supported by Base44 RLS (admins could update/delete), so "protected" rather than cryptographically immutable. Complexity: Medium. Base44-implementable: Yes (with caveat).
I. Security hardening:
- Move case evidence to
UploadPrivateFile+ signed URLs (expire). Base44-implementable: Yes. - Server-side entitlement check before the full analysis (backend function). Base44-implementable: Yes.
- Scope agent reads to assigned cases/customers (add assignment field + RLS rule). Base44-implementable: Yes.
- Close
getCase/saveCaseAnalysisunclaimed-case IDOR (share token or session binding). Base44-implementable: Yes. - Admin MFA — platform-level, not app-controllable. Base44-implementable: No.
- Data encryption — platform-managed at rest. Base44-implementable: No.
- Secure deletion — wire file-blob deletion on
CaseDocumentdelete. Base44-implementable: Partially.
J. Commercial analytics: Add post-analysis + post-resolution micro-surveys, wire a Stripe webhook to a backend function recording a Purchase entity, add advocate-time-saved and willingness-to-pay fields. Complexity: Medium. Base44-implementable: Yes.
PART 17 — PRIORITIZATION
| # | Requirement | Current state | Risk/problem | Recommended change | Base44? | Complexity | Commercial importance | Security importance | Timing |
|---|---|---|---|---|---|---|---|---|---|
| 1 | Server-side payment record | localStorage flag only; Stripe redirect, no webhook | "Actual purchase" not verifiable; paywall bypassable | Wire Stripe webhook → Purchase entity; gate entitlement on it | Yes | Medium | Critical | High | Before testing |
| 2 | Server-side entitlement gate on full analysis | reanalyzeCase calls LLM from browser; paywall is localStorage | Non-paying users can burn the most expensive credits | Move full analysis behind a backend function with entitlement check | Yes | Low | Critical | High | Before testing |
| 3 | Email identity transparency | from.name = "FeudFix"; no disclaimer | Recipient may mistake FeudFix for a law firm / the author | Customer name as from.name + in-body disclaimer | Yes | Low | High | Medium | Before testing |
| 4 | Private evidence storage | Public UploadFile URLs for receipts/contracts/PII | Anyone with the URL downloads evidence; links leak | Switch to UploadPrivateFile + signed URLs | Yes | Medium | Medium | Critical | Before testing |
| 5 | Admin/agent MFA | None enforced | Admin account takeover exposes all customer data | Enforce at platform/account level | No | Low | Medium | Critical | Before testing |
| 6 | Admin action audit log | None | No traceability of who accessed/edited what | AuditEvent entity, log from backend functions | Yes | Medium | Low | High | During testing |
| 7 | Post-analysis + post-resolution surveys | None | Can't measure the 6 commercial questions | 2-3 question surveys on Case/SurveyResponse | Yes | Low | Critical | Low | Before testing |