ReferenceView as Markdown ↗

Chasing overdue invoices: the tool sequence for AI assistants

In plain terms

The order an assistant follows to remind every late-paying client, what each step answers, and how it tells the difference between reminders sent and reminders that were never staged.

get_attention_digest (invoices_to_chase) or list_invoices → propose_payment_reminders (stages one statement send per overdue client under one batch_id; sends nothing) → read each client's warnings with the person → approve_payment_reminders(batch_id, confirmed: true). A batch where every client was skipped answers code nothing_staged — never report it as sent. One client: propose_send_statement → confirm_pending_operation → approve_pending_operation.

At a glance

Who it's for:
AI assistants working in a Biloh business
What a person approves:
Biloh never moves money: its tools record payments that already happened, and a pay-now link opens the business's own Stripe checkout for its client.
Checked against the code:
2026-11-08

This is the sequence an assistant follows when the person it works for says "chase everyone who owes us". Every reminder is staged first and sent only after the person says yes. Money is integer cents: overdue_cents: 196900 is $1,969.00.

The sequence

StepToolWhat it does
1get_attention_digest or list_invoicesWho is overdue
2propose_payment_remindersStages one reminder per overdue client; returns batch_id. Nothing is sent
3(the person)Reads each client's preview and warnings, and says which to send
4approve_payment_remindersSends the chosen ones, with confirmed: true

Step 1 — who is overdue?

get_attention_digest returns an invoices_to_chase bucket: one item per client with client_id, invoice_numbers, overdue_cents and oldest_days_overdue, plus next_tool. The bucket is filled only when the business has turned on attention.digest.include_money_queues (default off); invoices_to_chase.enabled says which. list_invoices answers who is overdue in every case.

Step 2 — propose_payment_reminders

Required: statement_date (the real send date, YYYY-MM-DD, no default) and cover_message_template (plain text; the platform adds the greeting). The only tokens allowed are {pay_all_link}, {overdue_total} and {client_name}; an unknown token is refused before anything is staged.

Optional: client_ids or exclude_client_ids, min_days_overdue (default 1), bundle_scope (outstanding, the default — only invoices still owing — or none), link_scope (all, the default — one link paying everything outstanding — or overdue, one link per overdue invoice) and subject_template.

It returns batch_id, expires_at and clients[], each with recipients, greeting, subject, cover_message_rendered, pay_all_url, overdue_cents, link_total_cents, attachments, operation_id and warnings. Put every warning to the person before approving — among them: the link also covers invoices not yet due, two part-paid invoices short by the same amount (an old price), or unanswered email from the client.

A client the server holds back is listed under skipped with a code:

codeMeaningWhat to do
nothing_overdueNothing overdue by min_days_overdueNothing
already_sentThis statement was already sentpropose_resend_statement for that client
unmatched_client_creditsA bank deposit looks like their paymentMatch it, or pass acknowledge_unmatched_credits: true
feed_behind_statement_dateThe bank feed stops before statement_dateImport the latest statement, or pass acknowledge_feed_gap: true

The same inputs while the sends are still pending return the same batch_id with idempotent: true and stage nothing new.

Step 4 — approve_payment_reminders

Input: { batch_id, confirmed: true }, and client_ids to send only some. Without confirmed: true it refuses with confirmation_required and sends nothing.

It returns sent[] (client_id, operation_id, sent_at, message_id, attachments) and failed[] (client_id, code, error). A send that has expired comes back with code expired; propose again.

When the batch staged nothing — every client was skipped at step 2 — the answer is code: "nothing_staged", with sent: [], a message saying nothing was sent and why, the skipped list and next_step: "propose_resend_statement". Report it as nothing sent.

One client — the statement send, in three legs

propose_send_statement needs client_id, period_start, period_end and statement_date. It runs the same unmatched-deposit and bank-feed gate, and refuses with already_sent (with a prior_send block) when that statement has gone. Then confirm_pending_operation stages it — nothing is sent — and approve_pending_operation sends it. A resend is different: propose_resend_statement then confirm_pending_operation, which sends.

Where did it go?

On a business whose outbound email is kept rather than delivered — the public demo is one — list_demo_inbox lists every kept email, newest first; pass since to see only yours.

Biloh never moves money: its tools record payments that already happened, and a pay-now link opens the business's own Stripe checkout for its client.

Related

Under the hood

Source files this page is checked against:

  • lib/operations/payment-reminders.ts
  • lib/mcp/tools/finance/propose-payment-reminders.ts
  • lib/mcp/tools/finance/approve-payment-reminders.ts
  • lib/mcp/tools/finance/propose-send-statement.ts
  • lib/operations/attention-digest.ts

Raw markdown: /docs-md/reference/chasing-overdue-invoices-for-agents

Last updated 2026-11-08