{
  "name": "07 · Extra data on the invoice PDF (ZUGFeRD)",
  "nodes": [
    {
      "parameters": {},
      "id": "4bb745a7-d3bf-4595-8f4c-25945551d45b",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -160,
        300
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "c1",
              "name": "spreadsheetId",
              "value": "",
              "type": "string"
            },
            {
              "id": "c2",
              "name": "sheetBilling",
              "value": "Billing",
              "type": "string"
            },
            {
              "id": "c3",
              "name": "sheetItems",
              "value": "LineItems",
              "type": "string"
            },
            {
              "id": "c4",
              "name": "sheetCustomers",
              "value": "Customers",
              "type": "string"
            },
            {
              "id": "c5",
              "name": "countryCode",
              "value": "DE",
              "type": "string"
            },
            {
              "id": "c6",
              "name": "format",
              "value": "zugferd",
              "type": "string"
            },
            {
              "id": "c7",
              "name": "currency",
              "value": "EUR",
              "type": "string"
            },
            {
              "id": "c8",
              "name": "dueDays",
              "value": "30",
              "type": "string"
            },
            {
              "id": "c9",
              "name": "outputDir",
              "value": "/home/node/e-invoice/out",
              "type": "string"
            },
            {
              "id": "c10",
              "name": "defaultTemplateId",
              "value": "",
              "type": "string"
            },
            {
              "id": "c11",
              "name": "sellerName",
              "value": "Musterfirma GmbH",
              "type": "string"
            },
            {
              "id": "c12",
              "name": "sellerVatId",
              "value": "DE123456789",
              "type": "string"
            },
            {
              "id": "c13",
              "name": "sellerStreet",
              "value": "Musterstraße 1",
              "type": "string"
            },
            {
              "id": "c14",
              "name": "sellerPostalCode",
              "value": "60311",
              "type": "string"
            },
            {
              "id": "c15",
              "name": "sellerCity",
              "value": "Musterstadt",
              "type": "string"
            },
            {
              "id": "c16",
              "name": "sellerCountryCode",
              "value": "DE",
              "type": "string"
            },
            {
              "id": "c17",
              "name": "sellerEmail",
              "value": "rechnung@musterfirma.de",
              "type": "string"
            },
            {
              "id": "c18",
              "name": "sellerContactName",
              "value": "Buchhaltung",
              "type": "string"
            },
            {
              "id": "c18b",
              "name": "sellerPhone",
              "value": "+49 69 1234567",
              "type": "string"
            },
            {
              "id": "c19",
              "name": "sellerIban",
              "value": "DE02120300000000202051",
              "type": "string"
            },
            {
              "id": "c20",
              "name": "sellerBic",
              "value": "BYLADEM1001",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "7e1bedd4-cab5-4b93-bad5-fbd26d30b078",
      "name": "Set Config",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        60,
        300
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "={{ $('Set Config').first().json.spreadsheetId }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "={{ $('Set Config').first().json.sheetBilling }}",
          "mode": "name"
        },
        "options": {}
      },
      "id": "ee375190-dcb4-4eb3-88eb-0761e6d6b8ac",
      "name": "Read Billing Rows",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        280,
        300
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "={{ $('Set Config').first().json.spreadsheetId }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "={{ $('Set Config').first().json.sheetCustomers }}",
          "mode": "name"
        },
        "options": {}
      },
      "id": "ccd02d97-fb31-40b6-b86c-6c1ec257115c",
      "name": "Read Master Data",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        500,
        300
      ],
      "executeOnce": true
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "={{ $('Set Config').first().json.spreadsheetId }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "={{ $('Set Config').first().json.sheetItems }}",
          "mode": "name"
        },
        "options": {}
      },
      "id": "060c4377-d9a1-4f3f-840c-27a936d73abd",
      "name": "Read Line Items",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        720,
        300
      ],
      "executeOnce": true
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const cfg = $('Set Config').first().json;\n\n// --- Stammdaten: Käufer -> Datensatz -------------------------------------\nconst customers = {};\nfor (const c of $('Read Master Data').all()) {\n  const r = c.json;\n  if (r.buyerKey) customers[String(r.buyerKey).trim()] = r;\n}\n\n// --- Positionen nach Abrechnung gruppieren -------------------------------\nconst itemsByBilling = {};\nfor (const it of $('Read Line Items').all()) {\n  const r = it.json;\n  const key = String(r.billingId || '').trim();\n  if (!key) continue;\n  (itemsByBilling[key] ||= []).push(r);\n}\n\nconst out = [];\n\nfor (const row of $('Read Billing Rows').all().map((i) => i.json)) {\n  if (String(row.status || '').toLowerCase() !== 'ready') continue;\n\n  const billingId = String(row.billingId || '').trim();\n  const buyerKey = String(row.buyerKey || '').trim();\n  const customer = customers[buyerKey];\n\n  // Käufer unbekannt -> Fehlerpfad. Weder Mailadresse noch Käuferreferenz\n  // stehen auf der Abrechnungszeile; ohne Stammdatensatz geht es nicht weiter.\n  if (!customer) {\n    out.push({ json: { billingId, ok: false, reason: `Buyer \"${buyerKey}\" not found in master data` } });\n    continue;\n  }\n\n  const rows = itemsByBilling[billingId] || [];\n  if (rows.length === 0) {\n    out.push({ json: { billingId, ok: false, reason: 'No line items for this billing row' } });\n    continue;\n  }\n\n  // --- Positionen und Summen NEU rechnen, nie aus der Tabelle übernehmen ---\n  let subtotal = 0, taxTotal = 0;\n  const taxByRate = {};\n  const items = rows.map((r, i) => {\n    const quantity = parseFloat(r.quantity ?? 1);\n    const unitPrice = parseFloat(r.unitPrice ?? 0);\n    const taxRate = parseFloat(r.taxRate ?? 19);\n    const netAmount = Math.round(quantity * unitPrice * 100) / 100;\n    const taxAmount = Math.round(netAmount * taxRate) / 100;\n    subtotal += netAmount;\n    taxTotal += taxAmount;\n    taxByRate[taxRate] = (taxByRate[taxRate] || 0) + netAmount;\n    return {\n      position: i + 1,\n      description: String(r.description || ''),\n      quantity,\n      unit: String(r.unit || 'C62'),\n      unitPrice,\n      taxRate,\n      netAmount,\n      taxAmount,\n      grossAmount: Math.round((netAmount + taxAmount) * 100) / 100,\n    };\n  });\n\n  subtotal = Math.round(subtotal * 100) / 100;\n  taxTotal = Math.round(taxTotal * 100) / 100;\n  const total = Math.round((subtotal + taxTotal) * 100) / 100;\n\n  // Gegenprobe gegen die Tabelle, Toleranz 2 Cent.\n  const expected = parseFloat(row.grossTotal ?? total);\n  if (Math.abs(expected - total) > 0.02) {\n    out.push({ json: { billingId, ok: false,\n      reason: `Total mismatch: sheet ${expected.toFixed(2)}, computed ${total.toFixed(2)}` } });\n    continue;\n  }\n\n  const taxSummary = Object.entries(taxByRate).map(([rate, net]) => {\n    const netAmount = Math.round(net * 100) / 100;\n    return { taxRate: parseFloat(rate), netAmount,\n             taxAmount: Math.round(netAmount * parseFloat(rate)) / 100 };\n  });\n\n  // --- Normfelder ---------------------------------------------------------\n  // Die Rechnungsnummer wird NICHT hier vergeben. Zwei parallele Läufe würden\n  // denselben Höchststand lesen und dieselbe Nummer schreiben; §14 UStG\n  // verlangt eine einmalig vergebene Nummer. Sie kommt aus dem System, das\n  // sie transaktional vergibt, und steht in der Tabelle.\n  const invoice = {\n    invoiceNumber: String(row.invoiceNumber || ''),\n    type: 'invoice',\n    issueDate: String(row.issueDate || ''),\n    dueDate: String(row.dueDate || row.issueDate || ''),\n    currency: cfg.currency || 'EUR',\n    countrySpecific: {\n      countryCode: cfg.countryCode || 'DE',\n      // Was der Empfänger maschinell braucht, gehört in die Normfelder.\n      buyerReference: String(customer.buyerReference || row.invoiceNumber || 'N/A'),\n    },\n    seller: {\n      name: cfg.sellerName, vatId: cfg.sellerVatId,\n      street: cfg.sellerStreet, postalCode: cfg.sellerPostalCode,\n      city: cfg.sellerCity, countryCode: cfg.sellerCountryCode || 'DE',\n      email: cfg.sellerEmail,\n      contact: { name: cfg.sellerContactName, email: cfg.sellerEmail, phone: cfg.sellerPhone },\n      bankAccount: { iban: cfg.sellerIban, bic: cfg.sellerBic, accountHolder: cfg.sellerName },\n    },\n    buyer: {\n      name: String(customer.name || ''), vatId: String(customer.vatId || ''),\n      street: String(customer.street || ''), postalCode: String(customer.postalCode || ''),\n      city: String(customer.city || ''), countryCode: String(customer.countryCode || 'DE'),\n      email: String(customer.email || ''),\n    },\n    paymentMethods: [{ type: 'bank_transfer' }],\n    paymentTerms: { dueDays: parseInt(cfg.dueDays || 30, 10) },\n    items, subtotal, total, taxSummary,\n  };\n\n  // --- Zusatzobjekt: nur für die Dokumentebene ----------------------------\n  // Wird für die XML-Erzeugung nicht ausgewertet, steht im PDF-Template aber\n  // als Platzhalter zur Verfügung: {{transport.vessel}}, {{transport.eta?}}\n  const transport = {};\n  for (const [key, col] of Object.entries({\n    vessel: 'vessel', voyage: 'voyage', billOfLading: 'billOfLading',\n    portOfLoading: 'portOfLoading', portOfDischarge: 'portOfDischarge',\n    eta: 'eta', packages: 'packages', weightKg: 'weightKg',\n  })) {\n    if (row[col] !== undefined && row[col] !== '') transport[key] = String(row[col]);\n  }\n  if (Object.keys(transport).length > 0) invoice.transport = transport;\n\n  out.push({ json: {\n    billingId, ok: true, invoice,\n    invoiceNumber: invoice.invoiceNumber,\n    buyerEmail: invoice.buyer.email,\n    templateId: String(customer.templateId || cfg.defaultTemplateId || ''),\n    targetPath: `${cfg.outputDir}/${invoice.invoiceNumber}.pdf`,\n  }});\n}\n\nreturn out;"
      },
      "id": "c35a3692-6507-434b-8231-26791abfbf8a",
      "name": "Build Invoice",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        940,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "ok",
              "leftValue": "={{ $json.ok }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "114f3351-aa40-48e9-90ca-4dfd30ad2369",
      "name": "Buildable?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1160,
        300
      ]
    },
    {
      "parameters": {
        "operation": "validate",
        "countryCode": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set Config').first().json.countryCode }}"
        },
        "invoiceData": "={{ JSON.stringify($json.invoice) }}",
        "options": {
          "failOnErrors": false,
          "failOnWarnings": false
        }
      },
      "id": "7a9bb4d0-9600-4ef1-88a0-74667b767c21",
      "name": "invoice-api.xhub: Validate",
      "type": "n8n-nodes-invoice-api-xhub.invoiceXhub",
      "typeVersion": 1,
      "position": [
        1380,
        200
      ],
      "credentials": {
        "invoiceXhubApi": {
          "id": "",
          "name": "Invoice-api.xhub Account"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "valid",
              "leftValue": "={{ $json.valid }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "e539bc1d-b82d-4648-921e-3fa441183a83",
      "name": "Valid?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1600,
        200
      ]
    },
    {
      "parameters": {
        "operation": "generate",
        "countryCode": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set Config').first().json.countryCode }}"
        },
        "format": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set Config').first().json.format }}"
        },
        "invoiceData": "={{ JSON.stringify($('Build Invoice').item.json.invoice) }}",
        "options": {
          "outputBinary": true,
          "binaryPropertyName": "data",
          "includeWarnings": true,
          "templateId": "={{ $('Build Invoice').item.json.templateId }}"
        }
      },
      "id": "111b32fe-24e1-40db-a2ff-67a002d46c54",
      "name": "invoice-api.xhub: Generate",
      "type": "n8n-nodes-invoice-api-xhub.invoiceXhub",
      "typeVersion": 1,
      "position": [
        1820,
        120
      ],
      "credentials": {
        "invoiceXhubApi": {
          "id": "",
          "name": "Invoice-api.xhub Account"
        }
      }
    },
    {
      "parameters": {
        "operation": "write",
        "fileName": "={{ $('Build Invoice').item.json.targetPath }}",
        "dataPropertyName": "data",
        "options": {}
      },
      "id": "30e339cd-dbb2-46db-aea6-f4724377b36f",
      "name": "Store E-Invoice",
      "type": "n8n-nodes-base.readWriteFile",
      "typeVersion": 1,
      "position": [
        2040,
        120
      ]
    },
    {
      "parameters": {
        "sendTo": "={{ $('Build Invoice').item.json.buyerEmail }}",
        "subject": "=Rechnung {{ $('Build Invoice').item.json.invoiceNumber }}",
        "message": "=Guten Tag,\n\nanbei die Rechnung {{ $('Build Invoice').item.json.invoiceNumber }} als E-Rechnung.\n\nMit freundlichen Grüßen",
        "options": {
          "attachmentsUi": {
            "attachmentsBinary": [
              {
                "property": "data"
              }
            ]
          }
        }
      },
      "id": "6aecb6eb-4911-4ee8-8561-60c13d723ec4",
      "name": "Send E-Invoice",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        2260,
        120
      ],
      "credentials": {
        "gmailOAuth2": {
          "id": "",
          "name": "Gmail account"
        }
      }
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "={{ $('Set Config').first().json.spreadsheetId }}",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "={{ $('Set Config').first().json.sheetBilling }}",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "billingId": "={{ $('Build Invoice').item.json.billingId }}",
            "status": "sent",
            "sentAt": "={{ new Date().toISOString() }}"
          },
          "matchingColumns": [
            "billingId"
          ],
          "schema": [
            {
              "id": "billingId",
              "displayName": "billingId",
              "required": false,
              "defaultMatch": true,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "sentAt",
              "displayName": "sentAt",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            }
          ]
        },
        "options": {}
      },
      "id": "49e0a281-6ea8-4e45-a80e-05686cab5d53",
      "name": "Update Status",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        2480,
        120
      ]
    },
    {
      "parameters": {},
      "id": "b432a8fc-9089-4389-ae21-d6a6605b5dfd",
      "name": "Not Buildable",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        1380,
        420
      ]
    },
    {
      "parameters": {},
      "id": "2be024fb-4af7-4d54-8309-86a358156e8a",
      "name": "Not Valid",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        1820,
        320
      ]
    },
    {
      "parameters": {
        "content": "## Shipment, site or project data on the invoice document\n\nThe billing row comes from a spreadsheet. The workflow **recomputes** the totals, validates, generates a **ZUGFeRD** invoice with your own PDF template and sends it.\n\nThe point sits in **Build Invoice**: alongside the standard fields, a `transport` object rides on the payload. It is **not evaluated** for XML generation — the ZUGFeRD data stays exactly as conformant as it would be without it. In the PDF template the fields are available as placeholders: `{{transport.vessel}}`, `{{transport.eta?}}`.\n\n**Before you start:** fill in `Set Config` and create the three sheets (note 2).",
        "height": 560,
        "width": 660,
        "color": 7
      },
      "id": "afa631a0-07f6-4bcf-ae64-db0ca185ccb1",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -180,
        -460
      ]
    },
    {
      "parameters": {
        "content": "## 1 · Why this works\nA ZUGFeRD invoice is a PDF/A-3 with embedded XML — two layers with different jobs.\n\n**XML** is standardised to EN 16931 and stays that way. That is the only reason the recipient's software processes the invoice automatically.\n\n**PDF** is your document. This is where whatever your customer needs for matching may go.\n\nFor **plain XRechnung** none of this applies: there is no document to print anything onto.",
        "height": 560,
        "width": 420,
        "color": 4
      },
      "id": "2d12ecc6-74ce-41f5-b3ac-2f22d2aa62c3",
      "name": "1 · Two layers",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        500,
        -460
      ]
    },
    {
      "parameters": {
        "content": "## 2 · The spreadsheet\n\n**Billing** — one row per billing:\n`billingId`, `invoiceNumber`, `issueDate`, `dueDate`, `buyerKey`, `grossTotal`, `status`\nplus the extra details: `vessel`, `voyage`, `billOfLading`, `portOfLoading`, `portOfDischarge`, `eta`, `packages`, `weightKg`\n\n**LineItems** — `billingId`, `description`, `quantity`, `unit`, `unitPrice`, `taxRate`\n\n**Customers** — `buyerKey`, `name`, `street`, `postalCode`, `city`, `countryCode`, `vatId`, `email`, `buyerReference`, `templateId`\n\n⚠️ Without an entry in **Customers** there is neither a recipient address nor a buyer reference. The document goes to the error path. That is intentional.\n\n⚠️ **Read Master Data** and **Read Line Items** are set to *Execute Once* — otherwise the Sheets node reads the tab once **per incoming item**.",
        "height": 560,
        "width": 460,
        "color": 5
      },
      "id": "804c10b9-66e5-4e50-a859-8284a378b103",
      "name": "2 · The three sheets",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        940,
        -460
      ]
    },
    {
      "parameters": {
        "content": "## 3 · Two things that do not happen here\n\n**Invoice numbers are not assigned.** Two parallel runs would read the same highest value and write the same number. German §14 UStG requires a number assigned **only once** — it comes from the system that assigns it transactionally and sits in the spreadsheet.\n\n**Totals are not taken over.** Net, VAT and gross are computed from the line items and checked against `grossTotal`. Tolerance two cents. That catches the cases where somebody added a line item by hand.",
        "height": 560,
        "width": 460,
        "color": 3
      },
      "id": "b8d830bd-7258-40ba-a088-543cd11caac6",
      "name": "3 · Numbers and totals",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1420,
        -460
      ]
    },
    {
      "parameters": {
        "content": "## 4 · Where things belong\nWhat the recipient needs **machine-readable** belongs in the fields of the standard — not in the extra object.\n\n`buyerReference` has a place there; in XRechnung it carries the **Leitweg-ID**. Put such details in the extra object and they are on the paper, but the recipient's software won't find them.\n\n**Rule of thumb:** check first whether the standard has a field. Only what finds no home there and is meant for a human goes on the document layer.",
        "height": 560,
        "width": 440,
        "color": 4
      },
      "id": "a12f6885-bb70-44ab-ae6d-48f2c84954bb",
      "name": "4 · Where things belong",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1900,
        -460
      ]
    },
    {
      "parameters": {
        "content": "## 5 · What doesn't get through\n**Buildable?** — buyer unknown, no line items, totals disagree.\n\n**Valid?** — `valid` from the object check. The `results[]` per format sit in the same item and belong in the error message.\n\nBoth NoOp nodes are placeholders: this is where a mail to the case worker goes, carrying `reason` or the validation results in plain text.",
        "height": 260,
        "width": 620,
        "color": 6
      },
      "id": "a8d04ba6-81af-4267-a6e2-9e42af1e7da1",
      "name": "5 · Error path",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1160,
        560
      ]
    }
  ],
  "connections": {
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Set Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Config": {
      "main": [
        [
          {
            "node": "Read Billing Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Billing Rows": {
      "main": [
        [
          {
            "node": "Read Master Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Master Data": {
      "main": [
        [
          {
            "node": "Read Line Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Line Items": {
      "main": [
        [
          {
            "node": "Build Invoice",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Invoice": {
      "main": [
        [
          {
            "node": "Buildable?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Buildable?": {
      "main": [
        [
          {
            "node": "invoice-api.xhub: Validate",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Not Buildable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "invoice-api.xhub: Validate": {
      "main": [
        [
          {
            "node": "Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Valid?": {
      "main": [
        [
          {
            "node": "invoice-api.xhub: Generate",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Not Valid",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "invoice-api.xhub: Generate": {
      "main": [
        [
          {
            "node": "Store E-Invoice",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store E-Invoice": {
      "main": [
        [
          {
            "node": "Send E-Invoice",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send E-Invoice": {
      "main": [
        [
          {
            "node": "Update Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  }
}
