DEV Community

Alex Kane
Alex Kane

Posted on

n8n for Maritime/Shipping SaaS Vendors: 5 Automations for IMO ISM Code, SOLAS, MARPOL, and CBP Compliance (Free Workflow JSON)

If you sell SaaS to shipping lines, port authorities, or ship management companies, your customers operate under one of the most complex compliance stacks in global industry — IMO ISM Code, SOLAS, MARPOL, STCW, MLC 2006, and US CBP C-TPAT. A Port State Control detention can idle a vessel for days; a MARPOL ORB gap can look like falsification under 18 U.S.C. §1001.

Here are five n8n workflows purpose-built for maritime and shipping SaaS vendors. All workflow JSON is free — grab it and deploy.

Store: stripeai.gumroad.com — premium templates with extended n8n workflows, Postgres schemas, and compliance-specific setup guides.


1. Maritime Customer Onboarding Drip

Who it targets: SaaS customers across seven maritime tiers — MAJOR_SHIPPING_LINE, PORT_AUTHORITY, SHIP_MANAGEMENT_COMPANY, MARITIME_LOGISTICS_PROVIDER, VESSEL_CLASSIFICATION_SOCIETY, OFFSHORE_ENERGY_OPERATOR, MARITIME_INSURANCE_COMPANY.

Compliance flags detected at signup:

  • IMO_ISM_CODE_APPLICABLE — company holds a Document of Compliance (DOC)
  • SOLAS_SAFETY_MANAGEMENT — vessel flag state registered, SMS in force
  • MARPOL_ANNEX_REGULATED — Annex I oil record book or Annex VI BDN required
  • USCG_VESSEL_INSPECTION — US port calls, COF required
  • CBP_C_TPAT_PARTICIPANT — C-TPAT SVI number on file
  • FMC_LICENSED_BROKER — ocean transport intermediary, 46 CFR §515
  • MLC_2006_APPLICABLE — flag state vessel with crew on board

Day 0 email routes MARPOL-flagged customers to ORB integration setup, C-TPAT customers to SVI perimeter config. Day 3 covers PSC monitor wiring and MARPOL incident webhook. Day 7 validates ISM Code §9.1 audit trail is running.

{
  "name": "Maritime Customer Onboarding Drip",
  "nodes": [
    {
      "id": "1",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        250,
        300
      ],
      "parameters": {
        "path": "/maritime-customer",
        "httpMethod": "POST",
        "responseMode": "responseNode"
      }
    },
    {
      "id": "2",
      "name": "Classify Customer",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        470,
        300
      ],
      "parameters": {
        "jsCode": "const d = $input.first().json;\nconst tier = d.vessel_count >= 50 ? 'MAJOR_SHIPPING_LINE'\n  : d.org_type === 'port_authority' ? 'PORT_AUTHORITY'\n  : d.org_type === 'ship_management' ? 'SHIP_MANAGEMENT_COMPANY'\n  : d.org_type === 'classification_society' ? 'VESSEL_CLASSIFICATION_SOCIETY'\n  : d.org_type === 'offshore_energy' ? 'OFFSHORE_ENERGY_OPERATOR'\n  : d.org_type === 'maritime_insurance' ? 'MARITIME_INSURANCE_COMPANY'\n  : 'MARITIME_LOGISTICS_PROVIDER';\nconst flags = {\n  IMO_ISM_CODE_APPLICABLE: !!(d.ism_doc_number || d.sms_certified),\n  SOLAS_SAFETY_MANAGEMENT: !!(d.vessel_flag_state || d.solas_chapter_ix),\n  MARPOL_ANNEX_REGULATED: !!(d.annex_i_oil || d.annex_vi_air || d.orb_required),\n  USCG_VESSEL_INSPECTION: !!(d.us_port_calls || d.uscg_cof),\n  CBP_C_TPAT_PARTICIPANT: !!(d.ctpat_svi_number || d.c_tpat_certified),\n  FMC_LICENSED_BROKER: !!(d.fmc_license_number || d.ocean_transport_intermediary),\n  MLC_2006_APPLICABLE: !!(d.vessel_flag_state && d.crew_count > 0)\n};\nreturn [{ json: { ...d, tier, flags,\n  day0_subject: `Welcome to the platform, ${d.company_name} \u2014 your maritime compliance setup guide`,\n  day3_subject: `${d.company_name}: ISM Code SMS integration checklist`,\n  day7_subject: flags.MARPOL_ANNEX_REGULATED\n    ? `${d.company_name}: MARPOL ORB chain-of-custody \u2014 keeping fuel records defensible`\n    : flags.CBP_C_TPAT_PARTICIPANT\n    ? `${d.company_name}: C-TPAT profile data perimeter \u2014 your SVI security boundary`\n    : `${d.company_name}: maritime compliance automation quick wins`\n}}];}"
      }
    },
    {
      "id": "3",
      "name": "Day 0 Welcome Email",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        690,
        200
      ],
      "parameters": {
        "operation": "send",
        "to": "={{ $json.contact_email }}",
        "subject": "={{ $json.day0_subject }}",
        "message": "Hi {{ $json.contact_name }},\n\nWelcome. Your account is active.\n\nGiven your profile ({{ $json.tier.replace(/_/g,' ') }}), configure these first:\n\n{% if $json.flags.IMO_ISM_CODE_APPLICABLE %}\u2022 ISM Code SMS Audit Trail: route all non-conformity reports and near-miss logs through your on-premise n8n instance. ISM Code \u00a79.1 requires an audit-ready investigation record \u2014 n8n Postgres logging satisfies this without sending crew incident data to third-party cloud.\n{% endif %}{% if $json.flags.MARPOL_ANNEX_REGULATED %}\u2022 MARPOL ORB Integration: connect your Annex I Oil Record Book entries to the MARPOL Monitor workflow. USCG boarding officers check ORB continuity \u2014 gaps look like falsification (18 U.S.C. \u00a71001).\n{% endif %}{% if $json.flags.CBP_C_TPAT_PARTICIPANT %}\u2022 C-TPAT Perimeter: your SVI security profile data must stay within your assessed boundary. Do not route CBP questionnaire responses through external automation SaaS.\n{% endif %}\n\nSetup guide: https://stripeai.gumroad.com\n\nFlowKit Team"
      }
    },
    {
      "id": "4",
      "name": "Log to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        690,
        400
      ],
      "parameters": {
        "operation": "append",
        "documentId": "YOUR_SHEET_ID",
        "sheetName": "customers",
        "columns": {
          "mappingMode": "autoMapInputData"
        }
      }
    },
    {
      "id": "5",
      "name": "Wait 3 Days",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [
        900,
        300
      ],
      "parameters": {
        "amount": 3,
        "unit": "days"
      }
    },
    {
      "id": "6",
      "name": "Day 3 SMS Integration Tips",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1110,
        300
      ],
      "parameters": {
        "operation": "send",
        "to": "={{ $json.contact_email }}",
        "subject": "={{ $json.day3_subject }}",
        "message": "Hi {{ $json.contact_name }},\n\nDay 3. Three integrations maritime ops teams enable this week:\n\n1. **Port State Control Monitor**: POST vessel AIS data to /psc-inspection-due \u2014 the PSC workflow fires a Slack alert 14 days before your next inspection window based on Paris/Tokyo MOU targeting lists.\n2. **MARPOL Incident Webhook**: wire your BWMS and incinerator logs to the MARPOL pipeline. Annex VI Reg. 18 fuel oil sampling records auto-archive with timestamp proof.\n3. **MLC 2006 Crew Welfare Tracker**: the deadline tracker covers MLC \u00a72.4 minimum rest hours and \u00a74.1 medical certificate renewals per flag state requirement.\n\nFlowKit Team"
      }
    },
    {
      "id": "7",
      "name": "Wait 4 Days",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [
        1320,
        300
      ],
      "parameters": {
        "amount": 4,
        "unit": "days"
      }
    },
    {
      "id": "8",
      "name": "Day 7 MARPOL Guide",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1530,
        300
      ],
      "parameters": {
        "operation": "send",
        "to": "={{ $json.contact_email }}",
        "subject": "={{ $json.day7_subject }}",
        "message": "Hi {{ $json.contact_name }},\n\nWeek 1 done. Compliance quick wins to validate:\n\n\u2022 MARPOL Monitor running? Confirm Annex I ORB and Annex VI BDN records are archiving to your Postgres instance.\n\u2022 PSC Deadline Tracker: add your next dry-dock date and flag state annual survey. The tracker emails your DPA 90/60/30/14 days out.\n\u2022 ISM Incident Pipeline: test the NON_CONFORMITY webhook \u2014 confirm DPA Slack alert and ISM \u00a79.1 incident log entry fire within 60 seconds.\n\nQuestions? Reply here.\n\nFlowKit Team"
      }
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Classify Customer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Customer": {
      "main": [
        [
          {
            "node": "Day 0 Welcome Email",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log to Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Day 0 Welcome Email": {
      "main": [
        [
          {
            "node": "Wait 3 Days",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 3 Days": {
      "main": [
        [
          {
            "node": "Day 3 SMS Integration Tips",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Day 3 SMS Integration Tips": {
      "main": [
        [
          {
            "node": "Wait 4 Days",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 4 Days": {
      "main": [
        [
          {
            "node": "Day 7 MARPOL Guide",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Port State Control & SOLAS Safety Monitor

Polls five critical maritime API endpoints every 30 minutes. On DOWN detection, alerts the Designated Person Ashore (DPA) via Slack and email.

Endpoint Annotation
ism_sms_api IMO ISM Code §9.1 — non-conformity and accident investigation records. DOWNTIME = PSC boarding audit trail gap.
marpol_orb_api MARPOL Annex I Reg. 17 / Annex VI Reg. 18 — ORB and BDN archive. DOWNTIME = USCG ORB continuity gap detection risk.
solas_gmdss_api SOLAS Chapter IV Reg. 4 — GMDSS equipment status. DOWNTIME = SOLAS safety equipment non-compliance window.
psc_targeting_api Paris / Tokyo / US MOU — PSC inspection targeting. DOWNTIME = missed detention window alert.
mlc_crew_welfare_api MLC 2006 §2.4 rest hours / §4.1 medical certs. DOWNTIME = flag state crew welfare audit exposure.

Uses $getWorkflowStaticData to only alert on UP→DOWN transitions (no repeated noise).

{
  "name": "Port State Control & SOLAS Safety Monitor",
  "nodes": [
    {
      "id": "1",
      "name": "Schedule \u2014 Every 30 Min",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        250,
        300
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 30
            }
          ]
        }
      }
    },
    {
      "id": "2",
      "name": "Check SMS Endpoints",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        470,
        300
      ],
      "parameters": {
        "method": "GET",
        "url": "={{ $vars.ISM_SMS_API_URL }}/health",
        "options": {
          "timeout": 10000
        }
      }
    },
    {
      "id": "3",
      "name": "Check MARPOL ORB API",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        470,
        500
      ],
      "parameters": {
        "method": "GET",
        "url": "={{ $vars.MARPOL_ORB_API_URL }}/health",
        "options": {
          "timeout": 10000
        }
      }
    },
    {
      "id": "4",
      "name": "Check SOLAS GMDSS API",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        470,
        700
      ],
      "parameters": {
        "method": "GET",
        "url": "={{ $vars.GMDSS_MONITORING_API_URL }}/health",
        "options": {
          "timeout": 10000
        }
      }
    },
    {
      "id": "5",
      "name": "Check PSC Inspection API",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        470,
        900
      ],
      "parameters": {
        "method": "GET",
        "url": "={{ $vars.PSC_TARGETING_API_URL }}/health",
        "options": {
          "timeout": 10000
        }
      }
    },
    {
      "id": "6",
      "name": "Check MLC Crew API",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        470,
        1100
      ],
      "parameters": {
        "method": "GET",
        "url": "={{ $vars.MLC_CREW_WELFARE_API_URL }}/health",
        "options": {
          "timeout": 10000
        }
      }
    },
    {
      "id": "7",
      "name": "Evaluate Status",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        700,
        700
      ],
      "parameters": {
        "jsCode": "const results = $input.all().map(i => i.json);\nconst endpoints = [\n  { name: 'ism_sms_api', label: 'ISM Code SMS API', note: 'ISM Code \u00a79.1 \u2014 non-conformity records and accident investigation. DOWNTIME = audit trail gap during PSC boarding.' },\n  { name: 'marpol_orb_api', label: 'MARPOL ORB Archive', note: 'MARPOL Annex I Reg. 17 / Annex VI Reg. 18 \u2014 ORB and BDN record-keeping. DOWNTIME = USCG boarding officer gap detection risk.' },\n  { name: 'solas_gmdss_api', label: 'SOLAS GMDSS Monitor', note: 'SOLAS Chapter IV / Reg. 4 \u2014 GMDSS equipment availability. DOWNTIME = SOLAS safety equipment non-compliance risk.' },\n  { name: 'psc_targeting_api', label: 'Port State Control API', note: 'Paris/Tokyo/US MOU targeting \u2014 PSC inspection due date tracking. DOWNTIME = missed detention window alerts.' },\n  { name: 'mlc_crew_welfare_api', label: 'MLC 2006 Crew API', note: 'MLC 2006 \u00a72.4 rest hours / \u00a74.1 medical certs. DOWNTIME = flag state audit exposure for crew welfare records.' }\n];\nconst prevState = $getWorkflowStaticData('global');\nconst alerts = [];\nendpoints.forEach((ep, i) => {\n  const isUp = results[i] && results[i].status === 'ok';\n  const wasUp = prevState[ep.name] !== false;\n  if (!isUp && wasUp) alerts.push({ endpoint: ep.label, note: ep.note, status: 'DOWN' });\n  prevState[ep.name] = isUp;\n});\n$setWorkflowStaticData('global', prevState);\nreturn alerts.length > 0 ? alerts.map(a => ({ json: a })) : [{ json: { status: 'all_healthy' } }];"
      }
    },
    {
      "id": "8",
      "name": "Alert DPA via Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        950,
        600
      ],
      "parameters": {
        "channel": "#maritime-compliance",
        "text": "=:anchor: *MARITIME COMPLIANCE ALERT*\n*Endpoint DOWN:* {{ $json.endpoint }}\n*Risk:* {{ $json.note }}\n*Time:* {{ $now.toISO() }}\n\nInvestigate immediately \u2014 PSC boarding can occur at any time."
      }
    },
    {
      "id": "9",
      "name": "Email DPA",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        950,
        800
      ],
      "parameters": {
        "operation": "send",
        "to": "={{ $vars.DPA_EMAIL }}",
        "subject": "=MARITIME ALERT: {{ $json.endpoint }} DOWN \u2014 compliance risk",
        "message": "=DPA,\n\nEndpoint {{ $json.endpoint }} is DOWN.\n\nRegulatory risk: {{ $json.note }}\n\nTime: {{ $now.toISO() }}\n\nInvestigate immediately.\n\nFlowKit Monitor"
      }
    }
  ],
  "connections": {
    "Schedule \u2014 Every 30 Min": {
      "main": [
        [
          {
            "node": "Check SMS Endpoints",
            "type": "main",
            "index": 0
          },
          {
            "node": "Check MARPOL ORB API",
            "type": "main",
            "index": 0
          },
          {
            "node": "Check SOLAS GMDSS API",
            "type": "main",
            "index": 0
          },
          {
            "node": "Check PSC Inspection API",
            "type": "main",
            "index": 0
          },
          {
            "node": "Check MLC Crew API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check SMS Endpoints": {
      "main": [
        [
          {
            "node": "Evaluate Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Evaluate Status": {
      "main": [
        [
          {
            "node": "Alert DPA via Slack",
            "type": "main",
            "index": 0
          },
          {
            "node": "Email DPA",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Maritime Regulatory Deadline Tracker

Weekdays at 8AM, scans a Google Sheet of 12 deadline types and emails the DPA when any vessel or entity is inside the alert window.

Deadline Type Regulation
ISM_CODE_DOC_ANNUAL IMO ISM Code §13 — DOC annual verification
ISM_CODE_SMC_RENEWAL IMO ISM Code §13.1 — SMC 5-year renewal + intermediate audit
MARPOL_ANNEX_I_SURVEY MARPOL Annex I Reg. 5 — annual oil pollution prevention survey
MARPOL_ANNEX_VI_SURVEY MARPOL Annex VI Reg. 5 — annual air pollution prevention survey
SOLAS_SAFETY_CERT_RENEWAL SOLAS Chapter I Reg. 12 — Cargo Ship Safety Certificate
PSC_INSPECTION_DUE Paris / Tokyo / US MOU — PSC targeting window
STCW_CREW_CERT_RENEWAL STCW 2010 Manila — officer certification (5-year)
MLC_MARITIME_LABOUR_CERT MLC 2006 Title 5 — Maritime Labour Certificate (5-year)
CBP_C_TPAT_ANNUAL_REVIEW CBP C-TPAT — annual SVI security profile
FMC_LICENSE_RENEWAL FMC 46 CFR §515 — OTI license triennial renewal
ISM_INTERNAL_AUDIT IMO ISM Code §12 — annual internal safety audit
ANNUAL_PENTEST IMO MSC-FAL.1/Circ.3 — maritime cyber risk assessment (SOLAS XI-2)

Urgency tiers: OVERDUE / CRITICAL (≤14d) / URGENT (≤30d) / WARNING (≤60d) / NOTICE (≤90d). Deduplicates alerts with alert_sent_date.

{
  "name": "Maritime Regulatory Deadline Tracker",
  "nodes": [
    {
      "id": "1",
      "name": "Schedule \u2014 Weekdays 8AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        250,
        300
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1-5"
            }
          ]
        }
      }
    },
    {
      "id": "2",
      "name": "Load Deadlines",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        470,
        300
      ],
      "parameters": {
        "operation": "getAll",
        "documentId": "YOUR_SHEET_ID",
        "sheetName": "maritime_deadlines",
        "options": {
          "returnAllMatches": true
        }
      }
    },
    {
      "id": "3",
      "name": "Calculate Days Remaining",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        690,
        300
      ],
      "parameters": {
        "jsCode": "const today = new Date(); today.setHours(0,0,0,0);\nconst DEADLINE_TYPES = {\n  ISM_CODE_DOC_ANNUAL: 'IMO ISM Code \u00a713 \u2014 DOC annual verification. Flag administration audit.',\n  ISM_CODE_SMC_RENEWAL: 'IMO ISM Code \u00a713.1 \u2014 SMC 5-year renewal + intermediate audit.',\n  MARPOL_ANNEX_I_SURVEY: 'MARPOL Annex I Reg. 5 \u2014 annual oil pollution prevention survey.',\n  MARPOL_ANNEX_VI_SURVEY: 'MARPOL Annex VI Reg. 5 \u2014 annual air pollution prevention survey.',\n  SOLAS_SAFETY_CERT_RENEWAL: 'SOLAS Chapter I Reg. 12 \u2014 Passenger Ship Safety / Cargo Ship Safety Certificate.',\n  PSC_INSPECTION_DUE: 'Paris/Tokyo/US MOU \u2014 Port State Control inspection targeting window.',\n  STCW_CREW_CERT_RENEWAL: 'STCW 2010 Manila Amendments \u2014 officer certification renewal (5-year cycle).',\n  MLC_MARITIME_LABOUR_CERT: 'MLC 2006 Title 5 \u2014 Maritime Labour Certificate renewal (5 years).',\n  CBP_C_TPAT_ANNUAL_REVIEW: 'CBP C-TPAT \u2014 annual SVI security profile validation.',\n  FMC_LICENSE_RENEWAL: 'FMC 46 CFR \u00a7515 \u2014 Ocean Transport Intermediary license triennial renewal.',\n  ISM_INTERNAL_AUDIT: 'IMO ISM Code \u00a712 \u2014 annual internal safety audit of all shipboard operations.',\n  ANNUAL_PENTEST: 'IMO MSC-FAL.1/Circ.3 \u2014 annual maritime cyber risk assessment (SOLAS XI-2).'\n};\nreturn $input.all().map(i => {\n  const d = i.json;\n  const due = new Date(d.due_date); due.setHours(0,0,0,0);\n  const days = Math.round((due - today) / 86400000);\n  const urgency = days < 0 ? 'OVERDUE' : days <= 14 ? 'CRITICAL' : days <= 30 ? 'URGENT' : days <= 60 ? 'WARNING' : days <= 90 ? 'NOTICE' : 'OK';\n  const note = DEADLINE_TYPES[d.deadline_type] || d.deadline_type;\n  return { json: { ...d, days_remaining: days, urgency, regulatory_note: note, alert_sent_date: d.alert_sent_date || '' }};\n}).filter(i => i.json.urgency !== 'OK');"
      }
    },
    {
      "id": "4",
      "name": "Skip Already Alerted",
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2,
      "position": [
        900,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": false
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "1",
              "leftValue": "={{ $json.alert_sent_date }}",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "rightValue": "={{ $now.toFormat('yyyy-MM-dd') }}"
            },
            {
              "id": "2",
              "leftValue": "={{ $json.urgency }}",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "rightValue": "OVERDUE"
            }
          ]
        }
      }
    },
    {
      "id": "5",
      "name": "Alert DPA + Flag Admin",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        1110,
        300
      ],
      "parameters": {
        "operation": "send",
        "to": "={{ $vars.DPA_EMAIL }}",
        "cc": "={{ $vars.FLAG_ADMIN_EMAIL }}",
        "subject": "=[{{ $json.urgency }}] Maritime deadline: {{ $json.deadline_type }} \u2014 {{ $json.days_remaining }} days",
        "message": "=DPA,\n\n{{ $json.urgency }} maritime compliance deadline:\n\nDeadline: {{ $json.deadline_type }}\nVessel / Entity: {{ $json.vessel_name || $json.entity_name }}\nDue Date: {{ $json.due_date }}\nDays Remaining: {{ $json.days_remaining }}\n\nRegulatory note: {{ $json.regulatory_note }}\n\nAction required: {{ $json.days_remaining < 0 ? 'OVERDUE \u2014 flag state notification may be required' : 'Schedule before due date' }}\n\nFlowKit Maritime Deadline Tracker"
      }
    }
  ],
  "connections": {
    "Schedule \u2014 Weekdays 8AM": {
      "main": [
        [
          {
            "node": "Load Deadlines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Deadlines": {
      "main": [
        [
          {
            "node": "Calculate Days Remaining",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Days Remaining": {
      "main": [
        [
          {
            "node": "Skip Already Alerted",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip Already Alerted": {
      "main": [
        [
          {
            "node": "Alert DPA + Flag Admin",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Maritime Incident & MARPOL Compliance Pipeline

Webhook receives incident reports and routes them by regulatory response window. All incidents log to Postgres for IMO ISM Code §9.1 audit trail integrity.

Incident Type Response Window Regulatory Note
MARPOL_OIL_DISCHARGE 24h MARPOL Annex I Reg. 4 — flag state notification. USCG NPRM if US waters. Civil penalty up to $25,000/day (33 U.S.C. §1321).
MARPOL_AIR_EMISSION_VIOLATION 48h MARPOL Annex VI Reg. 18 — fuel oil non-compliance report. Port State may detain vessel.
SOLAS_CASUALTY_SERIOUS 24h SOLAS Chapter XI-1 Reg. 6 — marine casualty report to flag state. IMO GISIS entry required.
ISM_NON_CONFORMITY_MAJOR 48h IMO ISM Code §9.1 — major non-conformity must be rectified before departure. Class society notification.
PSC_DETENTION 4h Paris / Tokyo / US MOU — vessel detained. Flag state notification immediate. Rectification plan required.
CBP_SECURITY_ALERT 2h CBP C-TPAT SVI — security incident affecting supply chain. C-TPAT coordinator notification within 2h.
MLC_CREW_WELFARE_COMPLAINT 72h MLC 2006 Standard A5.1.5 — flag state and ITF may inspect.
STCW_WATCHKEEPING_VIOLATION 48h STCW 2010 Section A-VIII/1 — rest hour violation. Flag state endorsement at risk.
{
  "name": "Maritime Incident & MARPOL Compliance Pipeline",
  "nodes": [
    {
      "id": "1",
      "name": "Incident Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        250,
        300
      ],
      "parameters": {
        "path": "/maritime-incident",
        "httpMethod": "POST",
        "responseMode": "responseNode"
      }
    },
    {
      "id": "2",
      "name": "Classify Incident",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        470,
        300
      ],
      "parameters": {
        "jsCode": "const d = $input.first().json;\nconst INCIDENT_TYPES = {\n  MARPOL_OIL_DISCHARGE: { hours: 24, authority: 'USCG / Flag State', reg: 'MARPOL Annex I Reg. 4 \u2014 immediate flag state notification required. USCG NPRM if in US waters. Civil penalty up to $25,000/day (33 U.S.C. \u00a71321).', log_required: true },\n  MARPOL_AIR_EMISSION_VIOLATION: { hours: 48, authority: 'Flag State / Port State', reg: 'MARPOL Annex VI Reg. 18 \u2014 fuel oil non-compliance report to flag administration. Port State may detain vessel.', log_required: true },\n  SOLAS_CASUALTY_SERIOUS: { hours: 24, authority: 'Flag State / IMO GISIS', reg: 'SOLAS Chapter XI-1 Reg. 6 \u2014 marine casualty report to flag state. IMO GISIS entry required within 24h.', log_required: true },\n  ISM_NON_CONFORMITY_MAJOR: { hours: 48, authority: 'DPA + Class Society', reg: 'IMO ISM Code \u00a79.1 \u2014 major non-conformity must be rectified before departure. Class society notification required.', log_required: true },\n  PSC_DETENTION: { hours: 4, authority: 'DPA + Flag State', reg: 'Paris/Tokyo/US MOU \u2014 vessel detained by Port State Control. Flag state must be notified immediately. Rectification plan required.', log_required: true },\n  CBP_SECURITY_ALERT: { hours: 2, authority: 'CBP C-TPAT + Legal', reg: 'CBP C-TPAT SVI \u2014 security incident affecting supply chain integrity. C-TPAT coordinator notification required within 2h.', log_required: true },\n  MLC_CREW_WELFARE_COMPLAINT: { hours: 72, authority: 'Flag State + ITF', reg: 'MLC 2006 Standard A5.1.5 \u2014 crew complaint must be investigated. Flag state and ITF may inspect.', log_required: false },\n  STCW_WATCHKEEPING_VIOLATION: { hours: 48, authority: 'Flag State + DPA', reg: 'STCW 2010 Section A-VIII/1 \u2014 rest hour violation. Flag state endorsement may be at risk.', log_required: true }\n};\nconst config = INCIDENT_TYPES[d.incident_type] || { hours: 72, authority: 'DPA', reg: 'Review required', log_required: true };\nconst deadline = new Date(Date.now() + config.hours * 3600000).toISOString();\nreturn [{ json: { ...d, response_hours: config.hours, authority: config.authority, regulatory_note: config.reg, log_required: config.log_required, response_deadline: deadline, incident_id: `MAR-${Date.now()}` }}];"
      }
    },
    {
      "id": "3",
      "name": "Log to Postgres",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        690,
        500
      ],
      "parameters": {
        "operation": "insert",
        "schema": "public",
        "table": "maritime_incidents",
        "columns": "incident_id,incident_type,vessel_name,vessel_imo,flag_state,incident_description,response_hours,regulatory_note,response_deadline,reported_by,reported_at",
        "columnValues": "={{ $json.incident_id }},={{ $json.incident_type }},={{ $json.vessel_name }},={{ $json.vessel_imo }},={{ $json.flag_state }},={{ $json.description }},={{ $json.response_hours }},={{ $json.regulatory_note }},={{ $json.response_deadline }},={{ $json.reported_by }},={{ $now.toISO() }}"
      }
    },
    {
      "id": "4",
      "name": "Alert DPA via Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        690,
        300
      ],
      "parameters": {
        "channel": "#maritime-incidents",
        "text": "=:anchor: *MARITIME INCIDENT \u2014 {{ $json.incident_type }}*\n*Vessel:* {{ $json.vessel_name }} (IMO {{ $json.vessel_imo }}) \u2014 Flag: {{ $json.flag_state }}\n*Incident ID:* {{ $json.incident_id }}\n*Response Required:* {{ $json.response_hours }}h \u2014 deadline {{ $json.response_deadline }}\n*Authority:* {{ $json.authority }}\n*Regulatory note:* {{ $json.regulatory_note }}\n*Reported:* {{ $now.toISO() }}"
      }
    },
    {
      "id": "5",
      "name": "Email DPA + Flag Admin",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        900,
        300
      ],
      "parameters": {
        "operation": "send",
        "to": "={{ $vars.DPA_EMAIL }}",
        "cc": "={{ $vars.FLAG_ADMIN_EMAIL }}",
        "subject": "=[MARITIME INCIDENT] {{ $json.incident_type }} \u2014 Vessel {{ $json.vessel_name }} \u2014 {{ $json.response_hours }}h response window",
        "message": "=DPA,\n\nMaritime incident reported requiring action.\n\nIncident ID: {{ $json.incident_id }}\nType: {{ $json.incident_type }}\nVessel: {{ $json.vessel_name }} (IMO {{ $json.vessel_imo }}) \u2014 Flag State: {{ $json.flag_state }}\nDescription: {{ $json.description }}\n\nResponse deadline: {{ $json.response_deadline }} ({{ $json.response_hours }}h window)\nNotify: {{ $json.authority }}\n\nRegulatory note:\n{{ $json.regulatory_note }}\n\nThis incident has been logged to the maritime incident database (ID: {{ $json.incident_id }}).\n\nFlowKit Maritime Incident Pipeline"
      }
    }
  ],
  "connections": {
    "Incident Webhook": {
      "main": [
        [
          {
            "node": "Classify Incident",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Incident": {
      "main": [
        [
          {
            "node": "Alert DPA via Slack",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log to Postgres",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Alert DPA via Slack": {
      "main": [
        [
          {
            "node": "Email DPA + Flag Admin",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

5. Weekly Maritime KPI Dashboard

Monday 8AM: queries Postgres for incidents and upcoming deadlines, emails CEO + DPA with MARPOL incident count, PSC detention tally, and critical deadline list (≤14 days).

{
  "name": "Weekly Maritime KPI Dashboard",
  "nodes": [
    {
      "id": "1",
      "name": "Schedule \u2014 Monday 8AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        250,
        300
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1"
            }
          ]
        }
      }
    },
    {
      "id": "2",
      "name": "Query Incidents This Week",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        470,
        300
      ],
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT incident_type, COUNT(*) as count, flag_state FROM maritime_incidents WHERE reported_at >= NOW() - INTERVAL '7 days' GROUP BY incident_type, flag_state ORDER BY count DESC"
      }
    },
    {
      "id": "3",
      "name": "Query Open Deadlines",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        470,
        500
      ],
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT deadline_type, vessel_name, due_date, EXTRACT(DAY FROM (due_date::date - CURRENT_DATE)) as days_remaining FROM maritime_deadlines WHERE due_date::date <= CURRENT_DATE + 30 AND resolved = false ORDER BY due_date"
      }
    },
    {
      "id": "4",
      "name": "Build KPI Report",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        700,
        400
      ],
      "parameters": {
        "jsCode": "const incidents = $('Query Incidents This Week').all().map(i => i.json);\nconst deadlines = $('Query Open Deadlines').all().map(i => i.json);\nconst marpol = incidents.filter(i => i.incident_type && i.incident_type.startsWith('MARPOL')).reduce((s,i) => s + Number(i.count), 0);\nconst pscDetentions = incidents.filter(i => i.incident_type === 'PSC_DETENTION').reduce((s,i) => s + Number(i.count), 0);\nconst critical = deadlines.filter(d => Number(d.days_remaining) <= 14);\nconst report = [\n  `Weekly Maritime Compliance KPI \u2014 ${new Date().toISOString().slice(0,10)}`,\n  '',\n  `MARPOL Incidents (7d): ${marpol}`,\n  `PSC Detentions (7d): ${pscDetentions}`,\n  `Total Incidents (7d): ${incidents.reduce((s,i) => s + Number(i.count), 0)}`,\n  `Deadlines \u226430d: ${deadlines.length}`,\n  `Critical Deadlines \u226414d: ${critical.length}`,\n  '',\n  'Critical deadlines:',\n  ...critical.map(d => `  ${d.deadline_type} \u2014 ${d.vessel_name} \u2014 due ${d.due_date} (${d.days_remaining}d)`),\n  '',\n  'Incident breakdown:',\n  ...incidents.map(i => `  ${i.incident_type} (${i.flag_state}): ${i.count}`)\n].join('\\n');\nreturn [{ json: { report, marpol_count: marpol, psc_detentions: pscDetentions, critical_count: critical.length }}];"
      }
    },
    {
      "id": "5",
      "name": "Email CEO + DPA",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2,
      "position": [
        950,
        400
      ],
      "parameters": {
        "operation": "send",
        "to": "={{ $vars.CEO_EMAIL }}",
        "cc": "={{ $vars.DPA_EMAIL }}",
        "bcc": "={{ $vars.CISO_EMAIL }}",
        "subject": "=Weekly Maritime KPI \u2014 MARPOL: {{ $json.marpol_count }} | PSC Detentions: {{ $json.psc_detentions }} | Critical: {{ $json.critical_count }}",
        "message": "={{ $json.report }}"
      }
    }
  ],
  "connections": {
    "Schedule \u2014 Monday 8AM": {
      "main": [
        [
          {
            "node": "Query Incidents This Week",
            "type": "main",
            "index": 0
          },
          {
            "node": "Query Open Deadlines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Query Incidents This Week": {
      "main": [
        [
          {
            "node": "Build KPI Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Query Open Deadlines": {
      "main": [
        [
          {
            "node": "Build KPI Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build KPI Report": {
      "main": [
        [
          {
            "node": "Email CEO + DPA",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Why maritime SaaS vendors self-host n8n

Compliance Reason Why Cloud Automation Creates Risk
IMO ISM Code §9.1 accident/near-miss investigation records MARPOL and PSC audit trail chain of custody requires records to stay within your assessed boundary — third-party cloud adds a sub-processor the flag state did not assess.
MARPOL Annex VI Reg. 18 fuel oil sampling records (BDN) USCG boarding officers cross-reference BDN continuity. A gap caused by third-party API downtime can look like falsification under 18 U.S.C. §1001. Self-hosted n8n eliminates the dependency.
US CBP C-TPAT SVI security profile data Your C-TPAT security questionnaire contains supply chain intelligence that must stay within your assessed perimeter. Routing it through Zapier or Make expands your CBP security profile scope.
SOLAS SMS documentation chain Git-versioned n8n workflow JSON satisfies IMO MSC/Circ.1533 SMS documentation version control requirements — no external SaaS needed.
FMC tariff filing audit records FMC 46 CFR §515 requires OTI records to be retained for 5 years. Self-hosted Postgres audit trail keeps the chain of custody intact without sub-processor DPA complications.

Buyer Q&A

Q: Does n8n's cloud meet MARPOL Annex I ORB chain-of-custody requirements for USCG boarding inspections?
A: Not out of the box. USCG boarding officers check ORB continuity — any gap caused by a third-party API timeout can look like falsification under 18 U.S.C. §1001. Self-hosted n8n with Postgres ORB logging eliminates the external dependency and keeps the audit trail within your assessed boundary.

Q: How does n8n handle Port State Control detention records across multiple flag states with different documentation requirements?
A: The PSC Pipeline webhook classifies by MOU region (Paris, Tokyo, US). Each detention auto-logs vessel IMO number, flag state, and detention reason to Postgres. The flag state field in the incident record drives the notification routing — Paris MOU requires different authority notification than Tokyo MOU.

Q: Our ISM Code §9.1 incident investigations contain sensitive crew incident data. Can we keep it on-premise?
A: Yes — this is the primary self-hosting argument. Run n8n on your own infrastructure, connect Postgres locally. Crew incident investigation records never leave your network. The ISM Code does not prohibit digital records, but the IMO expects the SMS to maintain record integrity without external dependencies.

Q: We are C-TPAT certified. Does routing our supply chain security workflows through n8n cloud expand our CBP security profile scope?
A: Yes, if you use n8n cloud. CBP C-TPAT requires your SVI security profile boundary to be assessed and maintained. Routing C-TPAT questionnaire data through a third-party cloud adds a sub-processor that falls within your assessed perimeter. Self-hosted n8n eliminates this scope expansion.

Q: Our vessel classification society audits our SMS documentation annually. How does n8n's git-versioned workflow JSON satisfy IMO MSC/Circ.1533 SMS documentation requirements?
A: n8n workflows export as JSON and can be version-controlled in git. Each commit is a timestamped, auditable change record. IMO MSC/Circ.1533 requires SMS documents to be controlled — git-versioned n8n JSON satisfies this without a separate document management system.


All five workflows above are free. For the full maritime compliance toolkit — extended Postgres schemas, MARPOL ORB record templates, PSC inspection targeting integration, and flag-state-specific notification routing — see the premium templates at stripeai.gumroad.com.

Top comments (0)