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)
- 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_runstable on success. - 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").
- 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.) toclio_status_events.pending → openis the primary conversion event and fires an alert when it happens. - 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. - 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.
- 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.
- 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_configand 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
alertstable 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.
- Visit the sign-in page
- Enter your work email
- Check your inbox for a link from Acuna Law Portal
- 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 incampaigns.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:
git checkout main && git pull origin main— start from the latestgit checkout -b feature/short-description— new branch per change- Make changes locally. Run
npm run preflightfrom project root to verify nothing's broken. For portal changes,cd portal && npm run devfor hot-reload preview. git push -u origin feature/short-descriptionand open a PR on GitHub- Ben (super_admin / GitHub admin) reviews + merges
- 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):
- Create Person Contact with the owner's mailing address
- Create Matter (status=pending, custom field "Ben Jackson Auto Direct Mail", MRED listing URL in description)
- Upload the letter PDF as a Document on the matter
- 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.