Files
HOALedgerIQ_Website/AI_SETUP.md
olsch01 b658f50c9c Add Chatwoot support widget and AI API setup documentation
- Insert Chatwoot chat widget script at bottom of index.html body
  (connects to chat.hoaledgeriq.com with token 1QMW1fycL5xHvd6XMfg4Dbb4)
- Add AI_SETUP.md documenting how to upgrade the benefit calculator
  from client-side math to live AI API calls (Claude or OpenAI),
  including endpoint code, app.js changes, prompt tuning, cost
  estimates, and rate limiting guidance

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 09:54:53 -04:00

6.5 KiB

HOA LedgerIQ — AI API Configuration Guide

Overview

The Benefit Calculator widget currently runs entirely client-side using conservative fixed-rate math. This guide explains how to upgrade it to call a real AI API (Claude or OpenAI) so the recommendation text is generated dynamically by a language model.


Architecture

Browser (app.js)
  └─► POST /api/calculate   (server.js)
        └─► Anthropic / OpenAI API
              └─► Returns AI-generated investment recommendation text
                    └─► JSON response back to browser

Step 1 — Add your API key to .env

Open .env in the project root and add one of the following:

# For Claude (Anthropic) — recommended
ANTHROPIC_API_KEY=sk-ant-...

# OR for OpenAI
OPENAI_API_KEY=sk-...

Step 2 — Install the SDK

# Claude (Anthropic)
npm install @anthropic-ai/sdk

# OR OpenAI
npm install openai

Step 3 — Add the /api/calculate endpoint to server.js

Add this block after the existing /api/health route:

Using Claude (Anthropic)

const Anthropic = require('@anthropic-ai/sdk');
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });

app.post('/api/calculate', async (req, res) => {
  const { homesites, propertyType, annualIncome, paymentFreq, reserveFunds, interest2025 } = req.body ?? {};

  if (!homesites || !annualIncome) {
    return res.status(400).json({ error: 'homesites and annualIncome are required.' });
  }

  const fmt = n => '$' + Math.round(n).toLocaleString();
  const freqLabel = { monthly: 'monthly', quarterly: 'quarterly', annually: 'annual' }[paymentFreq] || 'monthly';
  const typeLabel = { sfh: 'single-family home', townhomes: 'townhome', condos: 'condo', mixed: 'mixed-use' }[propertyType] || '';

  const prompt = `You are a conservative HOA financial advisor. Given the following community data, provide a brief (3-4 sentence) plain-English investment income recommendation. Use only conservative, realistic estimates. Do not speculate beyond what the data supports.

Community: ${homesites}-unit ${typeLabel} association
Annual dues income: ${fmt(annualIncome)} (collected ${freqLabel})
Reserve fund balance: ${fmt(reserveFunds || 0)}
Interest income earned in 2025: ${fmt(interest2025 || 0)}

Provide a recommendation focused on:
1. How much of the reserve funds could conservatively be invested and in what vehicle (e.g. CD ladder, money market, T-bills)
2. How much operating cash could earn interest between collection and expense periods
3. A realistic estimated annual interest income potential
4. A single sentence comparing that to their 2025 actual if provided

Keep the tone professional and factual. No bullet points — flowing paragraph only.`;

  try {
    const message = await anthropic.messages.create({
      model: 'claude-opus-4-6',
      max_tokens: 300,
      messages: [{ role: 'user', content: prompt }],
    });

    const text = message.content[0]?.text ?? '';
    res.json({ recommendation: text });
  } catch (err) {
    console.error('AI API error:', err.message);
    res.status(502).json({ error: 'AI service unavailable. Showing estimated result.' });
  }
});

Using OpenAI (GPT-4o)

const OpenAI = require('openai');
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

app.post('/api/calculate', async (req, res) => {
  const { homesites, propertyType, annualIncome, paymentFreq, reserveFunds, interest2025 } = req.body ?? {};

  // ... (same prompt construction as above) ...

  try {
    const completion = await openai.chat.completions.create({
      model: 'gpt-4o',
      max_tokens: 300,
      messages: [{ role: 'user', content: prompt }],
    });

    const text = completion.choices[0]?.message?.content ?? '';
    res.json({ recommendation: text });
  } catch (err) {
    console.error('AI API error:', err.message);
    res.status(502).json({ error: 'AI service unavailable.' });
  }
});

Step 4 — Update app.js to call the API endpoint

In the initCalculator function, replace this line in the submitBtn handler:

document.getElementById('calcAiText').textContent = ai;  // current: client-side text

With this:

// Call the AI endpoint; fall back to client-side text if unavailable
try {
  const aiRes = await fetch('/api/calculate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ homesites, propertyType, annualIncome, paymentFreq, reserveFunds, interest2025 }),
  });
  if (aiRes.ok) {
    const { recommendation } = await aiRes.json();
    document.getElementById('calcAiText').textContent = recommendation;
  } else {
    document.getElementById('calcAiText').textContent = ai; // fallback
  }
} catch (_) {
  document.getElementById('calcAiText').textContent = ai; // fallback
}

Note: The submitBtn handler must be declared async for the await above to work:

submitBtn?.addEventListener('click', async () => { ... });

Step 5 — Restart the server

sudo systemctl restart hoaledgeriqweb

# Verify the endpoint is live
curl -X POST http://localhost:3000/api/calculate \
  -H "Content-Type: application/json" \
  -d '{"homesites":150,"propertyType":"sfh","annualIncome":300000,"paymentFreq":"monthly","reserveFunds":500000,"interest2025":4200}'

Prompt Tuning Tips

The prompt in Step 3 is the core of the AI's behavior. You can adjust it to:

Goal Change
More optimistic estimates Change "conservative" to "moderate" in the prompt
Shorter output Reduce max_tokens to 150
Include specific investment products Add "mention specific products like Vanguard Federal Money Market or 6-month T-bills"
Add a disclaimer Append "End with one sentence reminding them this is not financial advice."

Cost Estimate

Model Approx. cost per calculator use
Claude Opus 4.6 ~$0.002
Claude Sonnet 4.6 ~$0.0004
GPT-4o ~$0.002
GPT-4o-mini ~$0.00005

For a landing page with low traffic, even Claude Opus is negligible cost. For scale, claude-sonnet-4-6 is the best balance of quality and price.


Security Notes

  • Never expose your API key in app.js or any client-side code. All AI calls must go through server.js.
  • Rate-limit the /api/calculate endpoint to prevent abuse (e.g. with express-rate-limit):
npm install express-rate-limit
const rateLimit = require('express-rate-limit');
const calcLimiter = rateLimit({ windowMs: 60 * 1000, max: 10 }); // 10 req/min per IP
app.use('/api/calculate', calcLimiter);