{
  "name": "Finance Ops Invoice Automation",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "finance-ops-invoice-automation",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-trigger",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "notesInFlow": true,
      "notes": "Invoice email, uploaded PDF, vendor portal webhook, or accounting inbox"
    },
    {
      "parameters": {
        "jsCode": "const input = $json.body ?? $json;\nconst requiredFields = [\n  \"vendorName\",\n  \"invoiceNumber\",\n  \"amount\",\n  \"currency\",\n  \"dueDate\",\n  \"invoiceText\",\n  \"poNumber\",\n  \"approver\"\n];\nconst missingFields = requiredFields.filter((field) => input[field] === undefined || input[field] === null || input[field] === '');\n\nreturn [{\n  json: {\n    workflow: \"Finance Ops Invoice Automation\",\n    trigger: \"Invoice email, uploaded PDF, vendor portal webhook, or accounting inbox\",\n    desiredOutcome: \"Extract invoice data, match vendors and POs, route approvals, sync accounting, and flag anomalies.\",\n    payload: input,\n    missingFields,\n    config: {\n  \"ghlBaseUrl\": \"https://services.leadconnectorhq.com\",\n  \"openAiUrl\": \"https://api.openai.com/v1/responses\",\n  \"slackWebhookUrl\": \"https://hooks.slack.com/services/REPLACE/ME\"\n},\n  },\n}];"
      },
      "id": "normalize-payload",
      "name": "Normalize Payload",
      "type": "n8n-nodes-base.code",
      "typeVersion": 1,
      "position": [
        260,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "const data = $json;\nconst payloadText = JSON.stringify(data.payload, null, 2);\nconst actions = [\n  \"extract_invoice_fields\",\n  \"match_vendor_and_po\",\n  \"detect_anomalies\",\n  \"route_approval\",\n  \"sync_accounting_system\",\n  \"notify_finance\"\n];\n\nconst prompt = [\n  \"You are a finance operations agent. Extract invoice fields, detect anomalies, match vendors, and recommend approval routing without inventing data.\",\n  'Return strict JSON with: qualification, score, recommendedActions, messages, risks, humanApprovalRequired.',\n  'Available action types: ' + actions.join(', '),\n  'Payload:',\n  payloadText,\n].join('\\n\\n');\n\n// Replace this stub with an OpenAI, Claude, or AI Agent node when credentials are connected.\nconst scoreBase = data.missingFields.length ? 58 : 82;\nconst humanApprovalRequired = data.missingFields.length > 0 || /refund|legal|angry|urgent|complaint/i.test(payloadText);\n\nreturn [{\n  json: {\n    ...data,\n    ai: {\n      prompt,\n      qualification: data.missingFields.length ? 'needs_more_info' : 'ready_for_action',\n      score: scoreBase,\n      recommendedActions: actions,\n      messages: {\n        internalSummary: `Finance Ops Invoice Automation: prepared action plan for ${data.payload.email || data.payload.phone || data.payload.company || 'incoming item'}.`,\n        customerDraft: 'Thanks for reaching out. I have the details and will help with the next step.',\n      },\n      risks: data.missingFields.map((field) => `Missing required field: ${field}`),\n      humanApprovalRequired,\n    },\n  },\n}];"
      },
      "id": "ai-decision-stub",
      "name": "AI Decision Stub",
      "type": "n8n-nodes-base.code",
      "typeVersion": 1,
      "position": [
        520,
        0
      ],
      "notesInFlow": true,
      "notes": "Replace this with a real model node after credentials are connected."
    },
    {
      "parameters": {
        "jsCode": "const now = new Date().toISOString();\nconst tasks = $json.ai.recommendedActions.map((action, index) => ({\n  step: index + 1,\n  action,\n  status: $json.ai.humanApprovalRequired ? 'pending_human_review' : 'ready',\n  idempotencyKey: [$json.workflow, action, $json.payload.email || $json.payload.phone || $json.payload.invoiceNumber || now].join('::'),\n}));\n\nreturn [{\n  json: {\n    ...$json,\n    run: {\n      createdAt: now,\n      mode: 'draft_safe_import',\n      tasks,\n    },\n  },\n}];"
      },
      "id": "build-action-tasks",
      "name": "Build Action Tasks",
      "type": "n8n-nodes-base.code",
      "typeVersion": 1,
      "position": [
        780,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "return [{\n  json: {\n    ok: true,\n    workflow: $json.workflow,\n    mode: $json.run.mode,\n    qualification: $json.ai.qualification,\n    score: $json.ai.score,\n    humanApprovalRequired: $json.ai.humanApprovalRequired,\n    missingFields: $json.missingFields,\n    tasks: $json.run.tasks,\n    customerDraft: $json.ai.messages.customerDraft,\n    internalSummary: $json.ai.messages.internalSummary,\n    activationNote: 'External HTTP/API nodes are present but disabled. Connect credentials and route approved actions before production activation.',\n  },\n}];"
      },
      "id": "audit-and-response-body",
      "name": "Audit and Response Body",
      "type": "n8n-nodes-base.code",
      "typeVersion": 1,
      "position": [
        1040,
        0
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{$json}}",
        "options": {}
      },
      "id": "respond-to-webhook",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        1300,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.ocr.space/parse/image",
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{$json}}",
        "options": {}
      },
      "id": "ocr-extract-pdf-text",
      "name": "OCR - Extract PDF Text",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        520,
        300
      ],
      "disabled": true,
      "notesInFlow": true,
      "notes": "Disabled by default. Attach credentials, verify endpoint schema, then connect into the live path."
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://quickbooks.api.intuit.com/v3/company/REPLACE/bill",
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{$json}}",
        "options": {}
      },
      "id": "quickbooks-xero-create-bill",
      "name": "QuickBooks/Xero - Create Bill",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        780,
        300
      ],
      "disabled": true,
      "notesInFlow": true,
      "notes": "Disabled by default. Attach credentials, verify endpoint schema, then connect into the live path."
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$json.config.slackWebhookUrl}}",
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{$json}}",
        "options": {}
      },
      "id": "slack-approval-request",
      "name": "Slack - Approval Request",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        1040,
        300
      ],
      "disabled": true,
      "notesInFlow": true,
      "notes": "Disabled by default. Attach credentials, verify endpoint schema, then connect into the live path."
    }
  ],
  "connections": {
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Normalize Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Payload": {
      "main": [
        [
          {
            "node": "AI Decision Stub",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Decision Stub": {
      "main": [
        [
          {
            "node": "Build Action Tasks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Action Tasks": {
      "main": [
        [
          {
            "node": "Audit and Response Body",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Audit and Response Body": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "draft",
  "meta": {
    "templateCredsSetupCompleted": false,
    "generatedBy": "Gstack n8n AI Ops Workflow Pack"
  },
  "tags": [
    "ai-ops",
    "starter",
    "gstack"
  ]
}