This workflow corresponds to n8n.io template #13691 — we link there as the canonical source.
This workflow follows the Agent → Emailsend 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 →
{
"id": "iIUk4v4G8vXADl0a",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "AI Customs Clearance Document Checker",
"tags": [],
"nodes": [
{
"id": "ba5857a1-4dce-41d5-bc31-c7bd6c4d87b6",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-80,
-480
],
"parameters": {
"width": 1060,
"height": 2144,
"content": "## AI Customs Clearance Document Checker\n\nThis workflow automates pre-dispatch customs document validation for international shipments. It ingests shipping document packages, extracts content from each file, uses Claude AI to cross-validate all documents for consistency, regulatory compliance, and HS code accuracy, then flags errors before goods are dispatched \u2014 preventing costly delays, fines, and rejected shipments at the border.\n\n### How it works\n\n1. **Trigger** \u2014 Webhook submission or watched Drive/S3 folder when new shipment docs are uploaded\n2. **Register Shipment** \u2014 Assigns shipment case ID, normalises metadata from payload\n3. **Fetch Document Files** \u2014 Downloads each document from Google Drive or URL\n4. **Extract Text Content** \u2014 Parses PDF/DOCX text from all documents\n5. **Classify Document Types** \u2014 Identifies invoice, packing list, bill of lading, COO, etc.\n6. **Cross-Document Consistency Check** \u2014 Detects mismatches across documents (values, weights, quantities)\n7. **AI Compliance Validation** \u2014 Claude AI validates each doc against destination country rules\n8. **Aggregate Findings** \u2014 Merges per-document results into a shipment-level report\n9. **Route by Risk Level** \u2014 Branches on CLEAR / HOLD / REJECT\n10. **Notify Logistics Team** \u2014 Slack alert with error summary and action items\n11. **Email Exporter Report** \u2014 Detailed validation report with fix instructions\n12. **Update Shipment Tracker** \u2014 Writes status back to Airtable / Google Sheets\n13. **Create Compliance Ticket** \u2014 Opens Jira issue for HOLD or REJECT shipments\n14. **Return API Response** \u2014 Structured JSON result to caller or TMS integration\n\n### Setup Steps\n\n1. Import workflow into n8n\n2. Configure credentials:\n - **Anthropic API** \u2014 Claude AI for compliance validation\n - **Google Drive OAuth** \u2014 Document intake and storage\n - **Google Sheets OAuth** \u2014 Shipment compliance audit log\n - **Airtable** \u2014 Shipment tracker CRM\n - **Slack OAuth** \u2014 Logistics team alerts\n - **SendGrid / SMTP** \u2014 Exporter notification emails\n - **Jira API** \u2014 Compliance issue tracking\n3. Set your Google Drive intake folder ID\n4. Configure destination country rules in the AI prompt node\n5. Set your Airtable base and shipment table IDs\n6. Activate the workflow\n\n### Sample Webhook Payload\n```json\n{\n \"shipmentId\": \"SHP-2025-00392\",\n \"exporterEmail\": \"logistics@exportco.com\",\n \"originCountry\": \"CN\",\n \"destinationCountry\": \"AU\",\n \"incoterms\": \"FOB\",\n \"declaredValue\": 48500,\n \"currency\": \"USD\",\n \"goodsDescription\": \"Electronic Components\",\n \"documents\": [\n { \"name\": \"Commercial Invoice\", \"type\": \"commercial_invoice\", \"driveFileId\": \"1aBcD\" },\n { \"name\": \"Packing List\", \"type\": \"packing_list\", \"driveFileId\": \"2eFgH\" },\n { \"name\": \"Bill of Lading\", \"type\": \"bill_of_lading\", \"driveFileId\": \"3iJkL\" },\n { \"name\": \"Certificate of Origin\", \"type\": \"certificate_of_origin\", \"driveFileId\": \"4mNoP\" }\n ]\n}\n```\n\n### Documents Supported\n- Commercial Invoice\n- Packing List\n- Bill of Lading (B/L) / Airway Bill (AWB)\n- Certificate of Origin (COO / Form D / EUR.1)\n- Customs Entry / Import Declaration\n- Dangerous Goods Declaration (DGD)\n- Phytosanitary / Health Certificate\n- Insurance Certificate\n- Letter of Credit (L/C)\n- Export Licence / Permit\n- Material Safety Data Sheet (MSDS)\n- Fumigation Certificate\n\n### AI Validation Checks\n- **Field Completeness** \u2014 All mandatory fields present and populated\n- **Cross-Document Consistency** \u2014 Values, weights, quantities, HS codes match across docs\n- **HS Code Validation** \u2014 Correct classification for declared goods and destination\n- **Incoterms Compliance** \u2014 Terms correctly applied across invoice and B/L\n- **Valuation Rules** \u2014 Customs value method correct, currency declared\n- **Country of Origin** \u2014 COO criteria met, preferential rates applicable\n- **Restricted / Prohibited Goods** \u2014 Flags potential dual-use, CITES, or embargoed items\n- **Sanction Screening** \u2014 Party names checked against common red flags\n- **Date & Validity** \u2014 Document dates consistent, certificates not expired\n\n### Features\n- Multi-document cross-validation in a single run\n- AI-powered HS code verification and suggestion\n- Destination-country\u2013specific compliance rules\n- Automatic HOLD/REJECT routing for high-risk findings\n- Detailed error report with fix instructions per field\n- Full audit trail in Google Sheets\n- Jira ticket creation for escalated compliance issues\n\n---\n\n**Explore More Automation:** \n[Contact us](https://www.oneclickitsolution.com/contact-us/) to design AI-powered lead nurturing, content engagement, and multi-platform reply workflows tailored to your growth strategy."
},
"typeVersion": 1
},
{
"id": "0b2d0325-6e94-40b8-b422-aae2cb7180cb",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
1032,
572
],
"parameters": {
"color": 5,
"width": 496,
"height": 548,
"content": "## 1. Shipment Intake & Document Retrieval\n### Webhook \u00b7 Drive Trigger \u00b7 File Download \u00b7 Text Extraction"
},
"typeVersion": 1
},
{
"id": "21be3ced-5308-42c6-8308-d97aa8122534",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1584,
608
],
"parameters": {
"color": 5,
"width": 800,
"height": 504,
"content": "## 2. Document Classification & Cross-Document Consistency Check\n### Type Detection \u00b7 Field Extraction \u00b7 Mismatch Detection"
},
"typeVersion": 1
},
{
"id": "b93a1e99-de64-4588-9a30-679eba2d1a44",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
2412,
612
],
"parameters": {
"color": 5,
"width": 840,
"height": 604,
"content": "## 3. Claude AI Customs Compliance Validation\n### Per-Document Rules \u00b7 HS Code Check \u00b7 Sanction Screening"
},
"typeVersion": 1
},
{
"id": "dca3d7b5-2aeb-4dc1-85bc-8ad3f9697cc4",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
3280,
368
],
"parameters": {
"color": 5,
"width": 884,
"height": 1040,
"content": "## 4. Risk Routing \u00b7 Notifications \u00b7 Tracker Update \u00b7 Jira \u00b7 Audit Log"
},
"typeVersion": 1
},
{
"id": "d1692071-f57d-44f8-b539-fe7f4b55e3ea",
"name": "Receive Shipment Documents",
"type": "n8n-nodes-base.webhook",
"position": [
1152,
736
],
"parameters": {
"path": "check-customs-documents",
"options": {
"allowedOrigins": "*"
},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "159e9ea3-ded9-45f7-b94e-506e7b6125e4",
"name": "Watch Shipment Docs Folder",
"type": "n8n-nodes-base.googleDriveTrigger",
"position": [
1152,
928
],
"parameters": {
"event": "fileCreated",
"options": {},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"triggerOn": "specificFolder",
"folderToWatch": {
"__rl": true,
"mode": "id",
"value": "YOUR_SHIPMENT_INTAKE_FOLDER_ID"
}
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"continueOnFail": true
},
{
"id": "a6b9ce08-1606-4c4c-8867-cdaaa1b808b9",
"name": "Register Shipment Case",
"type": "n8n-nodes-base.code",
"position": [
1376,
832
],
"parameters": {
"jsCode": "const raw = $input.first()?.json?.body || $input.first()?.json || {};\n\nconst isWebhook = !!(raw.shipmentId || raw.exporterEmail);\n\nconst shipmentId = raw.shipmentId\n || `SHP-${new Date().getFullYear()}-${String(Date.now()).slice(-5)}`;\n\nconst documents = raw.documents || [];\nconst finalDocs = documents.length > 0 ? documents : [{\n name: raw.name || 'Unknown Document',\n type: 'unknown',\n driveFileId: raw.id || raw.driveFileId || null,\n url: raw.webViewLink || null\n}];\n\nconst shipment = {\n shipmentId,\n exporterEmail: raw.exporterEmail || 'user@example.com',\n exporterName: raw.exporterName || 'Exporter',\n originCountry: (raw.originCountry || 'CN').toUpperCase(),\n destinationCountry: (raw.destinationCountry || 'AU').toUpperCase(),\n incoterms: (raw.incoterms || 'FOB').toUpperCase(),\n declaredValue: parseFloat(raw.declaredValue) || 0,\n currency: (raw.currency || 'USD').toUpperCase(),\n goodsDescription: raw.goodsDescription || 'General Merchandise',\n transportMode: (raw.transportMode || 'SEA').toUpperCase(),\n totalDocuments: finalDocs.length,\n caseRunId: `RUN-${Date.now()}-${Math.random().toString(36).substr(2,5).toUpperCase()}`,\n registeredAt: new Date().toISOString()\n};\n\nreturn finalDocs.map(doc => ({\n json: {\n shipment,\n document: {\n docId: `DOC-${Math.random().toString(36).substr(2,8).toUpperCase()}`,\n name: doc.name || 'Unnamed',\n declaredType: (doc.type || 'unknown').toLowerCase().replace(/\\s+/g, '_'),\n driveFileId: doc.driveFileId || doc.id || null,\n url: doc.url || null\n }\n }\n}));"
},
"typeVersion": 2
},
{
"id": "4cbc3adf-22a5-45be-8cce-492cc0c545eb",
"name": "Fetch Document from Drive",
"type": "n8n-nodes-base.googleDrive",
"position": [
1600,
832
],
"parameters": {
"fileId": {
"__rl": true,
"mode": "id",
"value": "={{ $json.document.driveFileId }}"
},
"options": {},
"operation": "download"
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3,
"continueOnFail": true
},
{
"id": "31926704-5e1f-4bf4-8577-0c08e8e93fbe",
"name": "Extract Text from Document",
"type": "n8n-nodes-base.extractFromFile",
"position": [
1824,
832
],
"parameters": {
"options": {},
"operation": "pdf"
},
"typeVersion": 1,
"continueOnFail": true
},
{
"id": "95ae7bcd-d338-4a18-bcff-8e766c4b0ef2",
"name": "Classify Type & Extract Key Fields",
"type": "n8n-nodes-base.code",
"position": [
2048,
832
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const meta = $('Register Shipment Case').item.json;\nconst rawText = $('Extract Text from Document').item?.json?.text || 'EXTRACTION_FAILED';\nconst text = rawText.toLowerCase();\n\n// \u2500\u2500 Document Type Detection \u2500\u2500\nconst detectedType = (() => {\n if (text.includes('commercial invoice') || (text.includes('invoice') && text.includes('unit price'))) return 'COMMERCIAL_INVOICE';\n if (text.includes('packing list') || (text.includes('net weight') && text.includes('gross weight') && text.includes('cartons'))) return 'PACKING_LIST';\n if (text.includes('bill of lading') || text.includes('b/l no') || text.includes('shipper') && text.includes('notify party')) return 'BILL_OF_LADING';\n if (text.includes('airway bill') || text.includes('awb') || text.includes('air waybill')) return 'AIRWAY_BILL';\n if (text.includes('certificate of origin') || text.includes('country of origin') && text.includes('certif')) return 'CERTIFICATE_OF_ORIGIN';\n if (text.includes('dangerous goods') || text.includes('imdg') || text.includes('un number')) return 'DANGEROUS_GOODS_DECLARATION';\n if (text.includes('phytosanitary') || text.includes('plant health') || text.includes('quarantine')) return 'PHYTOSANITARY_CERTIFICATE';\n if (text.includes('insurance') && (text.includes('marine') || text.includes('cargo'))) return 'INSURANCE_CERTIFICATE';\n if (text.includes('letter of credit') || text.includes('l/c no') || text.includes('documentary credit')) return 'LETTER_OF_CREDIT';\n if (text.includes('export licence') || text.includes('export permit') || text.includes('export authorization')) return 'EXPORT_LICENCE';\n if (text.includes('material safety') || text.includes('msds') || text.includes('safety data sheet')) return 'MSDS';\n if (text.includes('fumigation') || text.includes('methyl bromide') || text.includes('heat treatment')) return 'FUMIGATION_CERTIFICATE';\n if (text.includes('customs entry') || text.includes('import declaration') || text.includes('entry number')) return 'CUSTOMS_ENTRY';\n return meta.document.declaredType?.toUpperCase().replace(/\\s+/g,'_') || 'UNKNOWN';\n})();\n\n// \u2500\u2500 Field Extraction for Cross-Document Validation \u2500\u2500\nconst extractField = (patterns, src) => {\n for (const p of patterns) {\n const m = src.match(p);\n if (m) return m[1]?.trim() || m[0]?.trim();\n }\n return null;\n};\n\nconst fullText = rawText.substring(0, 14000);\n\n// Extract key values that should be consistent across docs\nconst extractedFields = {\n invoiceNumber: extractField([/invoice\\s*(?:no\\.?|number)[:\\s#]*([A-Z0-9\\-\\/]+)/i, /inv[\\s#.:]*([A-Z0-9\\-\\/]{4,20})/i], fullText),\n declaredValue: extractField([/(?:total|invoice)\\s*(?:amount|value)[:\\s]*([\\d,]+\\.?\\d*)/i, /(?:USD|EUR|AUD|GBP|CNY)[\\s]*([\\d,]+\\.?\\d*)/i], fullText),\n currency: extractField([/\\b(USD|EUR|AUD|GBP|CNY|JPY|SGD|HKD)\\b/i], fullText),\n grossWeight: extractField([/gross\\s*weight[:\\s]*([\\d,]+\\.?\\d*\\s*(?:kg|lbs|mt)?)/i], fullText),\n netWeight: extractField([/net\\s*weight[:\\s]*([\\d,]+\\.?\\d*\\s*(?:kg|lbs|mt)?)/i], fullText),\n numberOfPackages: extractField([/(?:total\\s*)?(?:packages?|cartons?|boxes?|pieces?)[:\\s]*([\\d,]+)/i], fullText),\n hsCode: extractField([/hs\\s*(?:code|tariff)[:\\s#]*([\\d\\.]{6,12})/i, /tariff\\s*(?:code|heading)[:\\s]*([\\d\\.]{6,12})/i], fullText),\n originCountry: extractField([/country\\s*of\\s*origin[:\\s]*([A-Za-z ]+?)(?:\\n|,|$)/i], fullText),\n shipperName: extractField([/shipper[:\\s]*([A-Z][A-Za-z\\s&.,]{3,50})/i], fullText),\n consigneeName: extractField([/consignee[:\\s]*([A-Z][A-Za-z\\s&.,]{3,50})/i], fullText),\n portOfLoading: extractField([/port\\s*of\\s*(?:loading|origin|departure)[:\\s]*([A-Za-z ,]+?)(?:\\n|$)/i], fullText),\n portOfDischarge: extractField([/port\\s*of\\s*(?:discharge|destination|delivery)[:\\s]*([A-Za-z ,]+?)(?:\\n|$)/i], fullText),\n incoterms: extractField([/\\b(EXW|FCA|FAS|FOB|CFR|CIF|CPT|CIP|DPU|DAP|DDP)\\b/i], fullText),\n blNumber: extractField([/(?:b\\/l|bill\\s*of\\s*lading)\\s*(?:no\\.?|number)[:\\s#]*([A-Z0-9\\-\\/]+)/i], fullText)\n};\n\n// Expiry / validity check\nconst datePatterns = [\n /valid\\s*(?:until|to|through)[:\\s]*([\\d]{1,2}[\\/-][\\d]{1,2}[\\/-][\\d]{2,4})/gi,\n /expir(?:y|es|ed)[:\\s]*([\\d]{1,2}[\\/-][\\d]{1,2}[\\/-][\\d]{2,4})/gi\n];\nconst validityDates = [];\nfor (const p of datePatterns) {\n const matches = rawText.matchAll(p);\n for (const m of matches) validityDates.push(m[1]);\n}\n\nreturn {\n json: {\n shipment: meta.shipment,\n document: {\n ...meta.document,\n detectedType,\n extractedFields,\n validityDates,\n fullText: rawText.substring(0, 12000),\n textLength: rawText.length,\n extractionStatus: rawText === 'EXTRACTION_FAILED' ? 'FAILED' : 'SUCCESS'\n }\n }\n};"
},
"typeVersion": 2
},
{
"id": "7ad92b81-036c-4569-82a6-a40437034706",
"name": "Cross-Document Consistency Engine",
"type": "n8n-nodes-base.code",
"position": [
2272,
832
],
"parameters": {
"jsCode": "const items = $input.all();\nif (!items.length) return [];\n\nconst shipment = items[0].json.shipment;\n\n// Group extracted fields by document\nconst docs = items.map(i => i.json);\n\n// Gather all field values across documents for comparison\nconst fieldMap = {};\nconst fieldKeys = ['declaredValue','currency','grossWeight','netWeight','numberOfPackages','hsCode','originCountry','shipperName','consigneeName','portOfLoading','portOfDischarge','incoterms'];\n\nfor (const key of fieldKeys) {\n fieldMap[key] = docs\n .filter(d => d.document.extractedFields?.[key])\n .map(d => ({\n docId: d.document.docId,\n docName: d.document.name,\n docType: d.document.detectedType,\n value: d.document.extractedFields[key]\n }));\n}\n\n// Detect mismatches \u2014 values found in 2+ docs that differ\nconst mismatches = [];\nconst normalise = v => (v || '').toString().toLowerCase().replace(/[,\\s]/g,'').trim();\n\nfor (const key of fieldKeys) {\n const entries = fieldMap[key];\n if (entries.length < 2) continue;\n const uniqueVals = [...new Set(entries.map(e => normalise(e.value)))];\n if (uniqueVals.length > 1) {\n mismatches.push({\n field: key,\n severity: ['declaredValue','hsCode','currency'].includes(key) ? 'HIGH' : 'MEDIUM',\n values: entries.map(e => `${e.docName}: ${e.value}`),\n description: `Inconsistent \"${key}\" across documents`\n });\n }\n}\n\n// Check declared value vs shipment header\nconst invoiceDoc = docs.find(d => d.document.detectedType === 'COMMERCIAL_INVOICE');\nif (invoiceDoc && shipment.declaredValue > 0) {\n const invoiceVal = parseFloat((invoiceDoc.document.extractedFields.declaredValue || '0').replace(/[,]/g,''));\n if (invoiceVal > 0 && Math.abs(invoiceVal - shipment.declaredValue) / shipment.declaredValue > 0.05) {\n mismatches.push({\n field: 'declaredValueVsHeader',\n severity: 'HIGH',\n values: [`Shipment header: ${shipment.declaredValue} ${shipment.currency}`, `Invoice extracted: ${invoiceVal}`],\n description: 'Invoice value differs from declared shipment value by more than 5%'\n });\n }\n}\n\n// Mandatory document checklist by destination\nconst MANDATORY_BY_DEST = {\n AU: ['COMMERCIAL_INVOICE','PACKING_LIST','BILL_OF_LADING','CERTIFICATE_OF_ORIGIN'],\n US: ['COMMERCIAL_INVOICE','PACKING_LIST','BILL_OF_LADING'],\n EU: ['COMMERCIAL_INVOICE','PACKING_LIST','BILL_OF_LADING','CERTIFICATE_OF_ORIGIN'],\n UK: ['COMMERCIAL_INVOICE','PACKING_LIST','BILL_OF_LADING'],\n DEFAULT: ['COMMERCIAL_INVOICE','PACKING_LIST','BILL_OF_LADING']\n};\nconst required = MANDATORY_BY_DEST[shipment.destinationCountry] || MANDATORY_BY_DEST.DEFAULT;\nconst presentTypes = docs.map(d => d.document.detectedType);\nconst missingDocs = required.filter(r => !presentTypes.includes(r));\n\n// Return all items enriched with cross-check context\nreturn items.map(item => ({\n json: {\n ...item.json,\n crossCheck: {\n mismatches,\n missingMandatoryDocs: missingDocs,\n totalDocsInPackage: docs.length,\n detectedDocTypes: presentTypes,\n crossCheckRunAt: new Date().toISOString()\n }\n }\n}));"
},
"typeVersion": 2
},
{
"id": "5700dfeb-2b5d-4abc-986e-62cf28dc979c",
"name": "AI Customs Compliance Validator",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
2496,
832
],
"parameters": {
"text": "=You are a licensed customs broker and trade compliance specialist with 20+ years of experience in international trade law (WTO, Incoterms 2020, HS Convention, WCO standards). Conduct a rigorous customs compliance review of this shipping document.\n\n**Shipment Context:**\n- Shipment ID: {{ $json.shipment.shipmentId }}\n- Route: {{ $json.shipment.originCountry }} \u2192 {{ $json.shipment.destinationCountry }}\n- Transport Mode: {{ $json.shipment.transportMode }}\n- Incoterms: {{ $json.shipment.incoterms }}\n- Declared Value: {{ $json.shipment.declaredValue }} {{ $json.shipment.currency }}\n- Goods Description: {{ $json.shipment.goodsDescription }}\n\n**Document Under Review:**\n- Document ID: {{ $json.document.docId }}\n- Document Name: {{ $json.document.name }}\n- Declared Type: {{ $json.document.declaredType }}\n- AI Detected Type: {{ $json.document.detectedType }}\n- Extraction Status: {{ $json.document.extractionStatus }}\n\n**Extracted Fields:**\n{{ JSON.stringify($json.document.extractedFields, null, 2) }}\n\n**Validity Dates Found:** {{ JSON.stringify($json.document.validityDates) }}\n\n**Cross-Document Mismatches Detected:**\n{{ JSON.stringify($json.crossCheck.mismatches, null, 2) }}\n\n**Missing Mandatory Documents:**\n{{ JSON.stringify($json.crossCheck.missingMandatoryDocs, null, 2) }}\n\n**Full Document Text (first 12,000 chars):**\n{{ $json.document.fullText }}\n\n---\n\n**Validation Requirements by Document Type:**\n\nCOMMERCIAL_INVOICE: Exporter/importer full details (name, address, country), invoice number and date, HS code per line item, unit price and total value, currency, quantity and UOM, country of origin, incoterms, payment terms, goods description matching HS code, shipper/consignee match with B/L.\n\nPACKING_LIST: Marks and numbers, number of packages, description per package, net/gross weight per item and total, dimensions/volume, HS code, matches invoice quantities and descriptions.\n\nBILL_OF_LADING: Shipper and consignee full details, notify party, vessel name and voyage, port of loading and discharge, container numbers and seal numbers, cargo description, gross weight, freight terms (prepaid/collect), B/L number, issue date and place, signature.\n\nCERTIFICATE_OF_ORIGIN: Issuing authority (chamber of commerce or government), exporter name and country, consignee, description of goods, HS code, origin criteria (wholly obtained / substantial transformation), authorised signature and stamp, reference to trade agreement if preferential.\n\nDANGEROUS_GOODS_DECLARATION: UN number, proper shipping name, hazard class, packing group, net/gross quantity, emergency contact, IMDG/IATA compliance, shipper's declaration.\n\nPHYTOSANITARY_CERTIFICATE: Issuing authority, treatment details, botanical name (if applicable), validity date, destination country requirements.\n\n**Destination-Specific Rules:**\n- AU: DAFF biosecurity declaration required for organic/food items; WET, GST applicability; ABN of importer; AUSTRADE preferential rates check for ChAFTA (China), JAEPA (Japan), KAFTA (Korea)\n- US: ACE filing, ISF 10+2 for ocean freight, PGA requirements, HTSUS classification, ADD/CVD duty checks\n- EU: EORI number required, ICS2 for air freight, preferential origin documentation for trade agreements, REACH compliance for chemicals\n- UK: CHIEF/CDS commodity codes, UK Global Tariff, import VAT, Rules of Origin post-Brexit\n\n**Response Format (JSON only, no markdown):**\n{\n \"docId\": \"{{ $json.document.docId }}\",\n \"documentName\": \"{{ $json.document.name }}\",\n \"confirmedType\": \"DOCUMENT_TYPE\",\n \"complianceStatus\": \"CLEAR | HOLD | REJECT\",\n \"riskLevel\": \"LOW | MEDIUM | HIGH | CRITICAL\",\n \"confidenceScore\": 91,\n \"isExpiredOrInvalid\": false,\n \"expiryIssue\": null,\n \"hsCodeFound\": \"8542.31\",\n \"hsCodeValid\": true,\n \"hsCodeSuggestion\": null,\n \"restrictedGoodsFlag\": false,\n \"sanctionRiskFlag\": false,\n \"restrictedGoodsDetail\": null,\n \"passedChecks\": [\"check description\"],\n \"errors\": [\n { \"field\": \"field name\", \"issue\": \"description\", \"severity\": \"CRITICAL|HIGH|MEDIUM|LOW\", \"fix\": \"how to correct\" }\n ],\n \"warnings\": [\"non-blocking cautions\"],\n \"missingFields\": [\"required field not found\"],\n \"crossDocumentIssues\": [\"issues flagged from cross-doc comparison\"],\n \"dutyImplications\": \"brief note on duty rate impact if HS or origin issues found\",\n \"documentSummary\": \"2-sentence plain-English compliance summary\",\n \"requiresBrokerReview\": false,\n \"canDispatch\": true\n}",
"options": {
"systemMessage": "You are a customs compliance expert. Return JSON only \u2014 no markdown, no code blocks, no preamble. Be specific \u2014 cite field names, values, and regulation references. Every error must include a concrete fix instruction. Flag any potential duty undervaluation, tariff classification errors, or restricted goods as CRITICAL regardless of other factors."
},
"promptType": "define"
},
"typeVersion": 1.6
},
{
"id": "3e56910b-ebdd-4c74-b9d6-d6a57832a715",
"name": "Claude AI Model",
"type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
"position": [
2568,
1056
],
"parameters": {
"model": "=claude-sonnet-4-20250514",
"options": {
"temperature": 0.05
}
},
"credentials": {
"anthropicApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "fbb836a5-cd51-439a-81ff-5c1ed3eab09f",
"name": "Parse AI Compliance Results",
"type": "n8n-nodes-base.code",
"position": [
2848,
832
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const aiResp = $input.item.json;\nlet aiText = aiResp.response || aiResp.output || aiResp.text || '';\nif (aiResp.content && Array.isArray(aiResp.content)) aiText = aiResp.content[0]?.text || '';\n\nconst clean = aiText.replace(/```json\\s*/g,'').replace(/```\\s*/g,'').trim();\nlet validation;\ntry {\n validation = JSON.parse(clean);\n} catch(e) {\n const m = clean.match(/\\{[\\s\\S]*\\}/);\n try { validation = m ? JSON.parse(m[0]) : null; } catch(e2) { validation = null; }\n if (!validation) {\n validation = {\n docId: 'PARSE_ERROR', complianceStatus: 'HOLD', riskLevel: 'HIGH',\n confidenceScore: 0, errors: [{ field: 'system', issue: 'AI response parse failed', severity: 'HIGH', fix: 'Retry or review manually' }],\n passedChecks: [], warnings: [], missingFields: [], crossDocumentIssues: [],\n documentSummary: 'Validation processing error \u2014 manual review required.',\n requiresBrokerReview: true, canDispatch: false\n };\n }\n}\n\nconst up = $('Cross-Document Consistency Engine').item.json;\nreturn {\n json: {\n shipment: up.shipment,\n document: up.document,\n crossCheck: up.crossCheck,\n validation,\n validatedAt: new Date().toISOString()\n }\n};"
},
"typeVersion": 2
},
{
"id": "74a857b2-8eaf-4e31-82fb-cb921139bfab",
"name": "Aggregate Shipment Validation Report",
"type": "n8n-nodes-base.code",
"position": [
3072,
832
],
"parameters": {
"jsCode": "const items = $input.all();\nif (!items.length) return [{ json: { error: 'No results to aggregate' } }];\n\nconst shipment = items[0].json.shipment;\nconst crossCheck = items[0].json.crossCheck;\n\nconst docResults = items.map(i => ({\n docId: i.json.document.docId,\n name: i.json.document.name,\n confirmedType: i.json.validation.confirmedType || i.json.document.detectedType,\n complianceStatus: i.json.validation.complianceStatus,\n riskLevel: i.json.validation.riskLevel,\n isExpiredOrInvalid: i.json.validation.isExpiredOrInvalid,\n hsCodeFound: i.json.validation.hsCodeFound,\n hsCodeValid: i.json.validation.hsCodeValid,\n hsCodeSuggestion: i.json.validation.hsCodeSuggestion,\n restrictedGoodsFlag: i.json.validation.restrictedGoodsFlag,\n sanctionRiskFlag: i.json.validation.sanctionRiskFlag,\n errors: i.json.validation.errors || [],\n warnings: i.json.validation.warnings || [],\n missingFields: i.json.validation.missingFields || [],\n passedChecks: i.json.validation.passedChecks || [],\n dutyImplications: i.json.validation.dutyImplications,\n documentSummary: i.json.validation.documentSummary,\n requiresBrokerReview: i.json.validation.requiresBrokerReview,\n canDispatch: i.json.validation.canDispatch\n}));\n\n// Overall shipment status\nconst hasReject = docResults.some(d => d.complianceStatus === 'REJECT');\nconst hasHold = docResults.some(d => d.complianceStatus === 'HOLD');\nconst hasCritical = docResults.some(d => d.riskLevel === 'CRITICAL');\nconst hasRestricted = docResults.some(d => d.restrictedGoodsFlag);\nconst hasSanction = docResults.some(d => d.sanctionRiskFlag);\nconst missingDocs = crossCheck.missingMandatoryDocs || [];\nconst crossMismatches = crossCheck.mismatches || [];\n\nconst overallStatus = (hasReject || hasCritical || hasRestricted || hasSanction || missingDocs.length > 0)\n ? 'REJECT'\n : hasHold\n ? 'HOLD'\n : 'CLEAR';\n\nconst totalErrors = docResults.reduce((s, d) => s + d.errors.length, 0);\nconst criticalErrors = docResults.reduce((s, d) => s + d.errors.filter(e => e.severity === 'CRITICAL').length, 0);\nconst highErrors = docResults.reduce((s, d) => s + d.errors.filter(e => e.severity === 'HIGH').length, 0);\n\n// Flat remediation list\nconst allFixes = [];\nif (missingDocs.length > 0) allFixes.push(`MISSING DOCS: Provide \u2014 ${missingDocs.join(', ')}`);\nfor (const m of crossMismatches) allFixes.push(`MISMATCH [${m.severity}]: ${m.description} \u2014 Values: ${m.values.join(' | ')}`);\nfor (const doc of docResults) {\n for (const err of doc.errors) {\n allFixes.push(`[${doc.name}] ${err.field}: ${err.issue} \u2192 FIX: ${err.fix}`);\n }\n}\n\nreturn [{\n json: {\n shipment,\n overallStatus,\n summary: {\n totalDocuments: docResults.length,\n cleared: docResults.filter(d => d.complianceStatus === 'CLEAR').length,\n held: docResults.filter(d => d.complianceStatus === 'HOLD').length,\n rejected: docResults.filter(d => d.complianceStatus === 'REJECT').length,\n expired: docResults.filter(d => d.isExpiredOrInvalid).length,\n totalErrors, criticalErrors, highErrors,\n crossDocMismatches: crossMismatches.length,\n missingMandatoryDocs: missingDocs.length,\n restrictedGoodsFlag: hasRestricted,\n sanctionRiskFlag: hasSanction\n },\n missingMandatoryDocs: missingDocs,\n crossDocumentMismatches: crossMismatches,\n documentResults: docResults,\n allRemediationSteps: allFixes,\n requiresBrokerReview: docResults.some(d => d.requiresBrokerReview) || hasCritical || hasRestricted,\n canDispatch: overallStatus === 'CLEAR',\n aggregatedAt: new Date().toISOString()\n }\n}];"
},
"typeVersion": 2
},
{
"id": "be953067-b361-4d44-bb36-1866b81e2f33",
"name": "Route by Shipment Risk Level",
"type": "n8n-nodes-base.switch",
"position": [
3296,
800
],
"parameters": {
"mode": "expression",
"output": "={{ $json.overallStatus }}"
},
"typeVersion": 3.1
},
{
"id": "38a2f2cd-13f3-4dfe-a33f-4905442857ae",
"name": "Alert Logistics Team on Slack",
"type": "n8n-nodes-base.httpRequest",
"position": [
3520,
448
],
"parameters": {
"url": "https://slack.com/api/chat.postMessage",
"method": "POST",
"options": {
"timeout": 10000
},
"jsonBody": "={\n \"channel\": \"{{ $json.overallStatus === 'REJECT' ? '#customs-critical' : '#customs-review' }}\",\n \"text\": \"\ud83d\udea2 Customs Check: {{ $json.overallStatus }} \u2014 {{ $json.shipment.shipmentId }}\",\n \"blocks\": [\n { \"type\": \"header\", \"text\": { \"type\": \"plain_text\", \"text\": \"{{ $json.overallStatus === 'REJECT' ? '\ud83d\udd34 REJECT' : $json.overallStatus === 'HOLD' ? '\ud83d\udfe1 HOLD' : '\ud83d\udfe2 CLEAR' }} \u2014 Shipment {{ $json.shipment.shipmentId }}\" } },\n {\n \"type\": \"section\",\n \"fields\": [\n { \"type\": \"mrkdwn\", \"text\": \"*Route:*\\n{{ $json.shipment.originCountry }} \u2192 {{ $json.shipment.destinationCountry }}\" },\n { \"type\": \"mrkdwn\", \"text\": \"*Goods:*\\n{{ $json.shipment.goodsDescription }}\" },\n { \"type\": \"mrkdwn\", \"text\": \"*Declared Value:*\\n{{ $json.shipment.currency }} {{ $json.shipment.declaredValue.toLocaleString() }}\" },\n { \"type\": \"mrkdwn\", \"text\": \"*Incoterms:*\\n{{ $json.shipment.incoterms }}\" },\n { \"type\": \"mrkdwn\", \"text\": \"*Total Errors:*\\n{{ $json.summary.totalErrors }} ({{ $json.summary.criticalErrors }} critical)\" },\n { \"type\": \"mrkdwn\", \"text\": \"*Missing Docs:*\\n{{ $json.missingMandatoryDocs.length > 0 ? $json.missingMandatoryDocs.join(', ') : 'None' }}\" }\n ]\n },\n {{ $json.summary.restrictedGoodsFlag || $json.summary.sanctionRiskFlag ? JSON.stringify({ type: 'section', text: { type: 'mrkdwn', text: '\u26a0\ufe0f *COMPLIANCE ALERT:* ' + ($json.summary.restrictedGoodsFlag ? 'Restricted goods detected. ' : '') + ($json.summary.sanctionRiskFlag ? 'Sanction risk flag raised.' : '') } }) + ',' : '' }}\n {\n \"type\": \"section\",\n \"text\": { \"type\": \"mrkdwn\", \"text\": \"*Top Fixes Required:*\\n{{ $json.allRemediationSteps.slice(0,3).map((s,i) => (i+1)+'. '+s).join('\\\\n') || 'No issues found' }}\" }\n },\n { \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"*Broker Review Required:* {{ $json.requiresBrokerReview ? '\ud83d\udd34 Yes \u2014 assign immediately' : '\ud83d\udfe2 No' }}\" } }\n ]\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "slackApi"
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.2,
"continueOnFail": true
},
{
"id": "e4a458e2-6d10-4bb8-bc45-20b2c1231d60",
"name": "Email Validation Report to Exporter",
"type": "n8n-nodes-base.emailSend",
"position": [
3520,
640
],
"parameters": {
"html": "=<html><body style=\"font-family:Arial,sans-serif;max-width:700px;margin:0 auto;padding:24px;color:#222;background:#f5f5f5;\">\n<div style=\"background:linear-gradient(135deg,#0f3460 0%,#16213e 100%);padding:28px;border-radius:10px 10px 0 0;\">\n <h1 style=\"color:white;margin:0;font-size:1.4em;\">\ud83d\udea2 Customs Document Pre-Check Report</h1>\n <p style=\"color:#aaa;margin:8px 0 0;\">{{ $json.shipment.shipmentId }} • {{ new Date($json.aggregatedAt).toLocaleString('en-AU') }}</p>\n</div>\n<div style=\"background:white;padding:24px;border:1px solid #ddd;\">\n <table style=\"width:100%;border-collapse:collapse;margin-bottom:16px;\">\n <tr><td style=\"padding:6px;color:#555;\"><strong>Route</strong></td><td style=\"padding:6px;\">{{ $json.shipment.originCountry }} \u2192 {{ $json.shipment.destinationCountry }}</td><td style=\"padding:6px;color:#555;\"><strong>Mode</strong></td><td style=\"padding:6px;\">{{ $json.shipment.transportMode }}</td></tr>\n <tr style=\"background:#f9f9f9;\"><td style=\"padding:6px;color:#555;\"><strong>Goods</strong></td><td style=\"padding:6px;\" colspan=\"3\">{{ $json.shipment.goodsDescription }}</td></tr>\n <tr><td style=\"padding:6px;color:#555;\"><strong>Value</strong></td><td style=\"padding:6px;\">{{ $json.shipment.currency }} {{ $json.shipment.declaredValue.toLocaleString() }}</td><td style=\"padding:6px;color:#555;\"><strong>Incoterms</strong></td><td style=\"padding:6px;\">{{ $json.shipment.incoterms }}</td></tr>\n </table>\n <div style=\"background:{{ $json.overallStatus === 'CLEAR' ? '#d4edda' : $json.overallStatus === 'REJECT' ? '#f8d7da' : '#fff3cd' }};border:2px solid {{ $json.overallStatus === 'CLEAR' ? '#28a745' : $json.overallStatus === 'REJECT' ? '#e94560' : '#ffc107' }};border-radius:8px;padding:16px;margin:16px 0;\">\n <h2 style=\"margin:0;color:{{ $json.overallStatus === 'CLEAR' ? '#155724' : $json.overallStatus === 'REJECT' ? '#721c24' : '#856404' }};\">{{ $json.overallStatus === 'CLEAR' ? '\u2705 CLEAR \u2014 Ready for dispatch' : $json.overallStatus === 'REJECT' ? '\ud83d\udd34 REJECT \u2014 Do not dispatch' : '\ud83d\udfe1 HOLD \u2014 Corrections required' }}</h2>\n <p style=\"margin:6px 0 0;color:#555;\">{{ $json.summary.totalDocuments }} docs reviewed • {{ $json.summary.totalErrors }} errors • {{ $json.summary.criticalErrors }} critical • {{ $json.summary.crossDocMismatches }} cross-doc mismatches</p>\n </div>\n {{ $json.missingMandatoryDocs.length > 0 ? '<div style=\"background:#f8d7da;border:1px solid #f5c6cb;border-radius:6px;padding:14px;margin:12px 0;\"><strong style=\"color:#721c24;\">\u26d4 Missing Mandatory Documents:</strong><ul>' + $json.missingMandatoryDocs.map(d => '<li>' + d.replace(/_/g,' ') + '</li>').join('') + '</ul></div>' : '' }}\n {{ $json.crossDocumentMismatches.length > 0 ? '<div style=\"background:#fff3cd;border:1px solid #ffc107;border-radius:6px;padding:14px;margin:12px 0;\"><strong>\u26a0\ufe0f Cross-Document Mismatches:</strong><ul>' + $json.crossDocumentMismatches.map(m => '<li><strong>' + m.field + '</strong> \u2014 ' + m.values.join(' | ') + '</li>').join('') + '</ul></div>' : '' }}\n <h3 style=\"color:#0f3460;border-bottom:2px solid #e94560;padding-bottom:6px;\">Document Results</h3>\n {{ $json.documentResults.map(doc => '<div style=\"border:1px solid #ddd;border-left:5px solid ' + (doc.complianceStatus === 'CLEAR' ? '#28a745' : doc.riskLevel === 'CRITICAL' ? '#e94560' : '#ffa500') + ';border-radius:4px;padding:12px;margin:10px 0;\"><strong>' + doc.name + '</strong> <span style=\"background:' + (doc.complianceStatus === 'CLEAR' ? '#28a745' : doc.complianceStatus === 'REJECT' ? '#e94560' : '#ffa500') + ';color:white;padding:2px 8px;border-radius:3px;font-size:0.8em;\">' + doc.complianceStatus + '</span>' + (doc.hsCodeFound ? ' <span style=\"background:#17a2b8;color:white;padding:2px 8px;border-radius:3px;font-size:0.8em;\">HS: ' + doc.hsCodeFound + (doc.hsCodeValid ? ' \u2713' : ' \u2717') + '</span>' : '') + '<p style=\"margin:6px 0;font-size:0.9em;color:#555;\">' + doc.documentSummary + '</p>' + (doc.errors.length > 0 ? '<ul style=\"margin:4px 0;\">' + doc.errors.map(e => '<li><strong>[' + e.severity + ']</strong> ' + e.field + ': ' + e.issue + ' \u2192 ' + e.fix + '</li>').join('') + '</ul>' : '') + '</div>').join('') }}\n {{ $json.allRemediationSteps.length > 0 ? '<h3>Required Corrections</h3><ol>' + $json.allRemediationSteps.map(s => '<li style=\"margin:6px 0;\">' + s + '</li>').join('') + '</ol>' : '' }}\n <p style=\"font-size:0.8em;color:#999;margin-top:20px;\">This is an automated pre-check. It does not guarantee customs clearance. Consult a licensed customs broker for final advice.</p>\n</div>\n<div style=\"background:#0f3460;padding:14px 24px;border-radius:0 0 10px 10px;text-align:center;\">\n <p style=\"color:#aaa;font-size:0.8em;margin:0;\">AI Customs Clearance Document Checker</p>\n</div>\n</body></html>",
"options": {
"appendAttribution": false
},
"subject": "={{ '\ud83d\udea2 Customs Pre-Check: ' + $json.overallStatus + ' \u2014 Shipment ' + $json.shipment.shipmentId + ' (' + $json.shipment.originCountry + '\u2192' + $json.shipment.destinationCountry + ')' }}",
"toEmail": "={{ $json.shipment.exporterEmail }}",
"fromEmail": "="
},
"credentials": {
"smtp": {
"name": "<your credential>"
}
},
"typeVersion": 2.1,
"continueOnFail": true
},
{
"id": "b9d2b6ab-a224-4652-b11c-9cc51e6763b5",
"name": "Create Jira Compliance Issue",
"type": "n8n-nodes-base.httpRequest",
"position": [
3520,
1024
],
"parameters": {
"url": "https://YOUR_JIRA_DOMAIN.atlassian.net/rest/api/3/issue",
"method": "POST",
"options": {
"timeout": 15000
},
"jsonBody": "={\n \"fields\": {\n \"project\": { \"key\": \"CUSTOMS\" },\n \"issuetype\": { \"name\": \"Compliance Issue\" },\n \"summary\": \"[{{ $json.overallStatus }}] Customs Doc Errors \u2014 {{ $json.shipment.shipmentId }} ({{ $json.shipment.originCountry }}\u2192{{ $json.shipment.destinationCountry }})\",\n \"priority\": { \"name\": \"{{ $json.overallStatus === 'REJECT' ? 'Highest' : 'High' }}\" },\n \"description\": {\n \"type\": \"doc\", \"version\": 1,\n \"content\": [\n { \"type\": \"paragraph\", \"content\": [{ \"type\": \"text\", \"text\": \"Shipment: {{ $json.shipment.shipmentId }} | Route: {{ $json.shipment.originCountry }} \u2192 {{ $json.shipment.destinationCountry }}\" }] },\n { \"type\": \"paragraph\", \"content\": [{ \"type\": \"text\", \"text\": \"Goods: {{ $json.shipment.goodsDescription }} | Value: {{ $json.shipment.currency }} {{ $json.shipment.declaredValue }}\" }] },\n { \"type\": \"paragraph\", \"content\": [{ \"type\": \"text\", \"text\": \"Total Errors: {{ $json.summary.totalErrors }} | Critical: {{ $json.summary.criticalErrors }} | Missing Docs: {{ $json.summary.missingMandatoryDocs }}\" }] },\n { \"type\": \"paragraph\", \"content\": [{ \"type\": \"text\", \"text\": \"Restricted Goods: {{ $json.summary.restrictedGoodsFlag }} | Sanction Risk: {{ $json.summary.sanctionRiskFlag }}\" }] },\n { \"type\": \"paragraph\", \"content\": [{ \"type\": \"text\", \"text\": \"Top Fix: {{ $json.allRemediationSteps[0] || 'See full report' }}\" }] }\n ]\n },\n \"labels\": [\"customs-compliance\", \"{{ $json.overallStatus.toLowerCase() }}\", \"{{ $json.shipment.destinationCountry.toLowerCase() }}\"]\n }\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "jiraSoftwareCloudApi"
},
"credentials": {
"jiraSoftwareCloudApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.2,
"continueOnFail": true
},
{
"id": "0065a13e-4562-435d-b6d1-9704632d9d3b",
"name": "Update Shipment Tracker in Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
3520,
832
],
"parameters": {
"columns": {
"value": {},
"schema": [],
"mappingMode": "autoMapInputData",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": true
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "id",
"value": "=YOUR_SHEET_TAB_ID"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "=YOUR_GOOGLE_SHEET_ID"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.5,
"continueOnFail": true
},
{
"id": "8944915f-61f3-4c2c-82ec-4cb815d4d9f2",
"name": "Build Final Compliance Response",
"type": "n8n-nodes-base.code",
"position": [
3744,
736
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const d = $input.item.json;\nreturn {\n json: {\n success: true,\n shipmentId: d.shipment.shipmentId,\n route: `${d.shipment.originCountry} \u2192 ${d.shipment.destinationCountry}`,\n overallStatus: d.overallStatus,\n canDispatch: d.canDispatch,\n requiresBrokerReview: d.requiresBrokerReview,\n summary: d.summary,\n missingMandatoryDocs: d.missingMandatoryDocs,\n crossDocumentMismatches: d.crossDocumentMismatches,\n documentResults: d.documentResults.map(doc => ({\n docId: doc.docId,\n name: doc.name,\n type: doc.confirmedType,\n status: doc.complianceStatus,\n riskLevel: doc.riskLevel,\n hsCode: doc.hsCodeFound,\n hsCodeValid: doc.hsCodeValid,\n errors: doc.errors,\n warnings: doc.warnings,\n summary: doc.documentSummary\n })),\n allRemediationSteps: d.allRemediationSteps,\n processedAt: d.aggregatedAt\n }\n};"
},
"typeVersion": 2
},
{
"id": "0d7ef775-c36e-4aa3-8c76-127b1c5f05ca",
"name": "Return Compliance Result to Caller",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
3968,
736
],
"parameters": {
"options": {
"responseHeaders": {
"entries": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json, null, 2) }}"
},
"typeVersion": 1
},
{
"id": "9a615d6e-409c-453e-8416-b83e87d54c1d",
"name": "Mark Shipment Cleared for Dispatch",
"type": "n8n-nodes-base.set",
"position": [
3520,
1216
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "s1",
"name": "status",
"type": "string",
"value": "CLEARED_FOR_DISPATCH"
},
{
"id": "s2",
"name": "shipmentId",
"type": "string",
"value": "={{ $json.shipment.shipmentId }}"
},
{
"id": "s3",
"name": "route",
"type": "string",
"value": "={{ $json.shipment.originCountry + ' \u2192 ' + $json.shipment.destinationCountry }}"
},
{
"id": "s4",
"name": "message",
"type": "string",
"value": "All documents passed customs compliance checks. Shipment cleared for dispatch."
},
{
"id": "s5",
"name": "clearedAt",
"type": "string",
"value": "={{ new Date().toISOString() }}"
}
]
}
},
"typeVersion": 3.4
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "46d65c68-b9cb-481d-9f16-d766aa468e55",
"connections": {
"Claude AI Model": {
"ai_languageModel": [
[
{
"node": "AI Customs Compliance Validator",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Register Shipment Case": {
"main": [
[
{
"node": "Fetch Document from Drive",
"type": "main",
"index": 0
}
]
]
},
"Fetch Document from Drive": {
"main": [
[
{
"node": "Extract Text from Document",
"type": "main",
"index": 0
}
]
]
},
"Extract Text from Document": {
"main": [
[
{
"node": "Classify Type & Extract Key Fields",
"type": "main",
"index": 0
}
]
]
},
"Receive Shipment Documents": {
"main": [
[
{
"node": "Register Shipment Case",
"type": "main",
"index": 0
}
]
]
},
"Watch Shipment Docs Folder": {
"main": [
[
{
"node": "Register Shipment Case",
"type": "main",
"index": 0
}
]
]
},
"Parse AI Compliance Results": {
"main": [
[
{
"node": "Aggregate Shipment Validation Report",
"type": "main",
"index": 0
}
]
]
},
"Create Jira Compliance Issue": {
"main": [
[
{
"node": "Build Final Compliance Response",
"type": "main",
"index": 0
}
]
]
},
"Route by Shipment Risk Level": {
"main": [
[
{
"node": "Email Validation Report to Exporter",
"type": "main",
"index": 0
},
{
"node": "Update Shipment Tracker in Sheets",
"type": "main",
"index": 0
},
{
"node": "Mark Shipment Cleared for Dispatch",
"type": "main",
"index": 0
}
],
[
{
"node": "Alert Logistics Team on Slack",
"type": "main",
"index": 0
},
{
"node": "Email Validation Report to Exporter",
"type": "main",
"index": 0
},
{
"node": "Create Jira Compliance Issue",
"type": "main",
"index": 0
},
{
"node": "Update Shipment Tracker in Sheets",
"type": "main",
"index": 0
}
],
[
{
"node": "Alert Logistics Team on Slack",
"type": "main",
"index": 0
},
{
"node": "Email Validation Report to Exporter",
"type": "main",
"index": 0
},
{
"node": "Create Jira Compliance Issue",
"type": "main",
"index": 0
},
{
"node": "Update Shipment Tracker in Sheets",
"type": "main",
"index": 0
}
]
]
},
"Alert Logistics Team on Slack": {
"main": [
[
{
"node": "Build Final Compliance Response",
"type": "main",
"index": 0
}
]
]
},
"AI Customs Compliance Validator": {
"main": [
[
{
"node": "Parse AI Compliance Results",
"type": "main",
"index": 0
}
]
]
},
"Build Final Compliance Response": {
"main": [
[
{
"node": "Return Compliance Result to Caller",
"type": "main",
"index": 0
}
]
]
},
"Cross-Document Consistency Engine": {
"main": [
[
{
"node": "AI Customs Compliance Validator",
"type": "main",
"index": 0
}
]
]
},
"Update Shipment Tracker in Sheets": {
"main": [
[
{
"node": "Build Final Compliance Response",
"type": "main",
"index": 0
}
]
]
},
"Classify Type & Extract Key Fields": {
"main": [
[
{
"node": "Cross-Document Consistency Engine",
"type": "main",
"index": 0
}
]
]
},
"Email Validation Report to Exporter": {
"main": [
[
{
"node": "Build Final Compliance Response",
"type": "main",
"index": 0
}
]
]
},
"Aggregate Shipment Validation Report": {
"main": [
[
{
"node": "Route by Shipment Risk Level",
"type": "main",
"index": 0
}
]
]
}
}
}
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.
anthropicApigoogleApigoogleDriveOAuth2ApijiraSoftwareCloudApislackApismtp
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow automates pre-dispatch customs document validation for international shipments. It ingests shipping document packages, extracts content from each file, uses Claude AI to cross-validate all documents for consistency, regulatory compliance, and HS code accuracy, then…
Source: https://n8n.io/workflows/13691/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Automatically transforms your travel photos and notes into beautiful journals, highlight reels, and review drafts using Claude's vision and language capabilities. Trip Completion Trigger - Webhook or
This workflow ingests property document packages submitted via webhook or monitored cloud storage, extracts text from each file, runs Claude AI to verify legal compliance, detect missing or expired do
Tired of grinding out YouTube content? This n8n workflow turns AI into your personal video factory—creating engaging, faceless shorts on autopilot. Perfect for creators, marketers, or side-hustlers lo
Faceless YouTube Generator. Uses httpRequest, limit, googleDrive, googleSheets. Webhook trigger; 49 nodes.
This workflow continuously monitors CVE databases, threat intelligence feeds, and public security advisories to surface emerging zero-day threats, correlates them against your registered infrastructur