1. The electricity bill both of you assumed the other paid
The bill arrives in one inbox. Each of you half-remembers seeing it. Neither pays it, because each assumes the other did — and you find out from the disconnection notice. The recurring ones are worse: rent, broadband, the society maintenance, the two subscriptions nobody can remember signing up for.
Trigger: Email / Webhook (biller or bank statement notification)
How the workflow runs
- Trigger
The bill lands. An Email trigger on either partner's forwarded biller mail, or a Webhook from the utility/bank portal — no first-party connector for these, so they arrive inbound and are read back through a Tool (MCP) node.
- Agent
Read it into a shared ledger line. An Agent node extracts biller, amount, due date and account reference into one structured line, marking anything unclear as needing a look at the original rather than guessing an amount.
- Condition
Already handled?. A Condition node checks the shared ledger for this billing period — if either partner already marked it paid, the workflow stops. This one branch is what kills the 'I thought you did it' failure mode.
- Output
Both of you, same message. An Output node posts one line to your shared WhatsApp or Telegram thread: what is due, how much, by when, and who is down to handle it this cycle. Identical message, both people.
- Approval
Paying is an explicit act. An Approval node holds the payment. A Razorpay/UPI request shows payee, amount and due date, and one of you approves it deliberately — autopay-by-agent is not on offer here, because money moving without a human is exactly the wrong default.
- Output
Mark it, once, for both. Once paid, the ledger line is closed for the period and the thread gets one confirmation — so neither of you pays it twice a week later.
Channels & connectors
- Webhook (biller / bank portal — inbound)
- Telegram
- Approval / HITL
- Razorpay (UPI)
Outcome
Every bill becomes one shared ledger line both of you can see, deduplicated against what is already paid, with the payment itself always an explicit human approval.
Why it helps
The mechanism is removing the assumption. The 'I thought you paid it' failure comes from two people holding a private, unsynchronised belief about the same bill; one shared line with a visible state replaces both beliefs with a fact either of you can check in three seconds.
Build spec
1 agent — 1 bill-reading agent. The already-paid dedupe and the payment approval are workflow nodes — deliberately not agent decisions. · Pattern: Extract → dedupe against the shared ledger → one shared message → Approval gate on every rupee
System prompt (paste-ready)
You read household bills for two people who share them. From ONE bill or biller notification, extract {biller, account_ref, amount, currency, due_date, period, source}. Use ONLY what the document states — never infer an amount, a due date or an account. If anything is unreadable, set it null, flag needs_original=true, and link the source. You never pay, never schedule a payment, and never describe a bill as settled: payment happens only after a person approves it. If this billing period already shows as paid in the ledger, return duplicate=true and stop.Agent roles & model tiers
Bill reader (per bill) — Economy tier — short structured extraction, runs on every biller mail
Extract one bill into strict JSON with nulls for anything unclear and needs_original=true. Never infer an amount or a due date. Never treat a bill as paid, and stop immediately if the period is already settled in the ledger.
MCP connectors
- Utility / biller portals and bank statement mail — inbound via Webhook and Email, read back via a Tool (MCP) node (no first-party connectors)
- The couple's shared calendar — via a Tool (MCP) node, for the due date
- WhatsApp / Telegram — the shared thread, as the single delivery surface
Built-in tools
- knowledge_search (the couple's own list of recurring bills, account references and providers)
- http_request (read the biller notice; write and close the shared ledger line)
- user-defined tool `bill_turn` (SpEL): input.lastPaidBy == 'A' ? 'B' : 'A' — a tenant-authored SpEL formula over the tool's JSON input that names whose turn it is to handle this cycle. Sandboxed by construction: no method calls, no constructors, no bean or type references, no loop in the grammar — a data-transform/formula tool, not a scripting engine. The benefit: the couple encodes their own arrangement once (alternate, or one person takes utilities and the other rent, or split by whose account it debits) and never re-litigates it at the due date.
Guardrails
- Both partners know this workflow exists and opted into it. The same message goes to both, in a thread both are in — there is no version of this that reports to one person about the other.
- No surveillance whatsoever: it reads bills that were forwarded into the workflow on purpose. No bank-login scraping of a partner's personal accounts, no location tracking, no reading anyone's messages, no inference about anyone's spending habits or whereabouts.
- Shared by agreement — either partner can opt out and the workflow stops for them; if either stops it, shared bill reading ends, and neither is left with continued access to the other's forwarded mail.
- Money never moves without an explicit human approval showing payee, amount and due date. Autopay-by-agent is deliberately not offered.
- Idempotent per biller + billing period, so a resent notice or a forwarded duplicate can never trigger a second payment.
- Amounts are never inferred — an unreadable bill says 'check the original' and links it.
Cost strategy
Bills are frequent and short, so the reader belongs on the economy tier with a hard cap; there is no synthesis step to pay for. Configure a fallback model — a bill that still gets surfaced on a plainer model beats a missed due date. The expensive resource here is human attention, and the dedupe branch is what protects it.
Output & delivery
The bill arrives by Email or Webhook → an economy-tier agent extracts one ledger line → the Condition node stops it dead if the period is already paid → one identical message goes to the shared thread with whose turn it is → a Razorpay/UPI request is held at the Approval node until a person approves payee and amount → the ledger line closes for the period and both of you see the confirmation.