{
  "name": "Multi-Carrier Tracking Aggregator (Nodrel)",
  "tags": [],
  "nodes": [
    {
      "id": "443f7e47-e315-4222-af63-842b6761a4f9",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        2304,
        4224
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "c176aab5-fe25-4909-970d-0a7d9419cc6d",
      "name": "Load active shipments",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        2528,
        4224
      ],
      "parameters": {
        "operation": "get",
        "returnAll": true,
        "dataTableId": {
          "__rl": true,
          "mode": "name",
          "value": "shipment_tracking"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "73db10e2-17b3-4e1e-b692-e66e0717ccc0",
      "name": "Active only",
      "type": "n8n-nodes-base.filter",
      "position": [
        2752,
        4224
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.canonicalStatus }}",
              "rightValue": "DELIVERED"
            },
            {
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.canonicalStatus }}",
              "rightValue": "RETURNED_TO_SENDER"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "653d387f-5bd1-4e80-905e-add89df79e6f",
      "name": "Switch by carrier",
      "type": "n8n-nodes-base.switch",
      "position": [
        2928,
        4192
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "fedex",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.carrier }}",
                    "rightValue": "fedex"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "ups",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.carrier }}",
                    "rightValue": "ups"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "dhl",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.carrier }}",
                    "rightValue": "dhl"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "351d0476-ba99-4fc4-a115-f4ffeb4ce875",
      "name": "FEDEX \u00b7 Check Breaker",
      "type": "n8n-nodes-base.code",
      "position": [
        3184,
        3872
      ],
      "parameters": {
        "jsCode": "const sd = $getWorkflowStaticData('global');\nsd.breakers = sd.breakers || {};\nconst COOLDOWN_MS = 5 * 60 * 1000; // OPEN duration before a half-open probe\nconst now = Date.now();\nconst out = [];\nfor (const item of $input.all()) {\n  const j = item.json;\n  const carrier = j.carrier;\n  const cb = sd.breakers[carrier] || { state: 'CLOSED', failures: 0, openedAt: 0 };\n  let allowed = true;\n  if (cb.state === 'OPEN') {\n    if (now - cb.openedAt >= COOLDOWN_MS) { cb.state = 'HALF_OPEN'; allowed = true; }\n    else { allowed = false; }\n  }\n  sd.breakers[carrier] = cb;\n  out.push({ json: { ...j, _cbAllowed: allowed, _cbState: cb.state } });\n}\nreturn out;"
      },
      "typeVersion": 2
    },
    {
      "id": "823363c6-5cf8-4ef7-8865-01a37d62a2e8",
      "name": "FEDEX \u00b7 IF allowed",
      "type": "n8n-nodes-base.if",
      "position": [
        3408,
        3872
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json._cbAllowed }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "f9abc54e-6700-473c-a926-fd678f73b393",
      "name": "FEDEX \u00b7 Track API",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "maxTries": 3,
      "position": [
        3648,
        3824
      ],
      "parameters": {
        "url": "https://apis.fedex.com/track/v1/trackingnumbers",
        "method": "POST",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1
            }
          },
          "response": {
            "response": {
              "neverError": true,
              "fullResponse": true
            }
          }
        },
        "jsonBody": "={{ { trackingInfo: [ { trackingNumberInfo: { trackingNumber: $json.trackingNumber } } ], includeDetailedScans: false } }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "oAuth2Api",
        "headerParameters": {
          "parameters": [
            {
              "name": "x-locale",
              "value": "en_US"
            }
          ]
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "alwaysOutputData": true,
      "waitBetweenTries": 2000
    },
    {
      "id": "20b1c9d0-192f-46bc-b605-6f35f9692cb1",
      "name": "FEDEX \u00b7 Skipped",
      "type": "n8n-nodes-base.set",
      "position": [
        3648,
        3984
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "938ac278-9c47-4439-8f4c-a16b4e436115",
              "name": "canonicalStatus",
              "type": "string",
              "value": "CARRIER_UNAVAILABLE"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "9af2ab7f-f60b-427d-886a-73d60eaad270",
      "name": "FEDEX \u00b7 Normalize + Breaker",
      "type": "n8n-nodes-base.code",
      "position": [
        3904,
        3872
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// FEDEX normalize + circuit-breaker update (run once for each item)\nconst ref = $('FEDEX \u00b7 IF allowed').item.json;\nconst trackingNumber = ref.trackingNumber;\nconst carrier = ref.carrier || 'fedex';\n\nconst sd = $getWorkflowStaticData('global');\nsd.breakers = sd.breakers || {};\nconst cb = sd.breakers[carrier] || { state: 'CLOSED', failures: 0, openedAt: 0 };\nconst THRESHOLD = 5;\nconst now = () => new Date().toISOString();\n\n// Breaker was OPEN -> call was skipped. Pass through, leave counters untouched.\nif ($json.canonicalStatus === 'CARRIER_UNAVAILABLE') {\n  return { json: { trackingNumber, carrier, canonicalStatus: 'CARRIER_UNAVAILABLE',\n    rawStatus: null, breakerState: cb.state, checkedAt: now() } };\n}\n\nconst httpErr = !!$json.error;\nconst code = typeof $json.statusCode === 'number' ? $json.statusCode : (httpErr ? 599 : 200);\nconst outage = httpErr || code === 429 || code >= 500; // only outages trip the breaker\n\nif (outage) {\n  cb.failures += 1;\n  if (cb.failures >= THRESHOLD) { cb.state = 'OPEN'; cb.openedAt = Date.now(); }\n} else { cb.failures = 0; cb.state = 'CLOSED'; }\nsd.breakers[carrier] = cb;\n\nconst MAP = { 'OC': 'PRE_TRANSIT', 'PU': 'IN_TRANSIT', 'IT': 'IN_TRANSIT', 'OD': 'OUT_FOR_DELIVERY', 'DL': 'DELIVERED', 'DE': 'EXCEPTION', 'RS': 'RETURNED_TO_SENDER', 'CA': 'EXCEPTION' };\nlet canonical, raw = null;\nif (outage) { canonical = 'EXCEPTION'; }\nelse if (code >= 400) { canonical = 'UNKNOWN'; }   // e.g. 404 not-found for this tracking #\nelse { raw = $json.body?.output?.completeTrackResults?.[0]?.trackResults?.[0]?.latestStatusDetail?.derivedCode; canonical = MAP[raw] || 'UNKNOWN'; }\n\nreturn { json: { trackingNumber, carrier, canonicalStatus: canonical, rawStatus: raw,\n  breakerState: cb.state, httpStatus: code, checkedAt: now() } };"
      },
      "typeVersion": 2
    },
    {
      "id": "0a437b30-4bdc-476b-9a94-5cd861dd9459",
      "name": "UPS \u00b7 Check Breaker",
      "type": "n8n-nodes-base.code",
      "position": [
        3184,
        4192
      ],
      "parameters": {
        "jsCode": "const sd = $getWorkflowStaticData('global');\nsd.breakers = sd.breakers || {};\nconst COOLDOWN_MS = 5 * 60 * 1000; // OPEN duration before a half-open probe\nconst now = Date.now();\nconst out = [];\nfor (const item of $input.all()) {\n  const j = item.json;\n  const carrier = j.carrier;\n  const cb = sd.breakers[carrier] || { state: 'CLOSED', failures: 0, openedAt: 0 };\n  let allowed = true;\n  if (cb.state === 'OPEN') {\n    if (now - cb.openedAt >= COOLDOWN_MS) { cb.state = 'HALF_OPEN'; allowed = true; }\n    else { allowed = false; }\n  }\n  sd.breakers[carrier] = cb;\n  out.push({ json: { ...j, _cbAllowed: allowed, _cbState: cb.state } });\n}\nreturn out;"
      },
      "typeVersion": 2
    },
    {
      "id": "6821525e-25c9-4ce5-98bf-e5ec55cca9a6",
      "name": "UPS \u00b7 IF allowed",
      "type": "n8n-nodes-base.if",
      "position": [
        3408,
        4192
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json._cbAllowed }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "2d04f03a-e108-4ac3-aed2-039a4970e5b1",
      "name": "UPS \u00b7 Track API",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "maxTries": 3,
      "position": [
        3648,
        4144
      ],
      "parameters": {
        "url": "=https://onlinetools.ups.com/api/track/v1/details/{{ $json.trackingNumber }}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1
            }
          },
          "response": {
            "response": {
              "neverError": true,
              "fullResponse": true
            }
          }
        },
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "oAuth2Api",
        "headerParameters": {
          "parameters": [
            {
              "name": "transId",
              "value": "={{ Date.now() }}"
            },
            {
              "name": "transactionSrc",
              "value": "n8n-tracking-aggregator"
            }
          ]
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "alwaysOutputData": true,
      "waitBetweenTries": 2000
    },
    {
      "id": "c8d6e202-3693-402c-94ab-b4f7f4607405",
      "name": "UPS \u00b7 Skipped",
      "type": "n8n-nodes-base.set",
      "position": [
        3648,
        4304
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "8c809b84-c88c-4707-9efa-753f4ae644f9",
              "name": "canonicalStatus",
              "type": "string",
              "value": "CARRIER_UNAVAILABLE"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "09bbb97d-41d7-4b4f-b7fd-0bce9237bc33",
      "name": "UPS \u00b7 Normalize + Breaker",
      "type": "n8n-nodes-base.code",
      "position": [
        3904,
        4192
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// UPS normalize + circuit-breaker update (run once for each item)\nconst ref = $('UPS \u00b7 IF allowed').item.json;\nconst trackingNumber = ref.trackingNumber;\nconst carrier = ref.carrier || 'ups';\n\nconst sd = $getWorkflowStaticData('global');\nsd.breakers = sd.breakers || {};\nconst cb = sd.breakers[carrier] || { state: 'CLOSED', failures: 0, openedAt: 0 };\nconst THRESHOLD = 5;\nconst now = () => new Date().toISOString();\n\n// Breaker was OPEN -> call was skipped. Pass through, leave counters untouched.\nif ($json.canonicalStatus === 'CARRIER_UNAVAILABLE') {\n  return { json: { trackingNumber, carrier, canonicalStatus: 'CARRIER_UNAVAILABLE',\n    rawStatus: null, breakerState: cb.state, checkedAt: now() } };\n}\n\nconst httpErr = !!$json.error;\nconst code = typeof $json.statusCode === 'number' ? $json.statusCode : (httpErr ? 599 : 200);\nconst outage = httpErr || code === 429 || code >= 500; // only outages trip the breaker\n\nif (outage) {\n  cb.failures += 1;\n  if (cb.failures >= THRESHOLD) { cb.state = 'OPEN'; cb.openedAt = Date.now(); }\n} else { cb.failures = 0; cb.state = 'CLOSED'; }\nsd.breakers[carrier] = cb;\n\nconst MAP = { 'M': 'PRE_TRANSIT', 'P': 'IN_TRANSIT', 'I': 'IN_TRANSIT', 'O': 'OUT_FOR_DELIVERY', 'D': 'DELIVERED', 'X': 'EXCEPTION', 'RS': 'RETURNED_TO_SENDER' };\nlet canonical, raw = null;\nif (outage) { canonical = 'EXCEPTION'; }\nelse if (code >= 400) { canonical = 'UNKNOWN'; }   // e.g. 404 not-found for this tracking #\nelse { raw = $json.body?.trackResponse?.shipment?.[0]?.package?.[0]?.currentStatus?.type; canonical = MAP[raw] || 'UNKNOWN'; }\n\nreturn { json: { trackingNumber, carrier, canonicalStatus: canonical, rawStatus: raw,\n  breakerState: cb.state, httpStatus: code, checkedAt: now() } };"
      },
      "typeVersion": 2
    },
    {
      "id": "43a3a675-1723-452e-9c52-b4b66f3ebc6e",
      "name": "DHL \u00b7 Check Breaker",
      "type": "n8n-nodes-base.code",
      "position": [
        3184,
        4512
      ],
      "parameters": {
        "jsCode": "const sd = $getWorkflowStaticData('global');\nsd.breakers = sd.breakers || {};\nconst COOLDOWN_MS = 5 * 60 * 1000; // OPEN duration before a half-open probe\nconst now = Date.now();\nconst out = [];\nfor (const item of $input.all()) {\n  const j = item.json;\n  const carrier = j.carrier;\n  const cb = sd.breakers[carrier] || { state: 'CLOSED', failures: 0, openedAt: 0 };\n  let allowed = true;\n  if (cb.state === 'OPEN') {\n    if (now - cb.openedAt >= COOLDOWN_MS) { cb.state = 'HALF_OPEN'; allowed = true; }\n    else { allowed = false; }\n  }\n  sd.breakers[carrier] = cb;\n  out.push({ json: { ...j, _cbAllowed: allowed, _cbState: cb.state } });\n}\nreturn out;"
      },
      "typeVersion": 2
    },
    {
      "id": "ffb95bec-8ed6-4db1-9e43-2171800017fd",
      "name": "DHL \u00b7 IF allowed",
      "type": "n8n-nodes-base.if",
      "position": [
        3408,
        4512
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json._cbAllowed }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "3b301a9f-7b55-4298-beab-b440d0f22eb6",
      "name": "DHL \u00b7 Track API",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "maxTries": 3,
      "position": [
        3648,
        4464
      ],
      "parameters": {
        "url": "https://api-eu.dhl.com/track/shipments",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1
            }
          },
          "response": {
            "response": {
              "neverError": true,
              "fullResponse": true
            }
          }
        },
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "oAuth2Api",
        "queryParameters": {
          "parameters": [
            {
              "name": "trackingNumber",
              "value": "={{ $json.trackingNumber }}"
            }
          ]
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "alwaysOutputData": true,
      "waitBetweenTries": 2000
    },
    {
      "id": "c260d815-3466-44c6-827e-93ce7fe72622",
      "name": "DHL \u00b7 Skipped",
      "type": "n8n-nodes-base.set",
      "position": [
        3648,
        4624
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "dea3856a-3f2b-4141-9e9a-7f9ca9de3d10",
              "name": "canonicalStatus",
              "type": "string",
              "value": "CARRIER_UNAVAILABLE"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "07b2f046-7646-4034-b4b1-7cf77d5f3284",
      "name": "DHL \u00b7 Normalize + Breaker",
      "type": "n8n-nodes-base.code",
      "position": [
        3904,
        4512
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// DHL normalize + circuit-breaker update (run once for each item)\nconst ref = $('DHL \u00b7 IF allowed').item.json;\nconst trackingNumber = ref.trackingNumber;\nconst carrier = ref.carrier || 'dhl';\n\nconst sd = $getWorkflowStaticData('global');\nsd.breakers = sd.breakers || {};\nconst cb = sd.breakers[carrier] || { state: 'CLOSED', failures: 0, openedAt: 0 };\nconst THRESHOLD = 5;\nconst now = () => new Date().toISOString();\n\n// Breaker was OPEN -> call was skipped. Pass through, leave counters untouched.\nif ($json.canonicalStatus === 'CARRIER_UNAVAILABLE') {\n  return { json: { trackingNumber, carrier, canonicalStatus: 'CARRIER_UNAVAILABLE',\n    rawStatus: null, breakerState: cb.state, checkedAt: now() } };\n}\n\nconst httpErr = !!$json.error;\nconst code = typeof $json.statusCode === 'number' ? $json.statusCode : (httpErr ? 599 : 200);\nconst outage = httpErr || code === 429 || code >= 500; // only outages trip the breaker\n\nif (outage) {\n  cb.failures += 1;\n  if (cb.failures >= THRESHOLD) { cb.state = 'OPEN'; cb.openedAt = Date.now(); }\n} else { cb.failures = 0; cb.state = 'CLOSED'; }\nsd.breakers[carrier] = cb;\n\nconst MAP = { 'pre-transit': 'PRE_TRANSIT', 'transit': 'IN_TRANSIT', 'delivered': 'DELIVERED', 'failure': 'EXCEPTION', 'unknown': 'UNKNOWN' };\nlet canonical, raw = null;\nif (outage) { canonical = 'EXCEPTION'; }\nelse if (code >= 400) { canonical = 'UNKNOWN'; }   // e.g. 404 not-found for this tracking #\nelse { raw = $json.body?.shipments?.[0]?.status?.statusCode; canonical = MAP[raw] || 'UNKNOWN'; }\n\nreturn { json: { trackingNumber, carrier, canonicalStatus: canonical, rawStatus: raw,\n  breakerState: cb.state, httpStatus: code, checkedAt: now() } };"
      },
      "typeVersion": 2
    },
    {
      "id": "d8f0e345-95ea-4c73-bfea-8bfb53ebb2bf",
      "name": "Unknown carrier",
      "type": "n8n-nodes-base.set",
      "position": [
        3184,
        4832
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "9be11100-cb45-4a1a-956b-aa0ed2ed59d6",
              "name": "canonicalStatus",
              "type": "string",
              "value": "UNKNOWN"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "b05bd787-beda-432f-b03d-2efaf2dda9e1",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        4176,
        4224
      ],
      "parameters": {
        "numberInputs": 4
      },
      "typeVersion": 3
    },
    {
      "id": "f93e063c-f910-4ace-8227-b5e68ffc1402",
      "name": "Persist \u2192 Data Table (upsert)",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        4400,
        4256
      ],
      "parameters": {
        "columns": {
          "value": {},
          "mappingMode": "autoMapInputData",
          "matchingColumns": [
            "trackingNumber"
          ]
        },
        "filters": {
          "conditions": [
            {
              "keyName": "trackingNumber",
              "keyValue": "={{ $json.trackingNumber }}"
            }
          ]
        },
        "options": {},
        "operation": "upsert",
        "dataTableId": {
          "__rl": true,
          "mode": "name",
          "value": "shipment_tracking"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ee5c027f-b672-41b8-91f8-59f4416d8fe4",
      "name": "Sticky 20f4c",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3120,
        3552
      ],
      "parameters": {
        "color": 4,
        "width": 952,
        "height": 136,
        "content": "# Multi-Carrier Tracking Aggregator\nFedEx \u00b7 UPS \u00b7 DHL \u2192 one canonical status schema. Resilient by design: **circuit breaker \u00b7 rate limiting \u00b7 retries**.   \u2014 Built by Nodrel"
      },
      "typeVersion": 1
    },
    {
      "id": "36e444f8-1b75-4111-b352-0c8522fd3437",
      "name": "Sticky 88222",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2224,
        4112
      ],
      "parameters": {
        "color": 5,
        "width": 860,
        "height": 288,
        "content": "## 1 \u00b7 Ingest & route\nRead active shipments \u00b7 drop terminal \u00b7 route per carrier \u00b7 unmatched \u2192 UNKNOWN"
      },
      "typeVersion": 1
    },
    {
      "id": "a9463968-710a-419d-bcc5-8922083937e4",
      "name": "Sticky 8ce49",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3120,
        3696
      ],
      "parameters": {
        "color": 3,
        "width": 952,
        "height": 1080,
        "content": "## 2 \u00b7 Resilient carrier call  (\u00d73)"
      },
      "typeVersion": 1
    },
    {
      "id": "05dcae09-f13e-4bec-90d3-014e3f1d08f5",
      "name": "Sticky c6eb2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4112,
        4112
      ],
      "parameters": {
        "color": 6,
        "width": 492,
        "height": 336,
        "content": "## 3 \u00b7 Aggregate & persist\nMerge \u2192 Upsert on `trackingNumber`"
      },
      "typeVersion": 1
    },
    {
      "id": "e5eb7166-8202-413a-9da4-7859cdfe1fbb",
      "name": "Sticky 29f88",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3472,
        4784
      ],
      "parameters": {
        "color": 3,
        "width": 620,
        "height": 230,
        "content": "### How the resilience works\nPer branch: **Check Breaker** \u2192 **IF allowed** \u2192 **Track API** \u2192 **Normalize + Breaker**.\n\nBreaker state lives in workflow static data. Track API runs with **Continue-On-Fail + neverError**, so 4xx/5xx come back as data (not thrown) and the run completes \u2014 which is what lets the breaker state persist. Only 429/5xx/network errors trip the breaker; a 404 (unknown tracking #) does not."
      },
      "typeVersion": 1
    },
    {
      "id": "360b42ad-7a0e-45f9-a73c-62df99083fd5",
      "name": "Sticky b8971",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2224,
        4416
      ],
      "parameters": {
        "color": 7,
        "width": 900,
        "height": 348,
        "content": "## Configure once (not gaps \u2014 these are environment, not logic)\n**A. Credentials** (n8n never stores secrets in a workflow):\n\u2022 FedEx \u2192 OAuth2 API cred, token URL `https://apis.fedex.com/oauth/token`, grant = client credentials\n\u2022 UPS \u2192 OAuth2 API cred, token URL `https://onlinetools.ups.com/security/v1/oauth/token`\n\u2022 DHL \u2192 Header Auth cred, name `DHL-API-Key`, value = your key\nSelect each in its **Track API** node.\n\n**B. Data Table** \u2192 create `shipment_tracking` once, then it's auto-selected by name in Load + Persist.\nColumns: trackingNumber \u00b7 carrier \u00b7 canonicalStatus \u00b7 rawStatus \u00b7 breakerState \u00b7 httpStatus \u00b7 checkedAt\nSeed it by inserting rows (carrier lowercase: fedex/ups/dhl, canonicalStatus PRE_TRANSIT).\n\nThen **Publish** \u2014 static data persists only on active, production runs."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "nodeGroups": [],
  "connections": {
    "Merge": {
      "main": [
        [
          {
            "node": "Persist \u2192 Data Table (upsert)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Active only": {
      "main": [
        [
          {
            "node": "Switch by carrier",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DHL \u00b7 Skipped": {
      "main": [
        [
          {
            "node": "DHL \u00b7 Normalize + Breaker",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "UPS \u00b7 Skipped": {
      "main": [
        [
          {
            "node": "UPS \u00b7 Normalize + Breaker",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Unknown carrier": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "DHL \u00b7 Track API": {
      "main": [
        [
          {
            "node": "DHL \u00b7 Normalize + Breaker",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FEDEX \u00b7 Skipped": {
      "main": [
        [
          {
            "node": "FEDEX \u00b7 Normalize + Breaker",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Load active shipments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "UPS \u00b7 Track API": {
      "main": [
        [
          {
            "node": "UPS \u00b7 Normalize + Breaker",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DHL \u00b7 IF allowed": {
      "main": [
        [
          {
            "node": "DHL \u00b7 Track API",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "DHL \u00b7 Skipped",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch by carrier": {
      "main": [
        [
          {
            "node": "FEDEX \u00b7 Check Breaker",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "UPS \u00b7 Check Breaker",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "DHL \u00b7 Check Breaker",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Unknown carrier",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "UPS \u00b7 IF allowed": {
      "main": [
        [
          {
            "node": "UPS \u00b7 Track API",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "UPS \u00b7 Skipped",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FEDEX \u00b7 Track API": {
      "main": [
        [
          {
            "node": "FEDEX \u00b7 Normalize + Breaker",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FEDEX \u00b7 IF allowed": {
      "main": [
        [
          {
            "node": "FEDEX \u00b7 Track API",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "FEDEX \u00b7 Skipped",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DHL \u00b7 Check Breaker": {
      "main": [
        [
          {
            "node": "DHL \u00b7 IF allowed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "UPS \u00b7 Check Breaker": {
      "main": [
        [
          {
            "node": "UPS \u00b7 IF allowed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load active shipments": {
      "main": [
        [
          {
            "node": "Active only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FEDEX \u00b7 Check Breaker": {
      "main": [
        [
          {
            "node": "FEDEX \u00b7 IF allowed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DHL \u00b7 Normalize + Breaker": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "UPS \u00b7 Normalize + Breaker": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "FEDEX \u00b7 Normalize + Breaker": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}