See llms.txt for all machine-readable content.
This workflow runs on the 1st of every month, reads open invoices from Google Sheets, builds one consolidated account statement per customer with ageing totals, emails it via Gmail, and appends a send log back to Google Sheets to prevent duplicate sends and retry failures.
invoices tab and reads prior send history from the statement_log tab.sent for the current statement period, generates an HTML statement with invoice lines and ageing buckets, and emails one message per customer via Gmail.sent when a Gmail message ID is returned, otherwise failed) and appends the results to the statement_log sheet.sheetUrl, business name, your name, reply-to email, currency symbol, and optional minBalanceToSend in the Settings step.invoices sheet with headers customer_name, customer_email, invoice_id, invoice_date, due_date, amount, status, and store due_date as plain text in YYYY-MM-DD format.statement_log sheet with headers sent_at, customer_name, customer_email, statement_period, invoice_count, total_due, skipped_rows, send_status, message_id, thread_id so the workflow can prevent duplicates and retry failed sends.minBalanceToSend in Settings to hold statements below a chosen amount.Honest note: live-tested end to end on self-hosted n8n 2.32.6 (standard nodes only - no Cloud-specific features). Real executions covered consolidation (six invoices into one email), every ageing boundary (30/31/90/91 days), the resend guard across repeated runs, a failed send that did not stop the other customers and went out after the address was fixed, the empty-month quiet run, timezone-driven ageing, and a real schedule activation. n8n Cloud was not part of the test environment.
Format notes, honestly: slash dates are read US-style (month/day/year) - the reliable path is a plain-text due_date column written as YYYY-MM-DD. Text amounts understand 1,250.00 / $1,250.00 / 9.500,50 / 2,5. Zero and negative amounts (credit notes) are never billed and are counted instead - this template lists open invoices, it does not offset credits. skipped_rows is a whole-run count stamped on every log row, not a per-customer figure. And one honest edge: if a run is interrupted after an email goes out but before its log row is written, that customer could be emailed again on a re-run - the guard is only as strong as the log.