AutomationFlowsAI & RAG › Gmail Shipping Docs to Google Sheets

Gmail Shipping Docs to Google Sheets

Original n8n title: Shipping Document Processor

Shipping Document Processor. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 10 nodes.

Event trigger★★★★☆ complexity10 nodesGmail TriggerGmailN8N Nodes PdfvectorGoogle SheetsSlack
AI & RAG Trigger: Event Nodes: 10 Complexity: ★★★★☆ Added:

This workflow follows the Gmail → Gmail Trigger recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "Shipping Document Processor",
  "nodes": [
    {
      "parameters": {
        "content": "## \ud83d\udea2 Shipping Document Processor\n\n### What this workflow does\n1. Watches Gmail for shipping docs\n2. Extracts B/L, containers, route, ETA\n3. Calculates days to arrival\n4. Logs to shipment tracker\n5. Alerts on arrivals within 3 days\n\n### Handles\n- Bills of Lading (B/L)\n- Packing Lists\n- Commercial Invoices\n- Customs Documents\n\n### Setup steps\n1. Connect Gmail OAuth2 credentials\n2. Get PDF Vector API key from pdfvector.com/api-keys\n3. Create Google Sheet with columns below\n4. Connect Slack for notifications\n5. Update credential IDs in the nodes\n\n### Sheet columns\nShipment ID, Document Type, B/L Number, Shipper, Consignee, Route, Vessel, ETD, ETA, Days to Arrival, Containers, Container Count, Total Weight (kg), HS Codes, Incoterms, Received Date\n\n### Perfect for\n- Freight forwarders\n- Import/export teams\n- Logistics managers",
        "height": 560,
        "width": 320,
        "color": 5
      },
      "id": "sticky-main",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        60,
        60
      ]
    },
    {
      "parameters": {
        "content": "## \u23f0 ETA Status\n\nAlerts based on days to arrival:\n- Arrived/Overdue: \u22640 days\n- Arriving Soon: 1-3 days \u2192 urgent alert\n- In Transit: >3 days \u2192 normal notification",
        "height": 140,
        "width": 260
      },
      "id": "sticky-eta",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        680,
        60
      ]
    },
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "filters": {
          "includeSpamTrash": false
        }
      },
      "id": "gmail-trigger",
      "name": "Gmail Trigger",
      "type": "n8n-nodes-base.gmailTrigger",
      "typeVersion": 1,
      "position": [
        140,
        340
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "notes": "Monitor for shipping documents"
    },
    {
      "parameters": {
        "operation": "get",
        "messageId": "={{ $json.id }}",
        "simple": false,
        "options": {
          "downloadAttachments": true
        }
      },
      "id": "gmail-get",
      "name": "Get a message",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        340,
        340
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "extract",
        "inputType": "file",
        "prompt": "Extract all shipping information from this document:\n\n1. Document Identification:\n   - Document type (Bill of Lading, Packing List, Commercial Invoice, etc.)\n   - B/L number or booking number\n\n2. Parties:\n   - Shipper name, address, country\n   - Consignee name, address, country\n   - Notify party if present\n\n3. Container Information:\n   - Container numbers\n   - Container types (20GP, 40GP, 40HC, etc.)\n   - Seal numbers\n\n4. Voyage Details:\n   - Vessel name\n   - Voyage number\n   - Port of Loading\n   - Port of Discharge\n   - Place of Delivery\n   - ETD (Estimated Time of Departure)\n   - ETA (Estimated Time of Arrival)\n\n5. Cargo Details:\n   - Cargo description\n   - HS codes (tariff codes)\n   - Number of packages\n   - Gross weight\n   - Volume/CBM\n\n6. Terms:\n   - Freight terms (Prepaid/Collect)\n   - Incoterms (FOB, CIF, EXW, etc.)\n   - Special instructions",
        "schema": "{\"type\":\"object\",\"properties\":{\"documentType\":{\"type\":\"string\",\"enum\":[\"Bill of Lading\",\"Packing List\",\"Commercial Invoice\",\"Customs Declaration\",\"Shipping Manifest\",\"Arrival Notice\",\"Delivery Order\",\"Other\"]},\"blNumber\":{\"type\":\"string\",\"description\":\"Bill of Lading number\"},\"bookingNumber\":{\"type\":\"string\",\"description\":\"Booking reference number\"},\"shipper\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\"},\"address\":{\"type\":\"string\"},\"country\":{\"type\":\"string\"}}},\"consignee\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\"},\"address\":{\"type\":\"string\"},\"country\":{\"type\":\"string\"}}},\"notifyParty\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\"},\"contact\":{\"type\":\"string\"}}},\"containers\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"number\":{\"type\":\"string\"},\"type\":{\"type\":\"string\"},\"sealNumber\":{\"type\":\"string\"}}}},\"vessel\":{\"type\":\"string\",\"description\":\"Vessel/ship name\"},\"voyageNumber\":{\"type\":\"string\"},\"portOfLoading\":{\"type\":\"string\"},\"portOfDischarge\":{\"type\":\"string\"},\"placeOfDelivery\":{\"type\":\"string\"},\"etd\":{\"type\":\"string\",\"description\":\"Estimated departure date\"},\"eta\":{\"type\":\"string\",\"description\":\"Estimated arrival date\"},\"cargo\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"description\":{\"type\":\"string\"},\"hsCode\":{\"type\":\"string\"},\"packages\":{\"type\":\"number\"},\"grossWeight\":{\"type\":\"number\"},\"volume\":{\"type\":\"number\"}}}},\"freightTerms\":{\"type\":\"string\",\"description\":\"Prepaid or Collect\"},\"incoterms\":{\"type\":\"string\",\"description\":\"FOB, CIF, EXW, etc.\"},\"totalWeight\":{\"type\":\"number\",\"description\":\"Total gross weight in kg\"},\"totalVolume\":{\"type\":\"number\",\"description\":\"Total volume in CBM\"},\"specialInstructions\":{\"type\":\"string\"}},\"required\":[\"documentType\"],\"additionalProperties\":false}",
        "binaryPropertyName": "attachment_0"
      },
      "id": "pdfvector-extract",
      "name": "PDF Vector - Extract Shipping",
      "type": "n8n-nodes-pdfvector.pdfVector",
      "typeVersion": 2,
      "position": [
        540,
        340
      ],
      "credentials": {
        "pdfVectorApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const doc = $input.first().json.data;\n\n// Get email data with optional chaining for manual testing\nconst emailData = $('Get a message')?.item?.json || {};\nconst senderEmail = emailData?.from?.value?.[0]?.address || 'test@example.com';\nconst receivedDate = emailData?.date || new Date().toISOString();\n\n// Generate shipment ID\nconst shipmentId = doc.blNumber || doc.bookingNumber || `SHP-${Date.now().toString(36).toUpperCase()}`;\n\n// Format containers\nconst containers = doc.containers || [];\nconst containerList = containers.length > 0\n  ? containers.map(c => `${c.number || 'N/A'} (${c.type || 'N/A'})`).join(', ')\n  : 'N/A';\nconst containerCount = containers.length;\n\n// Format cargo\nconst cargo = doc.cargo || [];\nconst cargoList = cargo.length > 0\n  ? cargo.map(c => `${c.description || 'Unknown'} - ${c.packages || 0} pkgs, ${c.grossWeight || 0} kg`).join('; ')\n  : 'N/A';\nconst hsCodes = cargo.map(c => c.hsCode).filter(Boolean).join(', ') || 'N/A';\n\n// Parse ETA with better date handling\nfunction parseDate(dateStr) {\n  if (!dateStr) return null;\n  \n  let date = new Date(dateStr);\n  if (!isNaN(date.getTime())) return date;\n  \n  // Try common date formats\n  const cleanedStr = dateStr\n    .replace(/\\s+at\\s+/i, ' ')\n    .replace(/\\s+(EST|CST|MST|PST|EDT|CDT|MDT|PDT|UTC|GMT)$/i, '');\n  \n  date = new Date(cleanedStr);\n  if (!isNaN(date.getTime())) return date;\n  \n  return null;\n}\n\n// Calculate days to arrival\nlet daysToArrival = 'N/A';\nlet etaStatus = 'Unknown';\nconst etaDate = parseDate(doc.eta);\nif (etaDate) {\n  const today = new Date();\n  today.setHours(0, 0, 0, 0);\n  etaDate.setHours(0, 0, 0, 0);\n  daysToArrival = Math.ceil((etaDate - today) / (1000 * 60 * 60 * 24));\n  \n  // Determine status\n  if (daysToArrival <= 0) etaStatus = 'Arrived/Overdue';\n  else if (daysToArrival <= 3) etaStatus = 'Arriving Soon';\n  else etaStatus = 'In Transit';\n}\n\n// Route summary\nconst route = `${doc.portOfLoading || 'N/A'} \u2192 ${doc.portOfDischarge || 'N/A'}`;\n\n// Shipper/Consignee names\nconst shipperName = doc.shipper?.name || 'N/A';\nconst consigneeName = doc.consignee?.name || 'N/A';\n\nreturn [{\n  json: {\n    shipmentId: shipmentId,\n    documentType: doc.documentType || 'Unknown',\n    blNumber: doc.blNumber || 'N/A',\n    bookingNumber: doc.bookingNumber || 'N/A',\n    \n    // Parties\n    shipper: doc.shipper,\n    shipperName: shipperName,\n    consignee: doc.consignee,\n    consigneeName: consigneeName,\n    notifyParty: doc.notifyParty,\n    \n    // Route\n    route: route,\n    portOfLoading: doc.portOfLoading || 'N/A',\n    portOfDischarge: doc.portOfDischarge || 'N/A',\n    placeOfDelivery: doc.placeOfDelivery || 'N/A',\n    \n    // Vessel\n    vessel: doc.vessel || 'N/A',\n    voyageNumber: doc.voyageNumber || 'N/A',\n    \n    // Dates\n    etd: doc.etd || 'N/A',\n    eta: doc.eta || 'N/A',\n    daysToArrival: daysToArrival,\n    etaStatus: etaStatus,\n    \n    // Containers\n    containers: containers,\n    containerList: containerList,\n    containerCount: containerCount,\n    \n    // Cargo\n    cargo: cargo,\n    cargoList: cargoList,\n    hsCodes: hsCodes,\n    totalWeight: doc.totalWeight || 0,\n    totalVolume: doc.totalVolume || 0,\n    \n    // Terms\n    freightTerms: doc.freightTerms || 'N/A',\n    incoterms: doc.incoterms || 'N/A',\n    specialInstructions: doc.specialInstructions || 'None',\n    \n    // Meta\n    senderEmail: senderEmail,\n    receivedDate: receivedDate,\n    processedAt: new Date().toISOString()\n  }\n}];"
      },
      "id": "process-shipping",
      "name": "Process Shipping Data",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        740,
        340
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "list"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Shipment ID": "={{ $json.shipmentId }}",
            "Document Type": "={{ $json.documentType }}",
            "B/L Number": "={{ $json.blNumber }}",
            "Shipper": "={{ $json.shipperName }}",
            "Consignee": "={{ $json.consigneeName }}",
            "Route": "={{ $json.route }}",
            "Vessel": "={{ $json.vessel }}",
            "ETD": "={{ $json.etd }}",
            "ETA": "={{ $json.eta }}",
            "Days to Arrival": "={{ $json.daysToArrival }}",
            "Containers": "={{ $json.containerList }}",
            "Container Count": "={{ $json.containerCount }}",
            "Total Weight (kg)": "={{ $json.totalWeight }}",
            "HS Codes": "={{ $json.hsCodes }}",
            "Incoterms": "={{ $json.incoterms }}",
            "Received Date": "={{ $json.receivedDate }}"
          }
        },
        "options": {}
      },
      "id": "sheets-log",
      "name": "Log to Shipment Tracker",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        940,
        340
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "arriving-soon-check",
              "leftValue": "={{ $('Process Shipping Data').item.json.etaStatus }}",
              "rightValue": "Arriving Soon",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            },
            {
              "id": "arrived-check",
              "leftValue": "={{ $('Process Shipping Data').item.json.etaStatus }}",
              "rightValue": "Arrived/Overdue",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "or"
        },
        "options": {}
      },
      "id": "if-arriving-soon",
      "name": "Arriving Soon?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1140,
        340
      ]
    },
    {
      "parameters": {
        "authentication": "oAuth2",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "YOUR_SLACK_CHANNEL_ID",
          "mode": "list"
        },
        "text": "=\ud83d\udea8 *SHIPMENT ARRIVING SOON*\n\n*Shipment:* {{ $('Process Shipping Data').item.json.shipmentId }}\n*Type:* {{ $('Process Shipping Data').item.json.documentType }}\n\n\ud83d\udea2 *Route:* {{ $('Process Shipping Data').item.json.route }}\n*Vessel:* {{ $('Process Shipping Data').item.json.vessel }}\n\n\u23f0 *ETA:* {{ $('Process Shipping Data').item.json.eta }}\n\ud83d\udd34 *Only {{ $('Process Shipping Data').item.json.daysToArrival }} days until arrival!*\n\n\ud83d\udce6 *Cargo:*\n\u2022 Containers: {{ $('Process Shipping Data').item.json.containerCount }}\n\u2022 Weight: {{ $('Process Shipping Data').item.json.totalWeight }} kg\n\u2022 Incoterms: {{ $('Process Shipping Data').item.json.incoterms }}\n\n*Shipper:* {{ $('Process Shipping Data').item.json.shipperName }}\n*Consignee:* {{ $('Process Shipping Data').item.json.consigneeName }}",
        "otherOptions": {}
      },
      "id": "slack-urgent",
      "name": "Urgent - Arriving Soon",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [
        1340,
        240
      ],
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "authentication": "oAuth2",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "YOUR_SLACK_CHANNEL_ID",
          "mode": "list"
        },
        "text": "=\ud83d\udce6 *New Shipping Document Processed*\n\n*Shipment:* {{ $('Process Shipping Data').item.json.shipmentId }}\n*Type:* {{ $('Process Shipping Data').item.json.documentType }}\n\n\ud83d\udea2 *Route:* {{ $('Process Shipping Data').item.json.route }}\n*Vessel:* {{ $('Process Shipping Data').item.json.vessel }}\n*ETA:* {{ $('Process Shipping Data').item.json.eta }} ({{ $('Process Shipping Data').item.json.daysToArrival }} days)\n\n\ud83d\udcca *Details:*\n\u2022 Containers: {{ $('Process Shipping Data').item.json.containerCount }}\n\u2022 Weight: {{ $('Process Shipping Data').item.json.totalWeight }} kg\n\u2022 Incoterms: {{ $('Process Shipping Data').item.json.incoterms }}\n\n*Shipper:* {{ $('Process Shipping Data').item.json.shipperName }}\n*Consignee:* {{ $('Process Shipping Data').item.json.consigneeName }}",
        "otherOptions": {}
      },
      "id": "slack-normal",
      "name": "Notify Logistics Team",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [
        1340,
        440
      ],
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Gmail Trigger": {
      "main": [
        [
          {
            "node": "Get a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get a message": {
      "main": [
        [
          {
            "node": "PDF Vector - Extract Shipping",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PDF Vector - Extract Shipping": {
      "main": [
        [
          {
            "node": "Process Shipping Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Shipping Data": {
      "main": [
        [
          {
            "node": "Log to Shipment Tracker",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Shipment Tracker": {
      "main": [
        [
          {
            "node": "Arriving Soon?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Arriving Soon?": {
      "main": [
        [
          {
            "node": "Urgent - Arriving Soon",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Notify Logistics Team",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "tags": [
    {
      "name": "PDF Vector"
    },
    {
      "name": "Logistics"
    },
    {
      "name": "Shipping"
    },
    {
      "name": "Bill of Lading"
    }
  ],
  "meta": {
    "templateCredsSetupCompleted": false
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Shipping Document Processor. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 10 nodes.

Source: https://github.com/khanhduyvt0101/workflows/blob/0153ee2efc0f692c931b9bb4c2a04abf11756822/n8n-workflows/shipping-document-processor.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

13. Insurance Pre-Authorization. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 12 nodes.

Gmail Trigger, Gmail, N8N Nodes Pdfvector +2
AI & RAG

Job Application Processor & Candidate Scorer. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 10 nodes.

Gmail Trigger, Gmail, N8N Nodes Pdfvector +2
AI & RAG

W14 - Purchase Order Processor & Approval Workflow. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 9 nodes.

Gmail Trigger, Gmail, N8N Nodes Pdfvector +2
AI & RAG

Insurance Claim Document Processor. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 9 nodes.

Gmail Trigger, Gmail, N8N Nodes Pdfvector +2
AI & RAG

Bank Statement Analyzer & Budget Tracker. Uses stickyNote, gmailTrigger, gmail, n8n-nodes-pdfvector. Event-driven trigger; 8 nodes.

Gmail Trigger, Gmail, N8N Nodes Pdfvector +2