{
  "id": "gDLUu13ouQw6hhsF",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Promo Code Expiry Alert System using Webhook, Google Sheets, Gmail & Slack",
  "tags": [],
  "nodes": [
    {
      "id": "8b5a7a7b-fec4-48c0-8dfc-75f2db56df2f",
      "name": "Receive Promo Request",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -656,
        -64
      ],
      "parameters": {
        "path": "27923033-a20e-4fc3-8bda-5308f6661a01",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2.1
    },
    {
      "id": "868d15bc-d75e-45af-9657-6b1cc40bbdaf",
      "name": "Normalize Promo Data",
      "type": "n8n-nodes-base.set",
      "position": [
        -352,
        -64
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "66113943-5c51-4825-acd6-f90fb1c2ad83",
              "name": "body.code",
              "type": "string",
              "value": "={{ $json.body.code }}"
            },
            {
              "id": "87925321-8d42-40ca-ae04-05d915598b4b",
              "name": "body.discount_type",
              "type": "string",
              "value": "={{ $json.body.discount_type }}"
            },
            {
              "id": "5141b5e6-1c1e-48d6-87c8-dbf7c53e8295",
              "name": "body.value",
              "type": "number",
              "value": "={{ $json.body.value }}"
            },
            {
              "id": "b7de7784-7564-440e-96ae-2d5f620c3573",
              "name": "body.expiry",
              "type": "string",
              "value": "={{ $json.body.expiry }}"
            },
            {
              "id": "d56b63af-b89d-40b1-8e7d-f574394dc4aa",
              "name": "body.usage_limit",
              "type": "number",
              "value": "={{ $json.body.usage_limit }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "b744deba-370b-4cd4-a3c2-89011a66e427",
      "name": "Expiry Is Valid",
      "type": "n8n-nodes-base.if",
      "position": [
        64,
        -64
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1cba2ed6-e3c2-48da-bf85-cb0f47162a1e",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ new Date($json[\"body\"][\"expiry\"]) > new Date() }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "64f64cfa-5098-49ff-b8f7-f75a4874aeb8",
      "name": "Send Promo Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        288,
        -80
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=<h2>\ud83c\udf89 New Promo Code Available!</h2> \n<p><b>Code:</b> {{ $('Expiry Is Valid').item.json.body.code }}</p> \n<p><b>Discount:</b> {{ $('Expiry Is Valid').item.json.body.value }}%</p> \n<p><b>Expiry:</b> {{ $('Expiry Is Valid').item.json.body.expiry }}</p>\n<p><b>Usage Limit:</b> {{ $('Expiry Is Valid').item.json.body.usage_limit }}</p>",
        "options": {
          "appendAttribution": false
        },
        "subject": "=\ud83c\udf89 New Promo Code: {{ $('Expiry Is Valid').item.json.body.code }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "57f812f3-3dc7-4d66-b62e-fc66f6da84f0",
      "name": "Save Promo to Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        528,
        -80
      ],
      "parameters": {
        "columns": {
          "value": {
            "Code": "={{ $('Expiry Is Valid').item.json.body.code }}",
            "Expiry": "={{ $('Expiry Is Valid').item.json.body.expiry }}",
            "Discount": "={{ $('Expiry Is Valid').item.json.body.value }}",
            "Usage Limit": "={{ $('Expiry Is Valid').item.json.body.usage_limit }}"
          },
          "schema": [
            {
              "id": "Code",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Code",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Discount",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Discount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Expiry",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Expiry",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Usage Limit",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Usage Limit",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Created At",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Created At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1KiQqP-eWsyCKWZa1X07rV0eGhk93TXC30uS9POyNGZQ",
          "cachedResultUrl": "",
          "cachedResultName": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "005324fd-512c-437f-94b6-f030888b69df",
      "name": "Calculate Days Left",
      "type": "n8n-nodes-base.set",
      "position": [
        944,
        -80
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "f6d0bdf2-d8bf-4baa-bd81-c507db8df7e2",
              "name": "daysLeft",
              "type": "string",
              "value": "={{ Math.ceil((new Date($json.Expiry) - new Date()) / (1000 * 60 * 60 * 24)) }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "085776b2-4a0a-49f4-84a3-629aac33ec4b",
      "name": "Is Urgent (1 Day Left)",
      "type": "n8n-nodes-base.if",
      "position": [
        1152,
        -80
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1755407a-ff14-4a9f-a7a7-e4e68dbfdfe8",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.daysLeft }}",
              "rightValue": "1"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "088fdaed-8e99-4756-9735-1d3ca3f4e472",
      "name": "Slack Urgent Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        1872,
        -368
      ],
      "parameters": {
        "text": "=\ud83d\udea8 URGENT: Promo Expiring Tomorrow!  Code: {{ $('Save Promo to Sheet').item.json.Code }} Discount:{{ $('Save Promo to Sheet').item.json.Discount }}% Expiry: {{ $('Save Promo to Sheet').item.json.Expiry }} \u26a1 Only 1 day left!",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09S57E2JQ2",
          "cachedResultName": "n8n"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "6b2e20f7-d4f6-4b9d-9a4b-71c190626df1",
      "name": "Is Warning (2\u20133 Days Left)",
      "type": "n8n-nodes-base.if",
      "position": [
        1728,
        256
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cdc81cf1-d994-45c3-a4b8-476685b62aeb",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.daysLeft > 1 && $json.daysLeft <= 3 }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ae3c3d89-517c-4c6e-8c29-c056d5a18ca1",
      "name": "Slack Warning Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        1984,
        272
      ],
      "parameters": {
        "text": "=\u26a0\ufe0f Promo Expiring Soon  Code:{{ $('Save Promo to Sheet').item.json.Code }}  Discount:{{ $('Save Promo to Sheet').item.json.Discount }} % Expiry:  {{ $('Save Promo to Sheet').item.json.Expiry }} \u23f3 Days Left: {{ $json.daysLeft }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09S57E2JQ2",
          "cachedResultName": "n8n"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "279483cf-1853-49cc-81ff-f681e530c25d",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -704,
        -240
      ],
      "parameters": {
        "color": 7,
        "width": 624,
        "height": 400,
        "content": "## Promo Data Ingestion & Preparation\nThis section captures incoming promo data via webhook and standardizes the payload into a consistent structure. It ensures all required fields like code, discount, expiry and usage limits are properly formatted before moving forward in the workflow for validation and processing."
      },
      "typeVersion": 1
    },
    {
      "id": "8aaa7edd-b206-4a07-85ba-a9ca8243b4c7",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        16,
        -240
      ],
      "parameters": {
        "color": 7,
        "width": 704,
        "height": 400,
        "content": "## Promo Validation & Storage\nThis section verifies whether the promo code is still valid based on its expiry date. If valid, it sends a notification email and stores the promo details in a Google Sheet, ensuring data persistence and enabling future tracking and analysis."
      },
      "typeVersion": 1
    },
    {
      "id": "a4bfad6e-48d0-4208-8fed-76dd333839ab",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        816,
        -240
      ],
      "parameters": {
        "color": 7,
        "width": 656,
        "height": 400,
        "content": "## Expiry Analysis & Urgency Detection\nThis section calculates the number of days remaining until the promo expires. It then evaluates whether the promo is critically close to expiry, specifically identifying cases where only one day is left, triggering high-priority alert conditions."
      },
      "typeVersion": 1
    },
    {
      "id": "5ed81672-7fb0-4ba0-ad63-a089ee2c1da6",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1616,
        -512
      ],
      "parameters": {
        "color": 7,
        "width": 688,
        "height": 336,
        "content": "## Urgent Alert Notification\nThis section handles high-priority alerts by sending an urgent Slack notification when a promo is about to expire within one day. It ensures immediate visibility to the team so they can take quick action before the offer becomes inactive."
      },
      "typeVersion": 1
    },
    {
      "id": "9422e74b-fc0a-4150-b434-7b63ad3a96c3",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1632,
        96
      ],
      "parameters": {
        "color": 7,
        "width": 688,
        "height": 336,
        "content": "## Warning Alert Notification\nThis section evaluates promos that are nearing expiry within two to three days. It sends a warning notification to Slack, helping teams stay informed in advance and take necessary actions before the promo reaches a critical expiration stage."
      },
      "typeVersion": 1
    },
    {
      "id": "76828cef-1046-4305-9fbe-eb07ef81215f",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1488,
        -912
      ],
      "parameters": {
        "width": 688,
        "height": 704,
        "content": "## How workflow workfs \n- The workflow begins when a webhook receives promo data from an external source such as a form, API or application.\n- Incoming data is transformed into a structured format to ensure consistency across all fields like code, discount, expiry and usage limit.\n- A validation check ensures that only non-expired promo codes are processed further in the workflow.\n- Once validated, a notification email is optionally sent and the promo details are stored in a Google Sheet for tracking and record-keeping.\n- The workflow then calculates the number of days remaining until the promo expires using the expiry date.\n- Based on the calculated value, it evaluates urgency conditions through decision nodes.\n- If only one day is left, an urgent Slack alert is triggered to notify the team immediately.\n- If the expiry is within two to three days, a warning Slack notification is sent to provide early awareness.\n- Promos outside these conditions are ignored, ensuring only relevant alerts are delivered.\n\n## Setup Steps\n- Create a new workflow in n8n and add a Webhook node configured to accept POST requests.\n- Add a Set node to normalize incoming data fields such as code, discount type, value, expiry and usage limit.\n- Configure an IF node to validate that the expiry date is greater than the current date.\n- Integrate Gmail (optional) to send email notifications for valid promo entries.\n- Add a Google Sheets node and connect your account to store promo data in a structured sheet.\n- Insert another Set node to calculate remaining days using a JavaScript expression.\n- Add decision nodes (IF) to check for urgency (1 day) and warning (2\u20133 days) conditions.\n- Connect Slack nodes and configure channels for urgent and warning alerts.\n- Test the workflow using sample webhook data and verify outputs in Slack and Google Sheets.\n- Activate the workflow to enable real-time automation and monitoring."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "937cfed7-34db-4bb3-bfae-ae5beae6ebcf",
  "connections": {
    "Expiry Is Valid": {
      "main": [
        [
          {
            "node": "Send Promo Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Promo Email": {
      "main": [
        [
          {
            "node": "Save Promo to Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Days Left": {
      "main": [
        [
          {
            "node": "Is Urgent (1 Day Left)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Promo to Sheet": {
      "main": [
        [
          {
            "node": "Calculate Days Left",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Promo Data": {
      "main": [
        [
          {
            "node": "Expiry Is Valid",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive Promo Request": {
      "main": [
        [
          {
            "node": "Normalize Promo Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Urgent (1 Day Left)": {
      "main": [
        [
          {
            "node": "Slack Urgent Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Is Warning (2\u20133 Days Left)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Warning (2\u20133 Days Left)": {
      "main": [
        [],
        [
          {
            "node": "Slack Warning Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}