An autonomous accounts-receivable agent that collects overdue invoices — and knows when not to.
Most collection bots send more messages when a debtor doesn't pay. Settle does the opposite: it prices every action before taking it. It chases overdue B2B invoices by email, SMS or AI voice call, pays for its own usage (Twilio, ElevenLabs, LLM inference) out of a live ledger, and takes a real success commission through Stripe. When contacting a debtor is expected to cost more than it recovers, it downgrades the channel or skips the outreach entirely — and books the loss it avoided.
Built solo in about a week for Hermes Agent — Accelerated Business, a hackathon by NVIDIA × Stripe × Nous Research (June 2026). It did not place; the economics engine and the egress sandbox are the parts worth reading.
An agent that spends real money on every action needs a reason to stop. Give an LLM a list of overdue invoices and a phone API and it will happily burn $1.20 of voice call chasing a $40 invoice that was never going to be paid. Settle makes the spend/recover tradeoff an explicit, auditable calculation instead of an emergent behavior — the same ledger that pays its bills is the one it consults before acting.
For each debtor d and channel c ∈ {email, sms, voice}:
value(d, c) = amount(d) × P_pay(d, c, days_overdue) − cost(c)
choose c* = argmax value(d, c)
if max value < 0 → skip paid outreach (free email only)
P_pay comes from the attempts table — real historical outcomes of previous contact attempts —
not a hardcoded heuristic. Costs run roughly voice ≈ $1.20, sms ≈ $0.08, email ≈ $0.001,
plus metered inference. It's a formula, not an if: when the numbers say a voice call loses money,
the agent doesn't place it.
overdue invoices ──▶ [scan] ──▶ [prioritize: expected recovery vs. channel cost]
│
profitable ───────┴─────── not profitable
│ │
[QA gate] ──▶ send / call free email only
consent · (loss avoided, recorded)
AI disclosure ·
opt-out · amount
│
▼
debtor pays via Stripe (live mode)
│
▼
webhook ──▶ signature verified ──▶ ledger
│ (+recovered, +commission)
▼
P&L updates live over SSE ──▶ funds the next collection
| Layer | Role |
|---|---|
| Agent core | Runs the loop; loads on-demand skills (skills/*.md), one file per capability |
| Decision skill | Per-channel unit economics from empirical attempt data — the only place a channel is chosen or skipped |
| QA gate | Consent on file, AI disclosure present, amount correct — checked before anything is sent |
Sidecar (app/sidecar.py) |
FastAPI: verifies Stripe webhook signatures, updates the ledger, pushes live P&L over SSE, serves TwiML audio |
Egress sandbox (policy.yaml + security/) |
Docker + squid proxy on an isolated network — the agent's container has no default route out except through the allowlist |
| Storage | SQLite, append-only ledger: invoices, debtors (consent/opt-out), ledger (earn/spend), attempts (outcomes → P_pay) |
The sandbox is not a mock. policy.yaml is the source of truth: every allowed host becomes a squid
ACL, and network.default: deny becomes the deny rule. A request to any undeclared host gets a real
403 — reproduce it with scripts/demo-403.sh. Host-level (not verb-level) enforcement is the honest
limit for HTTPS traffic, and security/README.md explains why.
Python 3.11 · FastAPI + SSE · SQLite · Stripe (live mode, Connect/Checkout) · Twilio (voice) · ElevenLabs (TTS) · Resend (email) · NVIDIA Nemotron via NIM · Docker + squid (egress sandbox).
git clone https://github.com/Enrique-Omen/settle && cd settle
python3 -m venv .venv && .venv/bin/pip install fastapi uvicorn stripe
cat > .env <<'ENV'
STRIPE_SECRET_KEY=<YOUR_STRIPE_KEY>
STRIPE_WEBHOOK_SECRET=<YOUR_STRIPE_WEBHOOK_SECRET>
TWILIO_ACCOUNT_SID=<YOUR_TWILIO_SID>
TWILIO_AUTH_TOKEN=<YOUR_TWILIO_TOKEN>
ELEVENLABS_API_KEY=<YOUR_ELEVENLABS_KEY>
RESEND_API_KEY=<YOUR_RESEND_KEY>
NVIDIA_API_KEY=<YOUR_NVIDIA_NIM_KEY>
ENV
sqlite3 app/settle.db < app/schema.sql # schema
python3 scripts/seed_history.py # demo business, debtors, attempt history
.venv/bin/uvicorn sidecar:app --app-dir app --port 8444Point a Stripe webhook (payment_intent.succeeded, charge.succeeded) at
https://<YOUR_DOMAIN>/webhook, then run scripts/settle-decide.py to see the decision engine
choose a channel — and downgrade one debtor to email because the voice call doesn't pay for itself.
Settle chases a business's own invoices, B2B only, with prior consent on file. Every message opens with an AI disclosure and offers an opt-out, and the QA gate blocks any message missing them. That is first-party, consent-based follow-up — outside the FDCPA/TCPA regime that governs third-party consumer debt collection.
The business, its debtors and their invoices are seeded demo data
(scripts/seed_history.py) — self-owned and consenting, not third
parties. The economics engine, the Stripe integration (live mode, real webhook, real commission),
the voice pipeline and the egress sandbox are real. Nothing here simulates a payment.