Acuna Law — Admin Portal

Sign in →

What is this?

The Acuna Law admin portal is the management interface for an automated MLS-to-Mail lead engine. Every weekday morning the system scrapes new Cook County property listings from MRED, identifies eligible sellers, calculates personalized tax-credit + closing-fee estimates, prints + mails a physical letter via Lob, and creates a matching Clio matter so an attorney can follow up. This portal lets the firm see what was mailed, monitor pipeline health, edit the letter copy, adjust operational settings, and triage anything that breaks — all without code access.

How the pipeline runs (timing, in Chicago time)

  1. 6:00 AM — Scraper (primary, runs on Ben's Mac Mini at home) via launchd. Logs in to MRED MLS, downloads the day's new detached + attached single-family listings in Cook County, enriches each PIN with Cook County Property Info taxpayer data, writes results to the Google Sheet. Heartbeat written to Supabase scraper_runs table on success.
  2. 7:00 AM — Scraper failover (GitHub Actions). Wakes up, checks the Mini's heartbeat. If the Mini ran in the last 12 hours (configurable), this exits cleanly. If the Mini didn't run, GitHub Actions takes over AND fires a critical alert to Ben's phone ("Mini scraper missed its window").
  3. 8:00 AM — Clio status sync (GitHub Actions). Polls Clio for the status of every matter we've created. Updates the campaigns table and writes transitions (pending → open, etc.) to clio_status_events. pending → open is the primary conversion event and fires an alert when it happens.
  4. 9:00 AM — Dispatch worker (GitHub Actions, Mon–Fri). Reads the sheet, filters for eligible leads (Date Listed ≥ GO_LIVE_DATE, no prior mailer, complete tax data), parses owner names with Claude, validates mailing addresses with Google Maps, calculates tax credit + closing fees, renders the letter HTML, sends to Lob, archives the PDF, writes the mailer pointer back to the sheet, and pushes a Contact + Matter + Document + Note to Clio.
  5. Hourly — Reconciler (GitHub Actions). Sweeps stuck in-flight rows, retries failed Clio pushes, retries dropped sheet writebacks. Idempotent — no risk of double-mailing because Lob dedupes by idempotency key + we have a unique constraint on (PIN, MLS#, touch number) in the database.
  6. Every minute — Alert poller (Mac Mini). Reads the alerts queue and delivers via iMessage to Ben's phone (737-781-3629) + email to bjackson35773@gmail.com. Critical alerts get pushed immediately.
  7. Quarterly — IDOR multiplier scraper (GitHub Actions). Watches the Illinois Department of Revenue news index for new "Cook County Final Multiplier" press releases. When the new value appears (typically late May each year), auto-updates closing_costs_config and fires a critical alert so Ben can review the new math before letters go out with it.

Where everything lives

The Mac Mini is the core of the system. It runs the scraper as the primary, hosts the alert poller for iMessage delivery, and is where Ben does most of the manual operational work via SSH. Tailscale IP 100.92.235.86 (always-on, won't change). If the Mini goes offline (power outage, ISP cut, hardware crash), the system stays functional:

  • The scraper failover (GitHub Actions, 7 AM ET) detects the missing heartbeat and runs the scrape instead. Critical alert is fired so Ben knows.
  • Dispatch + reconciler + Clio sync are GitHub Actions cron jobs — totally independent of the Mini, keep running.
  • Email alerts go through Supabase (independent of Mini); only iMessage delivery stops until Mini comes back. Pending iMessages queue in the alerts table and deliver when the Mini boots up.
GitHub repo
JXNmaster/acuna-law (private)
Portal hosting
Cloudflare Pages (acuna-law-portal.pages.dev)
Database
Supabase project gixykuybreelzfnzomrw
Cloud storage
Supabase Storage (letters/ + letter-pdfs/)
Email delivery
Supabase Auth + Gmail SMTP (sender bjackson35773@gmail.com)
iMessage alerts
Mac Mini AppleScript → 737-781-3629
Mini path
/Users/benm4mini/Documents/Projects/10_AcunaLaw
Mini Tailscale IP
100.92.235.86

How to log in

Passwordless magic-link auth. No password to remember or forget.

  1. Visit the sign-in page
  2. Enter your work email
  3. Check your inbox for a link from Acuna Law Portal
  4. Click it — you're in

Access is by invitation. The admin_users table is the whitelist. Three roles: super_admin (full access — Ben), admin (manage campaigns/alerts/Clio — Chris), user (read-only — Chris's law clerks). To add or remove people, ping Ben.

How to change things

  • Edit letter copy: /template page (admin or super_admin). Each paragraph + bullet + signer name + signer title of the letter is editable in the Edit tab. Save takes effect on the next dispatch — no deploy. Placeholders like {{property_address}} are substituted at render time. Layout (Acuna Law logo, page break, fees table, IL Rule 7.3 footer, Chris's signature image) is code-controlled and not editable through the portal — that's intentional, since structural changes can break Lob rendering or compliance. The Preview tab shows actual letters from recent dispatched campaigns. The History tab shows every saved version of the copy — each campaign records which version it was rendered against in campaigns.template_revision_id. The legacy Google Doc renderer is retained as an emergency fallback only; do not edit the Doc itself.
  • Edit closing-cost rates: /closing-costs page (super_admin only). Every number used in the seller-net estimate (equalization factor, transfer taxes, title insurance, agent commission, etc.) is editable with its source URL displayed. The hourly reconciler fires an info alert when any rate hasn't been verified in over a year. Cook County's equalization factor auto-updates from IDOR each quarter via the scraper.
  • Flip operational flags: /config page (super_admin only). Examples: dispatch_friday_enabled, mailer_enabled (kill switch — flip to false to pause everything), clio_enabled, scraper_vision_enabled, enrichment_per_run_cap.
  • Search a lead: /campaigns — by PIN, MLS#, or recipient name. Click a card for full detail.
  • Dismiss alerts: /alerts — per-alert Dismiss button or "Mark all as read".

How to change the code (PR workflow)

Production main branch is protected — direct pushes are blocked for everyone except Ben (admin override). Change flow:

  1. git checkout main && git pull origin main — start from the latest
  2. git checkout -b feature/short-description — new branch per change
  3. Make changes locally. Run npm run preflight from project root to verify nothing's broken. For portal changes, cd portal && npm run dev for hot-reload preview.
  4. git push -u origin feature/short-description and open a PR on GitHub
  5. Ben (super_admin / GitHub admin) reviews + merges
  6. On merge to main, GitHub Actions auto-deploys: portal → Cloudflare Pages (~30 sec), workers → next scheduled run picks up the new code

Branch protection rules: PR review required, status checks must pass, only Ben can bypass. This applies to BJ + Chris and any future collaborators.

Where the tokens live (and how to regenerate)

All operational credentials live in /Users/benm4mini/Documents/Projects/10_AcunaLaw/.env.local on the Mac Mini (never committed to git — gitignored). Production GitHub Actions reads from repo secrets set with the same names. If a token is lost or rotated, regenerate from the provider and update both the Mini and the GitHub secret.

ANTHROPIC_API_KEY

Anthropic Console → API Keys

Project-scoped key for Claude. Used for owner-name parsing.

https://console.anthropic.com/settings/keys

GOOGLE_MAPS_API_KEY

GCP Console (project content-site-web-1729006942927) → APIs & Services → Credentials

Used for address validation. Restrictions are currently OFF; re-enable IP allowlist if needed.

https://console.cloud.google.com/apis/credentials

GOOGLE_SERVICE_ACCOUNT_KEY_PATH

GCP Console → IAM → Service Accounts → acuna-law-app@content-site-web... → Keys

JSON key file at ~/.config/acuna-law/service-account.json on the Mini. Used for Sheets + Drive + Docs API.

https://console.cloud.google.com/iam-admin/serviceaccounts

MLS_USERNAME / MLS_PASSWORD

MRED ConnectMLS (Chris's account, MRED ID 886211)

If MRED locks the account or rotates the password, Chris regenerates from his MRED portal.

https://connectmls1.mredllc.com

LOB_API_KEY_TEST / LOB_API_KEY_LIVE

Lob Dashboard → Settings → API Keys

Live key prints + mails real letters. Test key is free, harmless, used for everything until production go-live.

https://dashboard.lob.com/settings/api-keys

SUPABASE_SERVICE_ROLE_KEY / SUPABASE_ANON_KEY

Supabase Dashboard → Settings → API

Service role key bypasses RLS; treat like a root password. Anon key is fine in browser code.

https://supabase.com/dashboard/project/gixykuybreelzfnzomrw/settings/api

SUPABASE_ACCESS_TOKEN (PAT)

Supabase Dashboard → Account → Access Tokens

Personal access token for the Supabase Management API (migrations). Stored in macOS keychain as service 'Supabase CLI' / account 'supabase'.

https://supabase.com/dashboard/account/tokens

CLIO_CLIENT_ID / CLIO_CLIENT_SECRET

Clio Manage → Settings → Developer Applications (app 'Acuna Lead Engine', id 32063)

OAuth app credentials. Refresh token + access token are stored in Supabase system_config (clio_refresh_token, clio_access_token), refreshed automatically.

https://app.clio.com/settings/developer_applications

CLOUDFLARE_API_TOKEN

Cloudflare → My Profile → API Tokens (logged in as mycroftjackson@gmail.com)

Scoped to the 'Acuna Law' Cloudflare account only. Used by wrangler to deploy the portal.

https://dash.cloudflare.com/profile/api-tokens

How Clio sync works

The pipeline is bidirectional with Chris's Clio Manage account (registered as "Acuna Lead Engine", OAuth app 32063).

Outbound (every dispatched letter):

  1. Create Person Contact with the owner's mailing address
  2. Create Matter (status=pending, custom field "Ben Jackson Auto Direct Mail", MRED listing URL in description)
  3. Upload the letter PDF as a Document on the matter
  4. Create a Note summarizing the lead (PIN, property, list price, tax credit estimate)

If any step fails, the previous records roll back so Chris's Clio doesn't accumulate orphan contacts.

Inbound (daily at 8 AM ET):

Polls every matter we've created, writes current status back to the database. pending → open = real client engagement (the headline metric). open → closed = matter completed.

Compliance + retention

Every letter mailed includes an "Advertising Material" footer required by Illinois Rule of Professional Conduct 7.3. The rendered HTML and the Lob PDF are archived in Supabase Storage indefinitely — IL Rule 7.3 requires attorneys to retain copies of solicitation materials for 2 years. The template revision ID is recorded on each campaign so we can prove which copy was used for any specific mailpiece during an audit.

Who's involved

Ben Jackson (bjackson35773@gmail.com) — owner, super_admin, on-call for everything
Chris Acuna (cacuna@acunalawoffices.com) — firm owner, admin role, primary user of Clio matters created by this system
BJ (Bryan Huls) — original scraper builder. Still has his version running until full cutover. Reach via Slack/email.
Mycroft Jackson (mycroftjackson@gmail.com) — Ben's coding-agent identity. Holds the Cloudflare account.