{
  "name": "Automate travel expense extraction with OCR, Mistral AI and Supabase",
  "nodes": [
    {
      "id": "8f329fe6-b950-466e-8d25-969b1e56be37",
      "name": "Split Out",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -2560,
        -96
      ]
    },
    {
      "id": "ae47305f-349e-4343-adfa-9ff2adb9bd48",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1520,
        -48
      ]
    },
    {
      "id": "5277e31d-f0ea-44d6-8fbd-a8b0319daf49",
      "name": "Supabase Get",
      "type": "n8n-nodes-base.supabaseTool",
      "position": [
        -848,
        352
      ]
    },
    {
      "id": "f79d8a79-2e22-43a6-ac08-530851788598",
      "name": "When chat message received",
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "position": [
        -3296,
        -32
      ]
    },
    {
      "id": "9873394a-6a42-4222-8016-65fc47f1e168",
      "name": "Simple Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        -3232,
        192
      ]
    },
    {
      "id": "c0c50bbc-fd7d-4acd-82f6-19976c95ffa8",
      "name": "Mistral Cloud Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatMistralCloud",
      "position": [
        -1616,
        272
      ]
    },
    {
      "id": "9e753ed9-e35c-470f-89ff-f74f0b5d6e43",
      "name": "Calculator1",
      "type": "@n8n/n8n-nodes-langchain.toolCalculator",
      "position": [
        -1184,
        352
      ]
    },
    {
      "id": "a0dde695-15d9-4ba9-890d-f5ee712fc95d",
      "name": "CHECK IF BINARY FILE IS PRESENT OR NOT",
      "type": "n8n-nodes-base.if",
      "position": [
        -2832,
        -32
      ]
    },
    {
      "id": "87137e37-8019-4f5c-a58b-f1814e0540fe",
      "name": "NORMALIZE binary file",
      "type": "n8n-nodes-base.code",
      "position": [
        -2256,
        -96
      ]
    },
    {
      "id": "342e95f4-588c-4a8c-9e34-ab5a34934442",
      "name": "OCR (ANY OCR API )",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2016,
        -96
      ]
    },
    {
      "id": "819e1b96-c255-468c-b65f-4aa4c7da338c",
      "name": "STORE OCR OUTPUT",
      "type": "n8n-nodes-base.supabase",
      "position": [
        -1808,
        -96
      ]
    },
    {
      "id": "cdb4c975-741c-497b-8c86-698df7282cfe",
      "name": "Travel reimbursement agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -1232,
        -48
      ]
    },
    {
      "id": "bf945e6a-466a-4138-b16a-6ff190b1f7e7",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2080,
        -272
      ],
      "parameters": {
        "width": 192,
        "height": 336,
        "content": "### OCR (ANY OCR API)\nSends multipart file to the configured OCR endpoint. Expects JSONL/JSON output with `blocks`\n"
      }
    },
    {
      "id": "3db6c166-41f8-4554-888f-2963814b6000",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1024,
        176
      ],
      "parameters": {
        "width": 320,
        "height": 304,
        "content": "## Security & Retention\nFollow zero-retention guidance. Encrypt credentials. Limit Supabase access to required rows/columns only.\n"
      }
    },
    {
      "id": "3f5bcafe-0e90-4f4d-9526-4aa3f83a0306",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2336,
        -272
      ],
      "parameters": {
        "width": 224,
        "height": 336,
        "content": "## NORMALIZE binary file\nCode node: picks the first binary key and returns `binary.data`. Ensures consistent payload shape for downstream OCR.\n"
      }
    },
    {
      "id": "eed6ad14-33ee-481d-9259-4e7ad01755e7",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1264,
        -208
      ],
      "parameters": {
        "width": 464,
        "height": 320,
        "content": "## Travel Reimbursement Agent (core)\nParses OCR into structured fields (vendor, category, dates, currency, total_amount). Infers missing values. Uses Calculator tool to sum totals. Must never return \""
      }
    },
    {
      "id": "cd09bf73-e5d3-4028-9e1b-aadd61c94954",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1840,
        -272
      ],
      "parameters": {
        "width": 192,
        "height": 352,
        "content": "## STORE OCR OUTPUT (Supabase)\n\n"
      }
    },
    {
      "id": "601b751f-b412-4fc1-bdce-628764287217",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2896,
        -256
      ],
      "parameters": {
        "width": null,
        "height": 368,
        "content": "## Binary Presence Check\nIF node verifies presence of `files` in the incoming payload. Routes flow: file present → Split Out → OCR. No file → Merge path to agent.\n"
      }
    },
    {
      "id": "ffb67b60-bacb-4307-a64c-55c173e855ff",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1424,
        192
      ],
      "parameters": {
        "width": 368,
        "height": 288,
        "content": "## Memory & Calculator\nMemory retains short session context. Calculator performs numeric sums, currency handling, and aggregated totals used by the agent.\n\n"
      }
    },
    {
      "id": "aca55599-ac0d-44c9-befa-f3c18ebaa0b8",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1584,
        -208
      ],
      "parameters": {
        "width": null,
        "height": 272,
        "content": "## Merge\nCombines OCR results and non-file user input back into a single item for the Travel Reimbursement Agent to process.\n"
      }
    },
    {
      "id": "c208be09-d0c9-4634-88af-e8071d1d8c21",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2592,
        -272
      ],
      "parameters": {
        "width": null,
        "height": 336,
        "content": "## Split Out (binary -> data)\nExtracts the first binary object into `data` field. \n"
      }
    },
    {
      "id": "1ba393cb-7169-441e-a293-0fa29dfe1872",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3312,
        -256
      ],
      "parameters": {
        "width": 304,
        "height": 512,
        "content": "## Chat Trigger / UI\nReceives user messages and optional uploads. `allowFileUploads` must be true. Uses sessionId to tie uploads to chat sessions.\n"
      }
    },
    {
      "id": "22727a8e-8d14-48d6-94c7-c9be22bf4582",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3872,
        -272
      ],
      "parameters": {
        "width": 464,
        "height": 624,
        "content": "# Travel Reimbursement — Overview \n\n## How it works\nThis workflow accepts chat input and optional uploaded receipts. If a file is attached the flow extracts the first binary, runs OCR, stores the pars"
      }
    },
    {
      "id": "7ae1b477-32b8-4f8f-a63f-461cdd8b6884",
      "name": "Simple Memory1",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        -1360,
        352
      ]
    }
  ],
  "connections": {
    "Merge": {
      "main": [
        [
          {
            "node": "Travel reimbursement agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "node": "NORMALIZE binary file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculator1": {
      "ai_tool": [
        [
          {
            "node": "Travel reimbursement agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Supabase Get": {
      "ai_tool": [
        [
          {
            "node": "Travel reimbursement agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory": {
      "ai_memory": [
        [
          {
            "node": "When chat message received",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory1": {
      "ai_memory": [
        [
          {
            "node": "Travel reimbursement agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "STORE OCR OUTPUT": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OCR (ANY OCR API )": {
      "main": [
        [
          {
            "node": "STORE OCR OUTPUT",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "NORMALIZE binary file": {
      "main": [
        [
          {
            "node": "OCR (ANY OCR API )",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mistral Cloud Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Travel reimbursement agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "When chat message received": {
      "main": [
        [
          {
            "node": "CHECK IF BINARY FILE IS PRESENT OR NOT",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CHECK IF BINARY FILE IS PRESENT OR NOT": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    }
  }
}