AutomationFlowsFinance › Count Xero Invoices, Bills, and Credit Notes with a Voided/deleted Filter

Count Xero Invoices, Bills, and Credit Notes with a Voided/deleted Filter

BySobek @maagic on n8n.io

This workflow runs on manual execution to count Xero Accounting records: sales invoices, bills, and sales/bill credit notes; with an optional filter to exclude VOIDED and DELETED statuses, returning a simple tally per record type. Starts when you manually execute the workflow in…

Event trigger★★★★☆ complexity16 nodesHTTP Request
Finance Trigger: Event Nodes: 16 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #16467 — we link there as the canonical source.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "BmlA-E_n5VzGGtf9vQ9_A",
  "name": "Count Xero records (invoices, bills & credit notes) with voided/deleted filter",
  "tags": [],
  "nodes": [
    {
      "id": "86bd961c-c959-447c-bd8c-d57a3afba1ed",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -448,
        416
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "4f422168-8870-4c41-8bb2-4d127e7a4fbe",
      "name": "Get Connections",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -128,
        416
      ],
      "parameters": {
        "url": "https://api.xero.com/connections",
        "options": {},
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "xeroOAuth2Api"
      },
      "credentials": {
        "xeroOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "84f24a97-39f6-4767-a975-b36f8bcc0ea5",
      "name": "Count Sales Invoices",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        176,
        128
      ],
      "parameters": {
        "url": "https://api.xero.com/api.xro/2.0/Invoices",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          },
          "pagination": {
            "pagination": {
              "parameters": {
                "parameters": [
                  {
                    "name": "page",
                    "value": "={{ $pageCount + 1 }}"
                  }
                ]
              },
              "completeExpression": "={{ ($response.body.Invoices || []).length < 100 }}",
              "paginationCompleteWhen": "other"
            }
          }
        },
        "sendQuery": true,
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "where",
              "value": "={{ 'Type==\"ACCREC\"' + ($('Count Options').first().json.includeVoidedDeleted ? '' : ' && Status!=\"VOIDED\" && Status!=\"DELETED\"') }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Xero-Tenant-Id",
              "value": "={{ $json.tenantId }}"
            },
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "xeroOAuth2Api"
      },
      "credentials": {
        "xeroOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "b7032209-1995-40cd-aac9-40045abd6a6a",
      "name": "Tally Sales Invoices",
      "type": "n8n-nodes-base.code",
      "position": [
        400,
        128
      ],
      "parameters": {
        "jsCode": "const LABEL='Sales invoices (ACCREC)';\nlet count=0;\nfor (const it of $input.all()) {\n  const b = it.json || {};\n  if (Array.isArray(b.Invoices)) count += b.Invoices.length;\n  else if (Array.isArray(b.CreditNotes)) count += b.CreditNotes.length;\n  else count += 1;\n}\nreturn [{ json: { type: LABEL, count } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "e867e2bd-e5f5-431f-8c70-6552c6f27a8a",
      "name": "Count Bills",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        176,
        320
      ],
      "parameters": {
        "url": "https://api.xero.com/api.xro/2.0/Invoices",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          },
          "pagination": {
            "pagination": {
              "parameters": {
                "parameters": [
                  {
                    "name": "page",
                    "value": "={{ $pageCount + 1 }}"
                  }
                ]
              },
              "completeExpression": "={{ ($response.body.Invoices || []).length < 100 }}",
              "paginationCompleteWhen": "other"
            }
          }
        },
        "sendQuery": true,
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "where",
              "value": "={{ 'Type==\"ACCPAY\"' + ($('Count Options').first().json.includeVoidedDeleted ? '' : ' && Status!=\"VOIDED\" && Status!=\"DELETED\"') }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Xero-Tenant-Id",
              "value": "={{ $json.tenantId }}"
            },
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "xeroOAuth2Api"
      },
      "credentials": {
        "xeroOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "4939b520-3cff-4686-9821-d867f967a0ef",
      "name": "Tally Bills",
      "type": "n8n-nodes-base.code",
      "position": [
        400,
        320
      ],
      "parameters": {
        "jsCode": "const LABEL='Bills (ACCPAY)';\nlet count=0;\nfor (const it of $input.all()) {\n  const b = it.json || {};\n  if (Array.isArray(b.Invoices)) count += b.Invoices.length;\n  else if (Array.isArray(b.CreditNotes)) count += b.CreditNotes.length;\n  else count += 1;\n}\nreturn [{ json: { type: LABEL, count } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "31fd12fc-81f0-4aa6-8dcb-9ee9e2ba3a3e",
      "name": "Count Sales Credit Notes",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        176,
        512
      ],
      "parameters": {
        "url": "https://api.xero.com/api.xro/2.0/CreditNotes",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          },
          "pagination": {
            "pagination": {
              "parameters": {
                "parameters": [
                  {
                    "name": "page",
                    "value": "={{ $pageCount + 1 }}"
                  }
                ]
              },
              "completeExpression": "={{ ($response.body.CreditNotes || []).length < 100 }}",
              "paginationCompleteWhen": "other"
            }
          }
        },
        "sendQuery": true,
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "where",
              "value": "={{ 'Type==\"ACCRECCREDIT\"' + ($('Count Options').first().json.includeVoidedDeleted ? '' : ' && Status!=\"VOIDED\" && Status!=\"DELETED\"') }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Xero-Tenant-Id",
              "value": "={{ $json.tenantId }}"
            },
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "xeroOAuth2Api"
      },
      "credentials": {
        "xeroOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "38dfd807-2583-416e-acbb-cf84fbac2ee6",
      "name": "Tally Sales Credit Notes",
      "type": "n8n-nodes-base.code",
      "position": [
        400,
        512
      ],
      "parameters": {
        "jsCode": "const LABEL='Sales credit notes (ACCRECCREDIT)';\nlet count=0;\nfor (const it of $input.all()) {\n  const b = it.json || {};\n  if (Array.isArray(b.Invoices)) count += b.Invoices.length;\n  else if (Array.isArray(b.CreditNotes)) count += b.CreditNotes.length;\n  else count += 1;\n}\nreturn [{ json: { type: LABEL, count } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "0438111f-1244-459f-9c2a-9053cf169178",
      "name": "Count Bill Credit Notes",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        176,
        704
      ],
      "parameters": {
        "url": "https://api.xero.com/api.xro/2.0/CreditNotes",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          },
          "pagination": {
            "pagination": {
              "parameters": {
                "parameters": [
                  {
                    "name": "page",
                    "value": "={{ $pageCount + 1 }}"
                  }
                ]
              },
              "completeExpression": "={{ ($response.body.CreditNotes || []).length < 100 }}",
              "paginationCompleteWhen": "other"
            }
          }
        },
        "sendQuery": true,
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "where",
              "value": "={{ 'Type==\"ACCPAYCREDIT\"' + ($('Count Options').first().json.includeVoidedDeleted ? '' : ' && Status!=\"VOIDED\" && Status!=\"DELETED\"') }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Xero-Tenant-Id",
              "value": "={{ $json.tenantId }}"
            },
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "xeroOAuth2Api"
      },
      "credentials": {
        "xeroOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "66cd3d2b-6b98-417a-9501-45b9365b8614",
      "name": "Tally Bill Credit Notes",
      "type": "n8n-nodes-base.code",
      "position": [
        400,
        704
      ],
      "parameters": {
        "jsCode": "const LABEL='Bill credit notes (ACCPAYCREDIT)';\nlet count=0;\nfor (const it of $input.all()) {\n  const b = it.json || {};\n  if (Array.isArray(b.Invoices)) count += b.Invoices.length;\n  else if (Array.isArray(b.CreditNotes)) count += b.CreditNotes.length;\n  else count += 1;\n}\nreturn [{ json: { type: LABEL, count } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "14afd870-81f9-4172-8f5a-e2290f29710b",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        672,
        384
      ],
      "parameters": {
        "numberInputs": 4
      },
      "typeVersion": 3.2
    },
    {
      "id": "b5d8acc4-5ee6-44c0-a455-db42475db8be",
      "name": "Count Options",
      "type": "n8n-nodes-base.set",
      "position": [
        -288,
        416
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "28bcec27-98e3-4b4e-8222-71d33f74d71d",
              "name": "includeVoidedDeleted",
              "type": "boolean",
              "value": false
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "7c9aa0c7-4f98-4d29-8b5a-6af7c3f0f277",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -960,
        -48
      ],
      "parameters": {
        "width": 420,
        "height": 912,
        "content": "## Count Xero records with a voided/deleted filter\n\n**Who it's for**\nAnyone who needs an accurate count of how many sales invoices, bills and credit notes exist in a Xero organisation - e.g. for data migrations, reconciliations or pre-archiving audits.\n\n**What it does**\nQueries the Xero Accounting API and returns a tally of four record types:\n- Sales invoices (ACCREC)\n- Bills (ACCPAY)\n- Sales credit notes (ACCRECCREDIT)\n- Bill credit notes (ACCPAYCREDIT)\n\n**How it works**\n1. **Count Options** sets a single flag, `includeVoidedDeleted`.\n2. **Get Connections** retrieves the Xero tenant ID.\n3. Four parallel branches page through each record type (100 per page) and a Code node tallies the totals.\n4. **Merge** combines the four counts into one result.\n\n**Setup**\n1. Add a **Xero OAuth2** credential and select it on the 5 HTTP Request nodes.\n2. (Optional) In **Count Options**, set `includeVoidedDeleted` to `true` to include VOIDED/DELETED records.\n3. Click **Execute workflow**.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "4b97ab86-88ba-471a-8821-5afc81066819",
      "name": "Section - Setup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -512,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 540,
        "height": 912,
        "content": "## 1. Trigger & options\n\nSet `includeVoidedDeleted`:\n- **false** (default) - excludes VOIDED & DELETED records\n- **true** - counts everything\n\n`Get Connections` resolves the Xero **tenant ID** used by every count node.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "36afd149-388a-404c-94d7-3581a28dadae",
      "name": "Section - Counting",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        48,
        -48
      ],
      "parameters": {
        "color": 5,
        "width": 516,
        "height": 920,
        "content": "## 2. Count each record type\n\nEach branch calls the Xero API with a `where` filter, paginates 100 records per page, then a Code node sums the totals.\n\u26a0\ufe0f **Multi-tenant note:** if the credential has access to more than one Xero org, `Get Connections` returns multiple tenants and counts are summed across all of them. Filter to a single tenant if you need per-org figures.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "270fac62-b7e3-4d1a-a657-7f5debe138b3",
      "name": "Section - Merge",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        592,
        -48
      ],
      "parameters": {
        "color": 6,
        "width": 260,
        "height": 912,
        "content": "## 3. Combine results\n\nMerges the four tallies into one output:\n`{ type, count }` per record type.\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "4f4f3afb-0026-4199-bd57-f2eea216f8d2",
  "connections": {
    "Count Bills": {
      "main": [
        [
          {
            "node": "Tally Bills",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tally Bills": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Count Options": {
      "main": [
        [
          {
            "node": "Get Connections",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Connections": {
      "main": [
        [
          {
            "node": "Count Sales Invoices",
            "type": "main",
            "index": 0
          },
          {
            "node": "Count Bills",
            "type": "main",
            "index": 0
          },
          {
            "node": "Count Sales Credit Notes",
            "type": "main",
            "index": 0
          },
          {
            "node": "Count Bill Credit Notes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Count Sales Invoices": {
      "main": [
        [
          {
            "node": "Tally Sales Invoices",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tally Sales Invoices": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Count Bill Credit Notes": {
      "main": [
        [
          {
            "node": "Tally Bill Credit Notes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tally Bill Credit Notes": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "Count Sales Credit Notes": {
      "main": [
        [
          {
            "node": "Tally Sales Credit Notes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tally Sales Credit Notes": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Count Options",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow runs on manual execution to count Xero Accounting records: sales invoices, bills, and sales/bill credit notes; with an optional filter to exclude VOIDED and DELETED statuses, returning a simple tally per record type. Starts when you manually execute the workflow in…

Source: https://n8n.io/workflows/16467/ — original creator credit. Request a take-down →

More Finance workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Finance

This is the ultimate sales-to-cash automation. When a deal in Airtable is marked "Approved for Invoicing," this workflow intelligently syncs customer data across QuickBooks and Stripe (creating them i

Airtable Trigger, Stripe, QuickBooks +2
Finance

This workflow triggers on successful Stripe payment events, generates and finalizes invoices, then formats and sends invoice details via Gmail, stores invoice PDFs in Google Drive, and notifies an adm

Stripe, Gmail, HTTP Request +3
Finance

How It Works Trigger: Watches for new emails in Gmail with PDF/image attachments. OCR: Sends the attachment to OCR.space API (https://ocr.space/OCRAPI) to extract invoice text. Parsing: Extracts key f

Gmail Trigger, Google Sheets, Slack +3
Finance

Automated Stripe Payment to QuickBooks Sales Receipt

HTTP Request, Stripe, Stripe Trigger +1
Finance

This workflow automatically generates and delivers professional invoice PDFs whenever a Stripe checkout session completes. It fetches the line items from Stripe, formats them into a clean invoice with

Stripe Trigger, HTTP Request, N8N Nodes Templatefox +2