Automated Sales Invoicing from Google Sheets to Xero (n8n)
Streamline financial operations by transforming Google Sheet rows into compliant Xero Invoices using n8n's logic nodes.
Tools: Google Sheets → Xero
Platform: n8n
Short Answer
A fully automated workflow that monitors Google Sheets for new entries, validates customer data in Xero, creates or updates contacts as needed, and generates a draft invoice with precise line-item mapping and error logging.
The Problem
Manual entry of sales data from spreadsheets into accounting software is prone to human error, duplicate entries, and delayed billing. Businesses need a way to ensure that once a deal is marked 'Closed' in a sheet, a professional, accurate invoice is generated immediately in Xero.
The Outcome
A fully automated workflow that monitors Google Sheets for new entries, validates customer data in Xero, creates or updates contacts as needed, and generates a draft invoice with precise line-item mapping and error logging.
Step-by-Step Guide
1. **Create Workflow & Credentials**: Open n8n, click 'Create New Workflow'. Set up credentials for Google Sheets (OAuth2) and Xero (OAuth2) via the 'Settings > Credentials' menu.
2. **Configure Google Sheets Trigger**: Add the 'Google Sheets Trigger' node. Select 'On Row Added'. Choose your Spreadsheet and Sheet. Set 'Poll Times' to every 5-15 minutes depending on your needs.
3. **Add Filter (IF Node)**: Insert an 'IF' node to check if the 'Status' column (or similar) equals 'Send to Xero'. This prevents unfinished rows from triggering invoices.
4. **Search for Contact**: Add a 'Xero' node. Set Resource to 'Contact' and Operation to 'Get'. Use an expression to search by email: `{{ $json["Email"] }}`.
5. **Route with Filter Node**: Use another 'IF' node to check if the Xero search returned a result. Check if `{{ $node["Xero"].json["ContactID"] }}` is empty.
6. **Create Contact (Optional Branch)**: On the 'false' path (contact not found), add a Xero node with Resource 'Contact' and Operation 'Create'. Map the Name and Email fields from the original Google Sheet node.
7. **Format Data for Invoice**: Use a 'Set' node or write an expression inside the Xero Invoice node. Convert date strings using `$now` or `{{ $json["Date"].toISODate() }}` to ensure Xero accepts the format.
8. **Create Invoice**: Add a Xero node. Set Resource to 'Invoice', Operation to 'Create'. Link the 'Contact ID' from the previous steps. Map 'Line Items' using an expression that references your Qty and Price columns.
9. **Update Sheet with Reference**: Add a Google Sheets node (Operation: Update). Use the Row ID from the trigger and write the Xero 'InvoiceNumber' back to a 'Xero Status' column for tracking.
10. **Implement Error Handling**: Create a separate 'Error Trigger' node connected to a 'Sticky Note' or 'Slack' node to alert you if a Xero API limit is hit or if data is malformed.
Data Mapping
| Google Sheets Field | Xero Field | Data Type | n8n Expression / Default |
| :--- | :--- | :--- | :--- |
| Customer Email | Contact Email | String | `{{ $json["Email"] }}` |
| Date | Date | ISO8601 | `{{ $json["Date"] || $now }}` |
| Item Desc | Line Items > Description | String | `{{ $json["Description"] }}` |
| Quantity | Line Items > Quantity | Number | `{{ parseFloat($json["Qty"]) }}` |
| Unit Price | Line Items > UnitAmount | Number | `{{ parseFloat($json["Price"]) }}` |
| Account Code | Line Items > AccountCode | String | `200` (Default Sales Code) |
| Status | Status | String | `DRAFT` (Recommended) |
Gotchas & Failure Modes
• **Xero Rate Limits**: Xero has a limit of 5,000 API calls per day. If processing bulk rows, use the 'Split in Batches' node (set to 40-50) to prevent hitting concurrency limits.
• **Date Formats**: Xero is strict about ISO 8601. Use `{{ DateTime.fromFormat($json.Date, 'dd/MM/yyyy').toISODate() }}` if your sheet uses non-standard date formats.
• **Decimal Accuracy**: Ensure your Sheet columns are formatted as numbers. Use `parseFloat()` in n8n expressions to avoid 'String to Number' schema errors in Xero.
• **OAuth Refresh**: If self-hosting n8n, ensure your `WEBHOOK_URL` is configured correctly in the `.env` file, otherwise Xero OAuth tokens may fail to refresh after 30 minutes.
Verification Checklist
- [ ] **Test Trigger**: Click 'Execute Node' on Google Sheets and manually add a row to ensure n8n picks up the data.
- [ ] **Validate Reference**: Ensure the 'Update Row' step correctly writes back the Xero Invoice ID to your sheet.
- [ ] **Check Contact Logic**: Run a test with an existing email and a new email to verify no duplicate contacts are created.
- [ ] **Review in Xero UI**: Log into the Xero Sandbox or Demo Company and confirm the 'Draft Invoice' appears with correct line items and tax codes.
- [ ] **Active Workflow**: Ensure the workflow is toggled to 'Active' in the top right corner of the n8n UI.
Ready to Automate?
Build this automation with n8n in minutes.