Buckets every open invoice by how overdue it is, flags what newly crossed 30/60/90 days, and drafts the polite chase email — which a person always sends.
Daily digest to the collections team: buckets, new crossings, top 10 — plus ready-drafted chase text per customer, saved into the digest for the team to copy, edit and send themselves.
Invoice data didn't exist in our reporting warehouse at all — the finance team had no aging view outside the ERP. One data extension (built for this loop) now syncs invoices nightly, and the first live run found AED 2.08M overdue across 96 invoices.
After the overnight invoice sync
Amounts unpaid, due dates, customers
Aging buckets + what newly crossed since yesterday
Polite, per-customer — saved into the digest
The team copies what they approve; the loop never emails a customer
For implementers and the technically curious. The full build sheet — verified queries, thresholds, and build notes — lives on the specs page.
| System | Role in this loop |
|---|---|
| NetSuite warehousePostgres on our server | Our nightly read-only copy of NetSuite (ERP) data — items, stock, transactions — refreshed by the ETL sync every morning. The loop queries this copy, never NetSuite itself. |
| ETL extensionraw.invoices | The data connection built for this loop: 24 months of customer invoices with open balances, syncing nightly with per-step fault isolation — it also unlocked FIN-2's calculations. |
| Metabasebi.huxapps.com | The BI layer over the warehouse. The loop runs its SQL through Metabase's API, and every number in the digest links to a live, clickable Metabase list so you can drill into the exact records. |
| Postgres “loops” schemareporting database | A small dedicated schema where loops keep snapshots and history that need SQL (week-over-week diffs, mirrors for drill-down links). Scoped role; Metabase can read it for the clickable aggregate links. |
| Hermesagent runtime on our server | The scheduler that wakes the loop up. Each loop is a cron job under a Hermes profile; the planned bizops profile will host the business digests (IT loops run under vpsops). |
| Healthcheckshealthchecks.huxapps.com | The dead-man's switch. The loop pings it only after a clean run — if the loop dies or errors, the ping stops and Healthchecks raises the alarm independently. This is how 'never silent' is enforced by machinery, not promises. |
| Email renderer + gwsrender_email.py | All digests pass through one shared renderer: Huxberry-branded HTML, tables for repeated rows, a coral 'needs your response' box when the loop has questions, and an arrow link on every record. Sent from the loops mailbox via the Google Workspace CLI. |
| Loop chassisloop_common.py | Shared plumbing every loop reuses instead of reinventing: state files, run-over-run diffing, Metabase drill-down link building, quiet-on-green notify logic. |
| Model / brain | What it does here |
|---|---|
| None at run timedeterministic script | A normal cycle is a plain Python script — no AI tokens are spent unless a diagnosis or judgment step is actually needed. AI wrote the script; the script does the rounds. |
| GPT-5.5 via Codexthe bulk-work model | Wrote and maintains the mechanical parts — SQL, diffing, digest assembly. Effectively free on our existing subscription, so routine cycles cost almost nothing. |
| Claude Opusthe judgment model | Reviews alert wording, thresholds, and anything a human will read and act on. Post-Fable, Opus owns everything that ships. |
Yesterday's invoice snapshot for the newly-overdue diff; a running ledger of what's been flagged so nothing is nagged twice in one bucket.
Daily email digest; invoice links to the ERP, bucket totals to live BI lists. A finance feed card (headline: total AR overdue; tiles per bucket) follows.
Read-only. Outward-facing text (chase drafts) is the one place the judgment model always reviews wording — it ships to customers, via human hands.