{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "9f8389be-ac2e-4a1d-bfe3-18a7d9900b36",
      "name": "Gmail Trigger",
      "type": "n8n-nodes-base.gmailTrigger",
      "position": [
        20,
        460
      ],
      "parameters": {
        "simple": false,
        "filters": {},
        "options": {
          "downloadAttachments": false
        },
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 1.2
    },
    {
      "id": "5eb9eadc-3836-4cc9-9130-62bc655b9eb0",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        520,
        640
      ],
      "parameters": {
        "jsonSchemaExample": "[\n  {\n  \"collection_request_id\": \"04-06-4EDW-75\",\n  \"store_id\":\"FRX-001\",\n  \"address\": \"103 Rue du Faubourg Saint-Antoine, 75011 Paris\",\n  \"collection_date\": \"June 7th, 2025\",\n  \"collection_time\": \"10:00\"\n}\n]\n\n\n"
      },
      "typeVersion": 1.2
    },
    {
      "id": "1a98f859-b7ba-4778-a05b-7bd1c6da29fe",
      "name": "AI Agent Parser",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        300,
        460
      ],
      "parameters": {
        "text": "=Here is an email from a customer requesting a shipment:\n\nSubject: {{ $json.subject }}\nBody: {{ $json.text }}\n\nPlease extract all the structured logistics data based on the format provided.",
        "options": {
          "systemMessage": "=You are a logistics assistant working for a transportation company.\n\nYour job is to extract **structured data** from collection request emails sent by customers (typically retail chains like Franprix). These emails include a list of pickup addresses, a collection start time, store identifiers, and a unique Collection Request ID.\n\nYou must return an **array of JSON objects**, where each object represents one pickup location.\n\nEach object must contain:\n\n{\n  \"collection_request_id\": \"\",\n  \"store_id\": \"\",\n  \"address\": \"\",\n  \"collection_date\": \"\",\n  \"collection_time\":\"\"\n}\n\n### Instructions:\n- Extract the value of `Collection Request ID` from the email and apply it to each object.\n- For each pickup location:\n  - Extract the **store_id** (e.g., FRX-001)\n  - Extract the full **address**\n  - Use the same **collection_time_start** for all records, as provided in the email\n- If any field is missing, set its value to `null`.\n- Do not infer missing information.\n- Keep the original formatting of the time and date. Do not transform it.\n- Do not add extra fields. The output must be a **JSON array**.\n\nThis output will be processed by another system. Follow the format exactly.\n"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.9
    },
    {
      "id": "170e9ffb-23a6-42ce-bca9-ae31d18124f0",
      "name": "Record Email Content",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1360,
        460
      ],
      "parameters": {
        "columns": {
          "value": {
            "address": "={{ $json.address }}",
            "store_id": "={{ $json.store_id }}",
            "location_id": "={{ $json.store_id }}_{{ $json.collection_request_id }}",
            "collection_date": "={{ $json.collection_date }}",
            "collection_time": "={{ $json.collection_time }}",
            "collection_request_id": "={{ $json.collection_request_id }}"
          },
          "schema": [
            {
              "id": "collection_request_id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "collection_request_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "store_id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "store_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "address",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "address",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "location_id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "location_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "longitude",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "longitude",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "latitude",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "latitude",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "gps_coordinates",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "gps_coordinates",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "collection_date",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "collection_date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "collection_time",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "collection_time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "sequence",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "sequence",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "total_duration",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "total_duration",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "shipment_number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1094697629,
          "cachedResultUrl": "",
          "cachedResultName": "Multi-Stop"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": "Transportation Orders"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.6
    },
    {
      "id": "4c3c818c-9931-4eed-b145-39190e405b50",
      "name": "5 sec",
      "type": "n8n-nodes-base.wait",
      "position": [
        2140,
        460
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "f567d1dd-432b-4e8e-9dec-5f27799ff61f",
      "name": "Query Open Route API Pickup",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1560,
        460
      ],
      "parameters": {
        "url": "https://api.openrouteservice.org/geocode/search",
        "options": {},
        "sendQuery": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "text",
              "value": "={{ $json.address }}"
            },
            {
              "name": "boundary.country",
              "value": "=FR"
            },
            {
              "name": "sources",
              "value": "openstreetmap"
            },
            {
              "name": "size",
              "value": "1"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json; charset=utf-8"
            },
            {
              "name": "Accept",
              "value": "application/json, application/geo+json, application/gpx+xml, img/png; charset=utf-8"
            }
          ]
        }
      },
      "credentials": {
        "httpQueryAuth": {
          "name": "<your credential>"
        },
        "httpBearerAuth": {
          "name": "<your credential>"
        },
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "a88ca302-325e-4309-b427-a9eb807ab0e9",
      "name": "Save Pickup GPS",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1940,
        460
      ],
      "parameters": {
        "columns": {
          "value": {
            "latitude": "={{ $json.latitude }}",
            "sequence": "=",
            "longitude": "={{ $json.longitude }}",
            "location_id": "={{ $('Record Email Content').item.json.location_id }}",
            "gps_coordinates": "={{ $json.longitude }}, {{ $json.latitude }}"
          },
          "schema": [
            {
              "id": "collection_request_id",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "collection_request_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "store_id",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "store_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "address",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "address",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "location_id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "location_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "longitude",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "longitude",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "latitude",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "latitude",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "gps_coordinates",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "gps_coordinates",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "collection_date",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "collection_date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "collection_time",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "collection_time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "sequence",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "sequence",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "total_duration",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "total_duration",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "location_id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1094697629,
          "cachedResultUrl": "",
          "cachedResultName": "Multi-Stop"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "17wbroY0w6xmw8AT-p2c_n1WbHzmfesz6d1DJbp39fn8",
          "cachedResultUrl": "",
          "cachedResultName": "Transportation Orders"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.6
    },
    {
      "id": "e07773a7-dd80-411e-b056-f6eb473d5dcb",
      "name": "GPS Pickup",
      "type": "n8n-nodes-base.set",
      "position": [
        1740,
        460
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "e4c92077-f294-4e49-8276-20d1441c0c2c",
              "name": "longitude",
              "type": "string",
              "value": "={{ $json.features[0].geometry.coordinates[0] }}"
            },
            {
              "id": "899e5304-e7a1-4ce1-bf9a-f403d74934fb",
              "name": "latitude",
              "type": "string",
              "value": "={{ $json.features[0].geometry.coordinates[1] }}"
            },
            {
              "id": "5b35642b-dda4-42b8-b7bb-1275616bfb59",
              "name": "borough",
              "type": "string",
              "value": "={{ $json.features[0].properties.borough }}"
            },
            {
              "id": "aa74bd9a-f543-48e1-a69b-0e6472bda053",
              "name": "neighbourhood",
              "type": "string",
              "value": "={{ $json.features[0].properties.neighbourhood }}"
            },
            {
              "id": "7f303dc8-4e28-4d46-8e3e-9db00dbcac3a",
              "name": "localadmin",
              "type": "string",
              "value": "={{ $json.features[0].properties.localadmin }}"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 3.4
    },
    {
      "id": "121aa413-54e5-470a-be3b-ef243f3e9445",
      "name": "Collect Coordinates",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        840,
        700
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "=04-06-4EDW-75",
              "lookupColumn": "collection_request_id"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1094697629,
          "cachedResultUrl": "",
          "cachedResultName": "Multi-Stop"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "17wbroY0w6xmw8AT-p2c_n1WbHzmfesz6d1DJbp39fn8",
          "cachedResultUrl": "",
          "cachedResultName": "Transportation Orders"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.6
    },
    {
      "id": "eca19e02-596a-47af-a448-b18ba2aa3af7",
      "name": "Request Open Route API",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1360,
        700
      ],
      "parameters": {
        "url": "https://api.openrouteservice.org/optimization",
        "body": "={{ JSON.stringify({\n  jobs: $json.jobs,\n  vehicles: $json.vehicles\n}) }}\n\n",
        "method": "POST",
        "options": {
          "response": {
            "response": {}
          }
        },
        "sendBody": true,
        "contentType": "raw",
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "rawContentType": "application/json",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json; charset=utf-8"
            },
            {
              "name": "Accept",
              "value": "application/json, application/geo+json, application/gpx+xml, img/png; charset=utf-8"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "6f0027ec-68f9-495f-8958-14960dde3ccc",
      "name": "Collect Shipment Information",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        840,
        1520
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "=04-06-4EDW-75",
              "lookupColumn": "collection_request_id"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1094697629,
          "cachedResultUrl": "",
          "cachedResultName": "Multi-Stop"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "17wbroY0w6xmw8AT-p2c_n1WbHzmfesz6d1DJbp39fn8",
          "cachedResultUrl": "",
          "cachedResultName": "Transportation Orders"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.6
    },
    {
      "id": "743b5224-10c1-4629-85e5-f32a57b85926",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        1140,
        1680
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "93ed811e-041f-4f1b-92eb-b519c5b758f8",
      "name": "AI Agent Reply",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1200,
        1520
      ],
      "parameters": {
        "text": "={{ $json.formatted_reply }}\n",
        "options": {
          "systemMessage": "=You are a logistics assistant working for a transportation company.\n\nYour job is to send a clear, professional, and customer-friendly confirmation email summarizing a **multi-stop pickup schedule**.\n\nUse the preformatted HTML content provided via `{{ $json.formatted_reply }}` to structure the message.\n\n\u2705 Do:\n- Keep a formal and polite tone.\n- Include the pickup request ID, scheduled date & time, and the ordered list of pickup stops.\n- Confirm the estimated total driving duration.\n- Use the content **as-is**. Do not modify or rephrase it.\n\n\u274c Do not:\n- Add or invent data.\n- Change the structure or wording of the HTML provided.\n\n\u26a0\ufe0f Format the reply strictly in **HTML** using tags like `<p>`, `<ul>`, `<li>`, and `<strong>`.\n\nAbout you\n[Name]: LogiGreen Bot\n[Position]: Transportation Planner\n[Company]: LogiGreen Transportation\n[Contact]: logigreenbot@gmail.com\n\n"
        },
        "promptType": "define"
      },
      "typeVersion": 1.9
    },
    {
      "id": "5ba3408c-37c7-4d2b-8972-9e753824b21e",
      "name": "Reply",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1520,
        1520
      ],
      "parameters": {
        "message": "={{ $json.output }}",
        "options": {},
        "messageId": "={{ $('Gmail Trigger').item.json.id }}",
        "operation": "reply"
      },
      "notesInFlow": true,
      "typeVersion": 2.1
    },
    {
      "id": "cfc99cbf-12ae-4c77-be75-99609d3f24d5",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        780,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 1560,
        "height": 1100,
        "content": "### 3. Record Shipment Request Information and fetch distance and driving time using Open Route API\nThis starts by recording all the information parsed by the AI node. Then, we use the **Open Route API** to complete with **geocoding data** *(GPS Coordinates)* that will be used to fetch **driving distance and time**.\n\n#### How to setup?\n- **Setup API Credentials**\n  1. Get your free API key: [Open Route API Documentation](https://openrouteservice.org/dev/#/api-docs)\n  2. Fill the API key in the HTTP request node\n  3. Select the driving mode (**driving-car**: individual truck) or (**driving-hgv**: commercial truck)\n- **Load records in the Google Sheet Node**:\n   1. Add your Google Sheet API credentials to access the Google Sheet file\n   2. Select the file using the list, an URL or an ID\n   3. Select the sheet in which you want to record your working sessions\n   4. Map the fields: **shipment_number**, **pickup_location**, **pickup_address**, **pickup_longitude**, **pickup_latitude**, **expected_pickup_time**,\t**temperature_control**, **destination_store_name**, **destination_address**, **destination_longitude**, **destination_latitude**, **expected_delivery_time**, **driving_distance**, **driving_time**\t\t\t\t\t\t\t\t\t\t\t\t\t\n  [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n\nLongitudes, latitudes, distance and time will be fetched from the **Open Route API**.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "2bb6e08e-1378-49dd-b3a4-ed68b2923705",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 180,
        "height": 820,
        "content": "### 1. Workflow Trigger with Gmail Trigger\nThe workflow is triggered by a new email received in your Gmail mailbox dedicated to process **collection request**.\n\n#### How to setup?\n- **Gmail Trigger Node:** set up your Gmail API credentials\n[Learn more about the Gmail Trigger Node](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.gmailtrigger)\n"
      },
      "typeVersion": 1
    },
    {
      "id": "59841c15-7453-4069-9e6b-af0e9b24dbd4",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        200,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 440,
        "height": 820,
        "content": "### 2. AI Agent to parse the collection process\nThis node will analyze the content of the email to extract information about the **sender** *(name, company)* and the **collections** *(address, time window)*.\n\n#### How to setup?\n- **AI Agent with the Chat Model**:\n   1. Add a **chat model** with the required credentials *(Example: Open AI 4o-mini)*\n   2. Adapt the system prompt to the format of emails you expect to receive *(type of information included)*\n  [Learn more about the AI Agent Node](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent)\n"
      },
      "typeVersion": 1
    },
    {
      "id": "95067b31-0ab2-48f2-b08f-ef08989c81ac",
      "name": "OpenAI Chat Model2",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        260,
        660
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "48a5b173-0022-4628-b4c2-acb8a0a19122",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        800,
        1120
      ],
      "parameters": {
        "color": 7,
        "width": 680,
        "height": 720,
        "content": "### 4. Reply with a confirmation of shipment allocation\nWe first collect all the information of the sequence of pickups from the **Google Sheets** the AI Agent will use that to generate a reply by email.\n#### How to setup?\n- **Load records in the Google Sheet Node**:\n   1. Add your Google Sheet API credentials to access the Google Sheet file\n   2. Select the file using the list, an URL or an ID\n   3. Select the sheet in which you want to record your working sessions\t\t\t\t\t\t\t\t\t\t\t\n  [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n- **AI Agent with the Chat Model**:\n   1. Add a **chat model** with the required credentials *(Example: Open AI 4o-mini)*\n   2. Adapt the system prompt with your company information **(name, contact, position)**\n  [Learn more about the AI Agent Node](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent)\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "bb47b105-1f97-4b23-b92c-dbe318ab21f9",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1080,
        460
      ],
      "parameters": {
        "options": {},
        "batchSize": "=1"
      },
      "typeVersion": 3
    },
    {
      "id": "60ede508-1b35-4ce3-a316-b23bd8d59ef3",
      "name": "Split by Address",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        840,
        460
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "output"
      },
      "typeVersion": 1
    },
    {
      "id": "5325cb92-2d71-4416-a521-1c911b1f6df3",
      "name": "Extract Job",
      "type": "n8n-nodes-base.code",
      "position": [
        1580,
        700
      ],
      "parameters": {
        "jsCode": "const steps = items[0].json.routes[0].steps;\n\nconst orderedJobs = steps\n  .filter(step => step.type === \"job\")\n  .map((step, index) => ({\n    id: step.job,\n    sequence: index + 1\n  }));\n\nreturn orderedJobs.map(job => ({ json: job }));\n"
      },
      "typeVersion": 2
    },
    {
      "id": "6d56227b-9c13-4924-86b3-53573ead3642",
      "name": "Jobs Mapping",
      "type": "n8n-nodes-base.set",
      "position": [
        1240,
        920
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "1d331865-d691-4f5b-9b6f-c6bac6b52e89",
              "name": "jobs",
              "type": "array",
              "value": "={{ $json.jobs }}"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 3.4
    },
    {
      "id": "17afe2de-5512-4c23-bc31-a67ebd10a3c8",
      "name": "Format Jobs",
      "type": "n8n-nodes-base.code",
      "position": [
        1040,
        700
      ],
      "parameters": {
        "jsCode": "// This Function node transforms store pickup data into the OpenRouteService optimization payload\n\nconst inputData = items.map(item => item.json);\n\n// Build the jobs array\nconst jobs = inputData.map((entry, index) => ({\n  id: index + 1,\n  location: [entry.longitude, entry.latitude]\n}));\n\n// Take the second item as the starting location (adjust index if needed)\nconst vehicleStart = [inputData[1].longitude, inputData[1].latitude];\n\nconst vehicles = [\n  {\n    id: 1,\n    start: vehicleStart,\n    capacity: [100],\n    profile: \"driving-hgv\"\n  }\n];\n\nreturn [\n  {\n    json: {\n      jobs,\n      vehicles\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "ea3ebcd5-76c6-43cf-a486-bb778d473c0d",
      "name": "Split Jobs",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        1480,
        920
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "jobs"
      },
      "typeVersion": 1
    },
    {
      "id": "1af40684-cb06-455c-b6f5-0684c06438c3",
      "name": "Merge Sequence with coordinates",
      "type": "n8n-nodes-base.merge",
      "position": [
        1820,
        780
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "fieldsToMatchString": "id"
      },
      "notesInFlow": true,
      "typeVersion": 3.1
    },
    {
      "id": "4d77cac0-5d3a-4778-aa98-ad10e3aa33b2",
      "name": "Format GPS Coordinates",
      "type": "n8n-nodes-base.set",
      "position": [
        2020,
        780
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "ffab2998-7250-4591-a192-f1a2980ea79c",
              "name": "sequence",
              "type": "string",
              "value": "={{ $json.sequence }}"
            },
            {
              "id": "38b7058b-dbd9-4679-b054-5de4b791ce50",
              "name": "gps_coordinates",
              "type": "string",
              "value": "={{ $json.location[0] }}, {{ $json.location[1] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "308f8e6b-ea6a-484f-ac0e-3593fd197360",
      "name": "Save Sequence & Duration",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2200,
        780
      ],
      "parameters": {
        "columns": {
          "value": {
            "sequence": "={{ $json.sequence }}",
            "total_duration": "={{ ($('Request Open Route API').item.json.summary.duration/60).toFixed(2) }}",
            "gps_coordinates": "={{ $json.gps_coordinates }}"
          },
          "schema": [
            {
              "id": "collection_request_id",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "collection_request_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "store_id",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "store_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "address",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "address",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "location_id",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "location_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "longitude",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "longitude",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "latitude",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "latitude",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "gps_coordinates",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "gps_coordinates",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "collection_date",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "collection_date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "collection_time",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "collection_time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "sequence",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "sequence",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "total_duration",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "total_duration",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "gps_coordinates"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1094697629,
          "cachedResultUrl": "",
          "cachedResultName": "Multi-Stop"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "17wbroY0w6xmw8AT-p2c_n1WbHzmfesz6d1DJbp39fn8",
          "cachedResultUrl": "",
          "cachedResultName": "Transportation Orders"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.6
    },
    {
      "id": "d1f64c61-2b7e-4454-b10d-7d9d49998eb7",
      "name": "Format Schedule",
      "type": "n8n-nodes-base.code",
      "position": [
        1000,
        1520
      ],
      "parameters": {
        "jsCode": "// Sort and format the pickup sequence\nconst sorted = items.sort((a, b) => a.json.sequence - b.json.sequence);\n\nconst pickupList = sorted.map((item, index) => `\n  <li><strong>Stop ${index + 1}</strong>: ${item.json.store_id}<br>\n  Address: ${item.json.address}<br>\n  Coordinates: ${item.json.gps_coordinates}<br>\n  Scheduled Time: ${item.json.collection_time}</li>\n`).join('');\n\nconst htmlReply = `\n<p>We have scheduled your multi-stop pickup request (ID: <strong>${sorted[0].json.collection_request_id}</strong>) for <strong>${sorted[0].json.collection_date}</strong> at <strong>${sorted[0].json.collection_time}</strong>.</p>\n\n<p>Below is the confirmed pickup sequence:</p>\n<ul>${pickupList}</ul>\n\n<p>The total estimated driving duration is <strong>${sorted[0].json.total_duration} minutes</strong>.</p>\n`;\n\nreturn [{ json: { formatted_reply: htmlReply } }];\n"
      },
      "typeVersion": 2
    }
  ],
  "connections": {
    "5 sec": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GPS Pickup": {
      "main": [
        [
          {
            "node": "Save Pickup GPS",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Jobs": {
      "main": [
        [
          {
            "node": "Merge Sequence with coordinates",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Extract Job": {
      "main": [
        [
          {
            "node": "Merge Sequence with coordinates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Jobs": {
      "main": [
        [
          {
            "node": "Request Open Route API",
            "type": "main",
            "index": 0
          },
          {
            "node": "Jobs Mapping",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Jobs Mapping": {
      "main": [
        [
          {
            "node": "Split Jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail Trigger": {
      "main": [
        [
          {
            "node": "AI Agent Parser",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent Reply": {
      "main": [
        [
          {
            "node": "Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent Parser": {
      "main": [
        [
          {
            "node": "Split by Address",
            "type": "main",
            "index": 0
          },
          {
            "node": "Collect Coordinates",
            "type": "main",
            "index": 0
          },
          {
            "node": "Collect Shipment Information",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Schedule": {
      "main": [
        [
          {
            "node": "AI Agent Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "Record Email Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Pickup GPS": {
      "main": [
        [
          {
            "node": "5 sec",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split by Address": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent Reply",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model2": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Collect Coordinates": {
      "main": [
        [
          {
            "node": "Format Jobs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Record Email Content": {
      "main": [
        [
          {
            "node": "Query Open Route API Pickup",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format GPS Coordinates": {
      "main": [
        [
          {
            "node": "Save Sequence & Duration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Request Open Route API": {
      "main": [
        [
          {
            "node": "Extract Job",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "AI Agent Parser",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Query Open Route API Pickup": {
      "main": [
        [
          {
            "node": "GPS Pickup",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Collect Shipment Information": {
      "main": [
        [
          {
            "node": "Format Schedule",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Sequence with coordinates": {
      "main": [
        [
          {
            "node": "Format GPS Coordinates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}