Billable work is recorded throughout the week in a Notion database. Each row includes the client, date, work description, hours and an explicit ready-to-invoice decision.
Every Friday, a scheduled automation turns the eligible rows into client-grouped draft invoices in Xero. It removes repetitive copying while retaining a clear human decision before anything is invoiced or sent.
The problem
Time records and accounting data have different structures. Notion is useful for recording work as it happens, while Xero needs a valid contact, rate, tax treatment, line description and invoice state.
Manually moving the data between them creates several avoidable risks: missed rows, duplicated hours, the wrong client contact, inconsistent descriptions and no reliable link back to the source record.
The weekly workflow
- Query Notion for rows that are marked ready and do not already have an invoice number.
- Validate the client relation, description, date, hours and invoicing state before making any accounting change.
- Fetch the linked client record and hourly rate.
- Group the rows by client, order them by date and calculate the total.
- Match the client to the correct Xero contact using a stored contact identifier where available.
- Create one draft invoice per client in Xero.
- Write the returned invoice number back to every included Notion row.
- Send me a summary of the drafts created, totals and any warnings.
The invoice number closes the loop. A time entry with that reference can be traced to the resulting Xero draft, and the same row will not be selected again on the next weekly run.
Technology used
- Notion database and Notion REST API for time records, client relations, rates, eligibility and invoice references.
- A scheduled Codex automation for the weekly run, validation and controlled decision flow.
- Xero API services for contact verification and draft invoice creation.
- Gmail tooling for the completion or blocker report.
- Structured validation and explicit states rather than asking a model to improvise accounting data.
AI assisted with designing and implementing the system, but the financial path is intentionally deterministic. The automation reads defined fields, applies explicit validation rules and stops when the data is ambiguous.
Safety boundaries
The workflow has several non-negotiable controls:
- It only processes rows I have explicitly marked ready to invoice.
- It creates draft invoices only. It cannot approve or send them.
- It completes all validation before the first Xero write.
- It stops rather than guessing when a client, rate or contact cannot be matched safely.
- It writes an invoice number back only after Xero confirms the draft exists.
- It reports partial failure immediately if a draft is created but the Notion update fails.
This means I remain responsible for deciding what is billable and for reviewing and sending the final invoice.
Decisions I owned
I defined the source fields, eligibility rule, client-matching hierarchy, grouping logic, invoice format, failure behaviour and approval boundary. I also decided that traceability and safe stopping mattered more than making the workflow fully autonomous.
AI helped translate those rules into the automation and assists with operation. It does not decide whether work should be billed, change the hourly rate or authorise an invoice.
What this project demonstrates
This is a live connection between operational data and a financial system. It demonstrates API integration, data validation, scheduled automation, reconciliation, exception handling and human control over a consequential action.
A measured time-saving figure has not yet been published. The defensible outcome is that eligible weekly records can become traceable draft invoices without manually rebuilding each invoice from individual time entries.
What is deliberately not published
This account excludes client names, rates, invoice values, database identifiers, contact identifiers, credentials, internal paths and exact authentication configuration.