{
  "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
          }
        ]
      ]
    }
  }
}