# R3 payment reminders - setting it up

Read-date 2026-11-10. Part of the Back-Office Autopilot pack.

## What you need

Nothing to buy, no account, no service. Python 3.10 or newer on your own
machine. One CSV export from whatever already issues your invoices.

Check the interpreter before you trust any number:

```
python3 -c 'import sys; print(sys.version)'
```

## Your export

The program needs five things per invoice. It says which one is missing and
names the column, so a wrong guess about your file is a message rather than a
silently wrong reminder.

| Column | What it holds | Example |
| --- | --- | --- |
| `id` | your invoice number, the one the customer sees | `INV-1002` |
| `customer` | who to address | `Kestrel Ceramics` |
| `email` | where the reply goes, for the draft's header | `orders@kestrel.example` |
| `amount` | what is owed, as your software wrote it | `1,250.00` |
| `due_date` | the day it became payable, `YYYY-MM-DD` | `2026-10-30` |
| `status` | `open`, or `paid` / `void` / `cancelled` | `open` |

Different header names are fine - name them with `--map`:

```
--map id=InvoiceNumber,customer=Client,email=ContactEmail,amount=Total,due_date=Due,status=State
```

## Running it

```
cd recipes/r3
python3 invoice_reminders.py --input samples/invoices_sample.csv \
    --outbox "$PWD/outbox" --as-of 2026-11-16
```

Real invoices, one morning:

```
python3 invoice_reminders.py --input /path/to/open-invoices.csv \
    --outbox "$HOME/invoice-drafts"
```

`--as-of` defaults to today. Name it explicitly (`--as-of 2026-11-16`) when you
want a repeatable run or want to see tomorrow's queue without waiting. It is
refused if it cannot be read as a date, because that one value decides every
"how late" figure in the output; a guess there would put a chase on the wrong
day.

## What it writes

One `.txt` per reminder that is owed, in the outbox you named, numbered so the
order survives sorting: `0004-INV-1001-reminder1.txt`. Each one carries the
invoice number, the customer, the amount exactly as your file wrote it, the due
date, and how many days past due it was on that run.

Nothing is sent. **Nothing can be sent.** There is no mail path in this file to
turn on; see `RECIPE.md` for the guard that makes that a checked fact rather
than a promise. You open each draft, read it against your own records, and send
it from your own mail account - or delete it.

## Costs, and what stops working

Nothing bills. No account to keep open, no key to rotate, no third party that
can change a price or lose your customer list. What can stop working is the
shape of your export: add a column, rename the status values, and the program
tells you which named column it could not find, rather than reminding the wrong
people.

## Its own tests

```
cd recipes/r3
bash smoke.sh                          # the 30-second smoke
python3 -m pytest test_invoice_reminders.py -q   # the full suite
```

Both need no network and no paid service. The suite drives the real command
line, and one test proves the no-send property from the file's own syntax tree
rather than from a comment claiming it.
