AutomationFlowsWeb Scraping › Birthday Automation - Production (fixed)

Birthday Automation - Production (fixed)

Birthday Automation - Production (Fixed). Uses stopAndError, httpRequest, emailSend, bannerbear. Scheduled trigger; 86 nodes.

Cron / scheduled trigger★★★★★ complexity86 nodesStop And ErrorHTTP RequestEmail SendBannerbear
Web Scraping Trigger: Cron / scheduled Nodes: 86 Complexity: ★★★★★ Added:
Birthday Automation - Production (fixed) — n8n workflow card showing Stop And Error, HTTP Request, Email Send integration

This workflow follows the Emailsend → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

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

Download .json
{
  "name": "Birthday Automation - Production (Fixed)",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -2368,
        2288
      ],
      "id": "ed5d96b1-59c1-45df-b7bd-fea2b135b364",
      "name": "Schedule 8 AM"
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 12
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -2368,
        2512
      ],
      "id": "2fbd0d19-4446-444e-be93-4e1b47e2f311",
      "name": "Schedule 12 PM"
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 18
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -2368,
        2736
      ],
      "id": "703118fe-b914-4c60-adc3-9deb22878e90",
      "name": "Schedule 6 PM"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "exec-id",
              "name": "execution_id",
              "value": "={{ $execution.id }}",
              "type": "string"
            },
            {
              "id": "backend-url",
              "name": "backend_url",
              "value": "http://127.0.0.1:4000",
              "type": "string"
            },
            {
              "id": "service-token",
              "name": "service_token",
              "value": "dev_n8n_service_token_0123456789abcdef0123456789abcdef",
              "type": "string"
            },
            {
              "id": "current-year",
              "name": "current_year",
              "value": "={{ Number($now.setZone('Asia/Kolkata').toFormat('yyyy')) }}",
              "type": "number"
            },
            {
              "id": "alert-email",
              "name": "alert_email",
              "value": "adrshs535@gmail.com",
              "type": "string"
            },
            {
              "id": "af4eeee9-24b0-4da6-8764-7fd572b4894c",
              "name": "env_mode",
              "value": "={{ $env.N8N_ENV || 'DEV' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -2144,
        1664
      ],
      "id": "4c8052ef-07e8-4ebc-9bb9-6b7480cb93f9",
      "name": "Set Global Vars"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 3
          },
          "conditions": [
            {
              "id": "132e39a5-052a-41ba-868a-f8f8a2c0c07e",
              "leftValue": "={{ $('Set Global Vars').first().json.env_mode }}",
              "rightValue": "PROD",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        -1920,
        1664
      ],
      "id": "13198c32-8b1a-4261-b854-0bf6abc7e01e",
      "name": "Production Guard"
    },
    {
      "parameters": {
        "errorMessage": "=Workflow running in {{ $('Set Global Vars').first().json.env_mode }} mode - DRY RUN only. Set N8N_ENV=PROD to enable production mode."
      },
      "type": "n8n-nodes-base.stopAndError",
      "typeVersion": 1,
      "position": [
        -1696,
        1568
      ],
      "id": "6a880c05-99a8-45e4-8f9e-da889f96fa9d",
      "name": "Stop and Error"
    },
    {
      "parameters": {
        "url": "={{ $('Set Global Vars').first().json.backend_url }}/api/system/config",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-service-token",
              "value": "={{ $('Set Global Vars').first().json.service_token }}"
            }
          ]
        },
        "options": {
          "timeout": 5000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        -1696,
        1760
      ],
      "id": "NEW-check-system-status",
      "name": "Check System Status",
      "alwaysOutputData": true,
      "continueOnFail": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "system-paused",
              "leftValue": "={{ $json.system_paused }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -1472,
        1760
      ],
      "id": "NEW-system-paused-check",
      "name": "System Paused?",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "fromEmail": "info@diplomaticresidences.in",
        "toEmail": "={{ $('Set Global Vars').first().json.alert_email }}",
        "subject": "\ud83d\udea8 Birthday Workflow PAUSED",
        "message": "=The birthday automation workflow has been paused.\n\nReason: {{ $('Check System Status').first().json.reason }}\n\nTo resume, update system config or run:\nPUT {{ $('Set Global Vars').first().json.backend_url }}/api/system/pause\nBody: {\"paused\": false}\n\nExecution ID: {{ $execution.id }}",
        "options": {}
      },
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        -1248,
        1856
      ],
      "id": "NEW-alert-system-paused",
      "name": "Alert System Paused",
      "alwaysOutputData": true,
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "errorMessage": "=System is paused: {{ $('Check System Status').first().json.reason }}"
      },
      "type": "n8n-nodes-base.stopAndError",
      "typeVersion": 1,
      "position": [
        -1024,
        1856
      ],
      "id": "NEW-stop-system-paused",
      "name": "Stop (System Paused)"
    },
    {
      "parameters": {
        "url": "={{ $('Set Global Vars').first().json.backend_url }}/health",
        "options": {
          "timeout": 5000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        -1248,
        1664
      ],
      "id": "9016aa9f-dba5-4faf-bfb4-21be620da693",
      "name": "Check Backend Health",
      "alwaysOutputData": true,
      "continueOnFail": true
    },
    {
      "parameters": {
        "url": "https://live-mt-server.wati.io/304793/api/v1/getMessageTemplates?channelPhoneNumber=918287949739",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {
          "timeout": 5000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        -1024,
        1664
      ],
      "id": "1271fc28-da7e-425a-9684-4727a9502b54",
      "name": "Check WATI Health",
      "alwaysOutputData": true,
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "continueOnFail": true
    },
    {
      "parameters": {
        "jsCode": "const backend = $('Check Backend Health').first()?.json;\nconst wati = $('Check WATI Health').first()?.json;\n\nconst backendOk = backend?.status === 'ok';\n\nconst watiOk =\n  wati?.result === 'success' ||\n  Array.isArray(wati?.messageTemplates);\n\nconst watiHasError =\n  !!wati?.error ||\n  !!wati?.errors ||\n  wati?.result === 'error';\n\nconst allHealthy = backendOk && watiOk && !watiHasError;\n\nreturn [{\n  json: {\n    all_healthy: allHealthy,\n    backend_status: backendOk ? 'OK' : 'DOWN',\n    wati_status: (watiOk && !watiHasError) ? 'OK' : 'DOWN',\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -800,
        1664
      ],
      "id": "853f16bb-2f3b-4f54-b5c2-401e7f640aa0",
      "name": "Check All Providers",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "all-healthy",
              "leftValue": "={{ $json.all_healthy }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -576,
        1664
      ],
      "id": "65f99190-36ce-4ffa-93ed-41f5cf7e2e89",
      "name": "All Providers Healthy?",
      "alwaysOutputData": true,
      "retryOnFail": true
    },
    {
      "parameters": {
        "fromEmail": "info@diplomaticresidences.in",
        "toEmail": "={{ $('Set Global Vars').first().json.alert_email }}",
        "subject": "\ud83d\udea8 Birthday Workflow - Providers Down",
        "options": {}
      },
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        -352,
        1760
      ],
      "id": "806fe094-c9ef-4082-9220-53deddb5205d",
      "name": "Alert Providers Down",
      "alwaysOutputData": true,
      "retryOnFail": true,
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "errorMessage": "Providers are down. Workflow stopped."
      },
      "type": "n8n-nodes-base.stopAndError",
      "typeVersion": 1,
      "position": [
        -128,
        1760
      ],
      "id": "ba2c2b3a-9ec6-4748-921f-389a34978526",
      "name": "Stop Workflow"
    },
    {
      "parameters": {
        "url": "={{ $('Set Global Vars').first().json.backend_url }}/api/contacts/upcoming?days=1&limit=100",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-service-token",
              "value": "={{ $('Set Global Vars').first().json.service_token }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        -352,
        1488
      ],
      "id": "9f17f338-67a7-49c3-bfa0-b62fcf5dd72a",
      "name": "Fetch Birthday Contacts"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "has-contacts",
              "leftValue": "={{ $json.total || 0 }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -128,
        1488
      ],
      "id": "27be2001-4b5f-44d7-96c4-4aaf61c235dd",
      "name": "Has Birthdays?",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "jsCode": "const allInputs = $input.all();\nconst contacts = allInputs.flatMap(i => i.json.contacts || []);\nreturn contacts.map(contact => ({ json: contact }));"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        96,
        1488
      ],
      "id": "371b6202-d0f9-4dfe-bd40-e8bfbc9b2e1b",
      "name": "Split Contacts"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "contact-id",
              "name": "contact_id",
              "value": "={{ $json.id }}",
              "type": "string"
            },
            {
              "id": "name",
              "name": "name",
              "value": "={{ $json.name }}",
              "type": "string"
            },
            {
              "id": "email",
              "name": "email",
              "value": "={{ ($json.email || '').toLowerCase().trim() }}",
              "type": "string"
            },
            {
              "id": "phone",
              "name": "phone",
              "value": "={{ ($json.phone || '').replace(/[^0-9]/g, '') }}",
              "type": "string"
            },
            {
              "id": "country-code",
              "name": "country_code",
              "value": "={{ $json.countryCode || '+91' }}",
              "type": "string"
            },
            {
              "id": "full-phone",
              "name": "full_phone",
              "value": "={{ ($json.countryCode || '+91').replace('+','') + ($json.phone || '').replace(/[^0-9]/g, '') }}",
              "type": "string"
            },
            {
              "id": "optout",
              "name": "optout",
              "value": "={{ ($json.optedOut === true || $json.optout === true || $json.opted_out === true) ? 'yes' : 'no' }}\n",
              "type": "string"
            },
            {
              "id": "active",
              "name": "active",
              "value": "={{ $json.active !== false ? 'yes' : 'no' }}",
              "type": "string"
            },
            {
              "id": "last-sent-year",
              "name": "last_sent_year",
              "value": "={{ $json.lastSentYear || 0 }}",
              "type": "number"
            },
            {
              "id": "needs-review",
              "name": "needs_review",
              "value": "={{ $json.needsReview || false }}",
              "type": "boolean"
            },
            {
              "id": "image-url-png",
              "name": "image_url_png",
              "value": "",
              "type": "string"
            },
            {
              "id": "image-url-jpg",
              "name": "image_url_jpg",
              "value": "",
              "type": "string"
            },
            {
              "id": "email-sent",
              "name": "email_sent",
              "value": false,
              "type": "boolean"
            },
            {
              "id": "whatsapp-sent",
              "name": "whatsapp_sent",
              "value": false,
              "type": "boolean"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        320,
        1488
      ],
      "id": "35e78168-0a7f-4d38-9436-eaa4beff85ce",
      "name": "Extract Contact Data"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "needs-review",
              "leftValue": "={{ $json.needs_review }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        544,
        1488
      ],
      "id": "529bc907-3180-4754-9d67-529627ba29b6",
      "name": "Needs Review?",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=http://127.0.0.1:4000/api/events",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"execution_id\": \"{{ $('Set Global Vars').first().json.execution_id }}\",\n  \"client_id\": \"demo\",\n  \"contact_id\": \"{{ $('Extract Contact Data').item.json.contact_id }}\",\n  \"channel\": \"EMAIL\",\n  \"provider\": \"OTHER\",\n  \"status\": \"SKIPPED\",\n  \"error_code\": \"NEEDS_REVIEW\",\n  \"error_reason\": \"Contact needs review\",\n  \"payload_snapshot\": {\n    \"name\": \"{{ $('Extract Contact Data').item.json.name }}\",\n    \"email\": \"{{ $('Extract Contact Data').item.json.email }}\",\n    \"full_phone\": \"{{ $('Extract Contact Data').item.json.full_phone }}\",\n    \"needs_review\": \"{{ $('Extract Contact Data').item.json.needs_review }}\"\n  }\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        768,
        1392
      ],
      "id": "f82736ff-5c9f-4e22-bffb-c2de3dc6055c",
      "name": "Log Needs Review",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "optout",
              "leftValue": "={{ $json.optout }}",
              "rightValue": "yes",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        768,
        1488
      ],
      "id": "34d6f620-663c-4404-981f-2c743c8e4de2",
      "name": "Optout?",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=http://127.0.0.1:4000/api/events",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"execution_id\": \"{{ $('Set Global Vars').first().json.execution_id }}\",\n  \"client_id\": \"demo\",\n  \"contact_id\": \"{{ $('Extract Contact Data').item.json.contact_id }}\",\n  \"channel\": \"EMAIL\",\n  \"provider\": \"OTHER\",\n  \"status\": \"SKIPPED\",\n  \"error_code\": \"OPTED_OUT\",\n  \"error_reason\": \"Contact opted out\",\n  \"payload_snapshot\": {\n    \"name\": \"{{ $('Extract Contact Data').item.json.name }}\",\n    \"email\": \"{{ $('Extract Contact Data').item.json.email }}\",\n    \"full_phone\": \"{{ $('Extract Contact Data').item.json.full_phone }}\"\n  }\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        992,
        1392
      ],
      "id": "d350f790-0195-4a4c-99ce-4adc0643a9ab",
      "name": "Log Optout",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "active",
              "leftValue": "={{ $json.active }}",
              "rightValue": "yes",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        992,
        1488
      ],
      "id": "35865675-5bf7-4168-a4f0-e0d0181c8b39",
      "name": "Active?",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=http://127.0.0.1:4000/api/events",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"execution_id\": \"{{ $('Set Global Vars').first().json.execution_id }}\",\n  \"client_id\": \"demo\",\n  \"contact_id\": \"{{ $('Extract Contact Data').item.json.contact_id }}\",\n  \"channel\": \"EMAIL\",\n  \"provider\": \"OTHER\",\n  \"status\": \"SKIPPED\",\n  \"error_code\": \"INACTIVE\",\n  \"error_reason\": \"Contact inactive\",\n  \"payload_snapshot\": {\n    \"name\": \"{{ $('Extract Contact Data').item.json.name }}\",\n    \"email\": \"{{ $('Extract Contact Data').item.json.email }}\",\n    \"full_phone\": \"{{ $('Extract Contact Data').item.json.full_phone }}\"\n  }\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        1216,
        1392
      ],
      "id": "7ad94dca-757e-44a6-8847-0473538b934b",
      "name": "Log Inactive",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "already-sent",
              "leftValue": "={{ $json.last_sent_year }}",
              "rightValue": "={{ $('Set Global Vars').first().json.current_year }}",
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1216,
        1488
      ],
      "id": "75cff781-055a-470d-8069-5d99f365980c",
      "name": "Already Sent?",
      "alwaysOutputData": true,
      "disabled": false
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=http://127.0.0.1:4000/api/events",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"execution_id\": \"{{ $('Set Global Vars').first().json.execution_id }}\",\n  \"client_id\": \"demo\",\n  \"contact_id\": \"{{ $('Extract Contact Data').item.json.contact_id }}\",\n  \"channel\": \"EMAIL\",\n  \"provider\": \"OTHER\",\n  \"status\": \"SKIPPED\",\n  \"error_code\": \"ALREADY_SENT\",\n  \"error_reason\": \"Already sent this year\",\n  \"payload_snapshot\": {\n    \"name\": \"{{ $('Extract Contact Data').item.json.name }}\",\n    \"email\": \"{{ $('Extract Contact Data').item.json.email }}\",\n    \"full_phone\": \"{{ $('Extract Contact Data').item.json.full_phone }}\",\n    \"last_sent_year\": \"{{ $('Extract Contact Data').item.json.last_sent_year }}\"\n  }\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        1440,
        1392
      ],
      "id": "4b4ef6c3-2b5f-41c0-9261-dca86dda87ed",
      "name": "Log Already Sent",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "amount": 10
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        1216,
        1264
      ],
      "id": "eecebab7-40c0-4c98-9a85-9666ccc40b30",
      "name": "End (Skipped Contact)"
    },
    {
      "parameters": {
        "templateId": "y1vwRJZeqlE8ZKNjxg",
        "additionalFields": {},
        "modificationsUi": {
          "modificationsValues": [
            {
              "name": "recipient_name",
              "text": "={{ $json.name }}"
            },
            {
              "name": "event_date",
              "text": "={{ $now.setZone('Asia/Kolkata').toFormat('dd-LL-yyyy') }}"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.bannerbear",
      "typeVersion": 1,
      "position": [
        1440,
        1488
      ],
      "id": "1ca66ea7-9f6e-437c-b9b7-81fdcb5011fb",
      "name": "Create Bannerbear Image",
      "credentials": {
        "bannerbearApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "retry",
              "name": "retry_count",
              "value": "={{ 0 }}",
              "type": "number"
            },
            {
              "id": "image-uid",
              "name": "image_uid",
              "value": "={{ $json.uid }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1664,
        1488
      ],
      "id": "cb45b02d-32c5-46e1-98ae-6558896c86e1",
      "name": "Init Retry Count"
    },
    {
      "parameters": {
        "amount": 10
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        1888,
        1488
      ],
      "id": "238618a3-17c5-4bd7-8e99-4d11b49f9e7d",
      "name": "Wait 10s"
    },
    {
      "parameters": {
        "operation": "get",
        "imageId": "={{ $json.image_uid }}"
      },
      "type": "n8n-nodes-base.bannerbear",
      "typeVersion": 1,
      "position": [
        2112,
        1488
      ],
      "id": "b405ae98-21b9-4180-8def-a91e6f0ec778",
      "name": "Get Image Status",
      "credentials": {
        "bannerbearApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "status-complete",
              "leftValue": "={{ $json.status }}",
              "rightValue": "completed",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            },
            {
              "id": "has-url",
              "leftValue": "={{ $json.image_url }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "singleValue": true
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        2336,
        1488
      ],
      "id": "45f1dcd2-8a1d-4e32-a401-b7168ae61cb9",
      "name": "Image Ready?",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "inc",
              "name": "retry_count",
              "value": "={{ Number($json.retry_count ?? 0) + 1 }}",
              "type": "number"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2336,
        1632
      ],
      "id": "980624ea-fb48-40f6-b678-5ea6907cadba",
      "name": "Increment Retry"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "max",
              "leftValue": "={{ Number($json.retry_count) }}",
              "rightValue": 12,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        2560,
        1632
      ],
      "id": "f358b45c-9a31-4c87-8edd-ae190794cd5f",
      "name": "Retry Limit?",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "errorMessage": "=Image not ready after {{ $json.retry_count }} retries"
      },
      "type": "n8n-nodes-base.stopAndError",
      "typeVersion": 1,
      "position": [
        2784,
        1536
      ],
      "id": "3fcb76a6-3b2c-4d6c-823c-b6db1aef16dc",
      "name": "Max Retries"
    },
    {
      "parameters": {
        "amount": 10
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        2784,
        1728
      ],
      "id": "8ef2b460-4080-487d-9ace-a421de3a71eb",
      "name": "Wait Retry"
    },
    {
      "parameters": {
        "mode": "combine",
        "combineBy": "combineByPosition",
        "options": {}
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        2560,
        1488
      ],
      "id": "94fbe10d-46f7-41f9-ab0d-f6f63772266f",
      "name": "Image"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "png",
              "name": "image_url_png",
              "value": "={{ $json.image_url }}",
              "type": "string"
            },
            {
              "id": "jpg",
              "name": "image_url_jpg",
              "value": "={{ $json.image_url ? $json.image_url.replace('.png', '.jpg') : '' }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2784,
        1488
      ],
      "id": "d1cc7e2e-d1ac-4837-85d8-8084af319efe",
      "name": "Save Image URLs"
    },
    {
      "parameters": {
        "amount": 2
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        3008,
        1488
      ],
      "id": "25cb1f44-3288-45f0-803e-237ca4ccca65",
      "name": "Rate Limit Before Send"
    },
    {
      "parameters": {
        "url": "={{ $('Save Image URLs').item.json.image_url_png }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file",
              "outputPropertyName": "birthday_image"
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        3232,
        1392
      ],
      "id": "21aa3b92-6b8f-4b67-a5e5-f3d7c6297124",
      "name": "Download Image (Email)"
    },
    {
      "parameters": {
        "fromEmail": "info@diplomaticresidences.in",
        "toEmail": "={{ $('Extract Contact Data').item.json.email }}",
        "subject": "={{ `Happy Birthday, ${$('Extract Contact Data').item.json.name} - from Puri Constructions` }}",
        "html": "=<!DOCTYPE html>\n<html lang=\"en\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <meta name=\"x-apple-disable-message-reformatting\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <title>Birthday Greetings | Puri Constructions</title>\n  <style>\n    @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700&family=Inter:wght@400;700&display=swap');\n    \n    table, td { mso-table-lspace: 0pt !important; mso-table-rspace: 0pt !important; }\n    img { -ms-interpolation-mode: bicubic; }\n    body { margin: 0; padding: 0; width: 100% !important; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; background-color: #F8FAFC; }\n    \n    .externalClass { width: 100%; }\n    .apple-link a { color: inherit !important; text-decoration: none !important; }\n    \n    @media screen and (max-width: 600px) {\n      .container { width: 100% !important; padding: 10px !important; }\n      .hero-image { border-radius: 12px !important; }\n      .content-box { padding: 25px 20px !important; }\n    }\n  </style>\n</head>\n<body style=\"font-family: 'Inter', Arial, sans-serif; background-color: #F8FAFC;\">\n  <div role=\"article\" aria-roledescription=\"email\" lang=\"en\" style=\"background-color: #F8FAFC;\">\n    \n    <div style=\"display: none; max-height: 0px; overflow: hidden;\">\n      A special birthday wish for you from the team at Puri Constructions.\n    </div>\n\n    <table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" style=\"background-color: #F8FAFC;\">\n      <tr>\n        <td align=\"center\" style=\"padding: 40px 0;\">\n          \n          <table class=\"container\" role=\"presentation\" width=\"600\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" style=\"width: 600px; background-color: #ffffff; border-radius: 24px; overflow: hidden; box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1);\">\n            \n            <tr>\n              <td align=\"center\" style=\"padding: 20px 0; background-color: #0F172A;\">\n                <h2 style=\"margin: 0; color: #ffffff; font-family: 'Playfair Display', serif; letter-spacing: 2px; font-size: 18px; text-transform: uppercase;\">Puri Constructions</h2>\n              </td>\n            </tr>\n\n            <tr>\n              <td style=\"padding: 20px 20px 0 20px;\">\n                <img src=\"cid:birthday_image\" alt=\"Happy Birthday\" width=\"560\" style=\"display: block; width: 100%; height: auto; border-radius: 16px; border: 1px solid #E2E8F0;\" class=\"hero-image\">\n              </td>\n            </tr>\n\n            <tr>\n              <td class=\"content-box\" style=\"padding: 40px 45px;\">\n                <table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\">\n                  <tr>\n                    <td>\n                      <h1 style=\"margin: 0 0 15px 0; font-family: 'Playfair Display', serif; font-size: 28px; color: #1E293B;\">\nDear {{ $('Extract Contact Data').item.json.name || 'Valued Customer' }},\n</h1>\n                      <p style=\"margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #475569;\">\n                        On this special day, the entire team at <strong>Puri Constructions</strong> joins in wishing you a very \n                        <span style=\"color: #0F172A; font-weight: 700;\">Happy Birthday!</span>\n                      </p>\n                      \n                      <table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" style=\"background-color: #F1F5F9; border-left: 4px solid #0F172A; border-radius: 8px;\">\n                        <tr>\n                          <td style=\"padding: 20px;\">\n                            <p style=\"margin: 0; font-style: italic; font-size: 15px; color: #334155; line-height: 1.5;\">\n                              \"May your year be filled with prosperity, good health, and success in every endeavor. We are honored to have you with us.\"\n                            </p>\n                          </td>\n                        </tr>\n                      </table>\n\n                      <p style=\"margin: 30px 0 0 0; font-size: 16px; color: #475569;\">\n                        Warm regards,<br>\n                        <strong style=\"color: #0F172A;\">Team Puri Constructions</strong>\n                      </p>\n                    </td>\n                  </tr>\n                </table>\n              </td>\n            </tr>\n\n            <tr>\n              <td align=\"center\" style=\"padding: 30px 45px; background-color: #F8FAFC; border-top: 1px solid #E2E8F0;\">\n                <p style=\"margin: 0; font-size: 12px; color: #94A3B8; line-height: 1.5;\">\n                  You are receiving this because you are a valued contact of Puri Constructions Pvt. Ltd.<br>\n                  <a href=\"#\" style=\"color: #64748B; text-decoration: underline;\">Unsubscribe</a> | <a href=\"https://www.puriconstructions.com/\" style=\"color: #64748B; text-decoration: underline;\">Visit Website</a>\n                </p>\n              </td>\n            </tr>\n          </table>\n          </td>\n      </tr>\n    </table>\n  </div>\n</body>\n</html>",
        "options": {
          "attachments": "birthday_image"
        }
      },
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        3456,
        1392
      ],
      "id": "6273b5fd-26fe-4c09-8d2e-f4992ad8ffa2",
      "name": "Send Email",
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "continueOnFail": true
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://live-mt-server.wati.io/304793/api/v1/sendTemplateMessage?whatsappNumber={{ $('Extract Contact Data').item.json.full_phone }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "accept",
              "value": "*/*"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"template_name\": \"birthday_dynamic_final\",\n  \"broadcast_name\": \"birthday_automation\",\n  \"parameters\": [\n    {\n      \"name\": \"product_image_url\",\n      \"value\": {{ JSON.stringify($('Save Image URLs').item.json.image_url_jpg) }}\n    },\n    {\n      \"name\": \"1\",\n     \"value\": \"{{ $('Extract Contact Data').item.json.name }}\"\n    }\n  ]\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3456,
        1584
      ],
      "id": "46a5ae31-d93f-448d-9c4d-98b5a45102b3",
      "name": "Send WhatsApp",
      "alwaysOutputData": true,
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "continueOnFail": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "success-check",
              "leftValue": "={{ $json.rejected?.length || 0 }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            },
            {
              "id": "has-message-id",
              "leftValue": "={{ $json.messageId }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "singleValue": true
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        3680,
        1392
      ],
      "id": "51c8efcf-d523-485f-b710-7fb79dc3c597",
      "name": "Email Success?",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "success",
              "leftValue": "={{ $json.result }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        3680,
        1584
      ],
      "id": "08d64433-1f13-4bb2-b2f9-2946af443b6f",
      "name": "WhatsApp Success?",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=http://127.0.0.1:4000/api/events",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"execution_id\": \"{{ $('Set Global Vars').first().json.execution_id }}\",\n  \"client_id\": \"demo\",\n  \"contact_id\": \"{{ $('Extract Contact Data').item.json.contact_id }}\",\n  \"channel\": \"EMAIL\",\n  \"provider\": \"SMTP\",\n  \"status\": \"SENT\",\n  \"provider_message_id\": \"{{ $('Send Email').item.json.messageId || '' }}\",\n  \"payload_snapshot\": {\n    \"email\": \"{{ $('Extract Contact Data').item.json.email }}\",\n    \"subject\": \"{{ `Happy Birthday, ${$('Extract Contact Data').item.json.name}` }}\",\n    \"contact_name\": \"{{ $('Extract Contact Data').item.json.name }}\",\n    \"from\": \"info@diplomaticresidences.in\"\n  }\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        3904,
        1296
      ],
      "id": "c2018274-3f9c-4e87-8be7-a908672ede2b",
      "name": "Log Email Success",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=http://127.0.0.1:4000/api/events",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"execution_id\": \"={{ $('Set Global Vars').first().json.execution_id }}\",\n  \"client_id\": \"demo\",\n  \"contact_id\": \"={{ $('Extract Contact Data').item.json.contact_id }}\",\n  \"channel\": \"WHATSAPP\",\n  \"provider\": \"OTHER\",\n  \"status\": \"SENT\",\n  \"provider_message_id\": \"={{ $json.contact?.id || '' }}\",\n  \"payload_snapshot\": {\n    \"actual_provider\": \"WATI\",\n    \"phone\": \"={{ $('Extract Contact Data').item.json.full_phone }}\",\n    \"template\": \"birthday_dynamic_final\",\n    \"contact_name\": \"={{ $('Extract Contact Data').item.json.name }}\",\n    \"wati_contact_id\": \"={{ $json.contact?.id || '' }}\"\n  }\n}",
        "options": {
          "timeout": 10000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3904,
        1488
      ],
      "id": "e3f5ee51-a512-4824-80ac-5199880079e1",
      "name": "Log WhatsApp Success",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "email-success",
              "name": "email_sent",
              "value": true,
              "type": "boolean"
            },
            {
              "id": "c1145a78-c209-4ff1-a9f2-8a897235566d",
              "name": "contact_id",
              "value": "={{ $('Extract Contact Data').item.json.contact_id }}",
              "type": "string"
            },
            {
              "id": "6af5cd35-6abb-4337-b381-51be0a6febdf",
              "name": "name",
              "value": "={{ $('Extract Contact Data').item.json.name }}",
              "type": "string"
            },
            {
              "id": "951b8249-5a4e-44b5-bf2f-ff4752474692",
              "name": "email",
              "value": "={{ $('Extract Contact Data').item.json.email }}",
              "type": "string"
            },
            {
              "id": "bf773b5f-043e-4b7c-a68d-1712371bf8c7",
              "name": "full_phone",
              "value": "={{ $('Extract Contact Data').item.json.full_phone }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        4128,
        1296
      ],
      "id": "c2a8d06b-142d-4df5-8c6d-312d6cc91af1",
      "name": "Set Email Success Flag"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "whatsapp-success",
              "name": "whatsapp_sent",
              "value": true,
              "type": "boolean"
            },
            {
              "id": "afa8db08-4612-4a84-9371-ff0aadf4260c",
              "name": "name",
              "value": "={{ $('Extract Contact Data').item.json.name }}",
              "type": "string"
            },
            {
              "id": "c2eda830-999a-443c-8edf-3c1dc317eed2",
              "name": "email",
              "value": "={{ $('Extract Contact Data').item.json.email }}",
              "type": "string"
            },
            {
              "id": "8d1765be-9566-4882-8ded-bc160c7c570a",
              "name": "full_phone",
              "value": "={{ $('Extract Contact Data').item.json.full_phone }}",
              "type": "string"
            },
            {
              "id": "0092e3eb-0904-4097-b7ea-8b8973b86c61",
              "name": "contact_id",
              "value": "={{ $('Extract Contact Data').item.json.contact_id }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        4128,
        1488
      ],
      "id": "1eb40183-0a7d-4acc-892d-dba0d7fa5c03",
      "name": "Set WhatsApp Success Flag"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const response = $json || {};\n\nlet errorCode = 'SMTP_ERROR';\nlet errorReason = 'SMTP send failed';\nlet isRetryable = false;\n\nif (Array.isArray(response?.rejected) && response.rejected.length > 0) {\n  errorCode = 'INVALID_EMAIL';\n  errorReason = `Email rejected: ${response.rejected.join(', ')}`;\n  isRetryable = false;\n\n} else if (response?.error?.code === 'EAUTH' || response?.code === 'EAUTH') {\n  errorCode = 'AUTH_FAILED';\n  errorReason = 'SMTP authentication failed';\n  isRetryable = false;\n\n} else if (response?.error?.code === 'ETIMEDOUT' || response?.code === 'ETIMEDOUT') {\n  errorCode = 'NETWORK_TIMEOUT';\n  errorReason = 'SMTP connection timeout';\n  isRetryable = true;\n\n} else if (response?.error?.code === 'ECONNREFUSED' || response?.code === 'ECONNREFUSED') {\n  errorCode = 'NETWORK_TIMEOUT';\n  errorReason = 'SMTP server refused connection';\n  isRetryable = true;\n\n} else if (\n  (response?.error?.message || response?.message || '').toLowerCase().includes('quota') ||\n  (response?.error?.message || response?.message || '').toLowerCase().includes('limit')\n) {\n  errorCode = 'QUOTA_EXCEEDED';\n  errorReason = 'Daily email quota exceeded';\n  isRetryable = true;\n\n} else if (response?.error?.message) {\n  errorReason = response.error.message;\n\n} else if (!response || Object.keys(response).length === 0) {\n  errorCode = 'NETWORK_TIMEOUT';\n  errorReason = 'No response from SMTP server';\n  isRetryable = true;\n}\n\nconst contact = $('Extract Contact Data').item?.json || {};\n\nreturn {\n  error_code: errorCode,\n  error_reason: errorReason,\n  is_retryable: isRetryable,\n  retry_count: 0,\n  rejected_emails: response?.rejected || [],\n  original_response: response,\n  contact_id: contact.contact_id,\n  name: contact.name,\n  email: contact.email,\n  full_phone: contact.full_phone,\n  email_sent: false,\n  whatsapp_sent: contact.whatsapp_sent === true\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3904,
        1392
      ],
      "id": "b9be732a-4cb3-42c9-b79a-cba371bdac26",
      "name": "Extract Email Error"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const response = $json || {};\n\nconst contact = $('Extract Contact Data').item?.json || {};\n\nlet errorCode = 'UNKNOWN_ERROR';\nlet errorReason = 'Unknown WhatsApp failure';\nlet isRetryable = false;\n\nif (response?.info?.[0]?.error) {\n  errorCode = response.info[0].error.code || 'WATI_ERROR';\n  errorReason = response.info[0].error.message || 'WATI API error';\n  isRetryable = ['RATE_LIMIT', 'TIMEOUT'].includes(String(errorCode).toUpperCase());\n}\nelse if (response?.error?.code) {\n  errorCode = response.error.code;\n  errorReason = response.error.message || 'WATI API error';\n  isRetryable = ['RATE_LIMIT', 'TIMEOUT'].includes(String(errorCode).toUpperCase());\n}\nelse if (response?.result === false) {\n  errorCode = 'WATI_SEND_FAILED';\n  errorReason = 'WATI API returned false';\n  isRetryable = false;\n}\nelse if (!response || Object.keys(response).length === 0) {\n  errorCode = 'NETWORK_TIMEOUT';\n  errorReason = 'WhatsApp API did not respond';\n  isRetryable = true;\n}\n\nconst codeUpper = String(errorCode || '').toUpperCase();\nconst reasonLower = String(errorReason || '').toLowerCase();\n\nconst isMetaRestricted =\n  codeUpper.includes('RESTRICT') ||\n  reasonLower.includes('restricted') ||\n  reasonLower.includes('policy') ||\n  reasonLower.includes('violat') ||\n  reasonLower.includes('temporarily blocked');\n\nif (isMetaRestricted) {\n  errorCode = 'META_RESTRICTED';\n  errorReason = errorReason || 'Meta restricted the WhatsApp business/account';\n  isRetryable = false;\n}\n\nreturn {\n  error_code: errorCode,\n  error_reason: errorReason,\n  is_retryable: isRetryable,\n  original_response: response,\n  contact_id: contact.contact_id,\n  name: contact.name,\n  full_phone: contact.full_phone,\n  email_sent: contact.email_sent || false,\n  whatsapp_sent: false\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3904,
        1584
      ],
      "id": "eeef6de9-a41d-42ce-9dee-c51da71b6d3a",
      "name": "Extract WhatsApp Error"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "retryable",
              "leftValue": "={{ $json.is_retryable }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        4128,
        1392
      ],
      "id": "3df4a4a5-a22e-4389-aa0b-046f9a2c4a7b",
      "name": "Email Retryable?",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "retryable-wa",
              "leftValue": "={{ $json.is_retryable }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        4128,
        1584
      ],
      "id": "d745f0e3-ee2b-4d9f-a893-2e73b02951db",
      "name": "WhatsApp Retryable?",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=http://127.0.0.1:4000/api/events",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"execution_id\": \"{{ $('Set Global Vars').first().json.execution_id }}\",\n  \"client_id\": \"demo\",\n  \"contact_id\": \"{{ $('Extract Email Error').item.json.contact_id }}\",\n  \"channel\": \"EMAIL\",\n  \"provider\": \"SMTP\",\n  \"status\": \"FAILED\",\n  \"error_code\": \"{{ $('Extract Email Error').item.json.error_code || 'RETRYABLE_ERROR' }}\",\n  \"error_reason\": \"{{ $('Extract Email Error').item.json.error_reason || 'Retryable failure - queued for retry' }}\",\n  \"payload_snapshot\": {\n    \"retry_intent\": \"PENDING_RETRY\",\n    \"retry_count\": \"{{ $('Extract Email Error').item.json.retry_count || 0 }}\",\n    \"is_retryable\": true,\n    \"email\": \"{{ $('Extract Email Error').item.json.email }}\",\n    \"subject\": \"{{ `Happy Birthday, ${$('Extract Email Error').item.json.name} - from Puri Constructions` }}\",\n    \"contact_name\": \"{{ $('Extract Email Error').item.json.name }}\"\n  }\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        4352,
        1296
      ],
      "id": "6962613f-ca6d-4b3b-9265-b6dd0a685db1",
      "name": "Log Email Pending Retry",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=http://127.0.0.1:4000/api/events",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"execution_id\": \"{{ $('Set Global Vars').first().json.execution_id }}\",\n  \"client_id\": \"demo\",\n  \"contact_id\": \"{{ $('Extract Email Error').item.json.contact_id }}\",\n  \"channel\": \"EMAIL\",\n  \"provider\": \"SMTP\",\n  \"status\": \"FAILED\",\n  \"error_code\": \"{{ $('Extract Email Error').item.json.error_code }}\",\n  \"error_reason\": \"{{ $('Extract Email Error').item.json.error_reason }}\",\n  \"is_retryable\": false,\n  \"payload_snapshot\": {\n    \"email\": \"{{ $('Extract Email Error').item.json.email }}\",\n    \"subject\": \"Happy Birthday, {{ $('Extract Email Error').item.json.name }}\",\n    \"contact_name\": \"{{ $('Extract Email Error').item.json.name }}\",\n    \"rejected_emails\": \"{{ $('Extract Email Error').item.json.rejected_emails }}\"\n  }\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        4352,
        1488
      ],
      "id": "a11b0642-2730-4074-8e9d-a7707829d014",
      "name": "Log Email Failed",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=http://127.0.0.1:4000/api/events",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"execution_id\": \"{{ $('Set Global Vars').first().json.execution_id }}\",\n  \"client_id\": \"demo\",\n  \"contact_id\": \"{{ $('Extract WhatsApp Error').item.json.contact_id }}\",\n  \"channel\": \"WHATSAPP\",\n  \"provider\": \"OTHER\",\n  \"status\": \"FAILED\",\n  \"error_code\": \"{{ $('Extract WhatsApp Error').item.json.error_code }}\",\n  \"error_reason\": \"{{ $('Extract WhatsApp Error').item.json.error_reason }}\",\n  \"payload_snapshot\": {\n    \"retry_intent\": \"PENDING_RETRY\",\n    \"retry_count\": 0,\n    \"is_retryable\": true,\n    \"phone\": \"{{ $('Extract WhatsApp Error').item.json.full_phone }}\",\n    \"template\": \"birthday_dynamic_final\",\n    \"contact_name\": \"{{ $('Extract WhatsApp Error').item.json.name }}\"\n  }\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        4352,
        1488
      ],
      "id": "17e78a11-8c30-4774-9809-22fa96652c59",
      "name": "Log WhatsApp Pending Retry",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=http://127.0.0.1:4000/api/events",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"execution_id\": \"{{ $('Set Global Vars').first().json.execution_id }}\",\n  \"client_id\": \"demo\",\n  \"contact_id\": \"{{ $('Extract WhatsApp Error').item.json.contact_id }}\",\n  \"channel\": \"WHATSAPP\",\n  \"provider\": \"OTHER\",\n  \"status\": \"FAILED\",\n  \"error_code\

Credentials you'll need

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

Pro

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

About this workflow

Birthday Automation - Production (Fixed). Uses stopAndError, httpRequest, emailSend, bannerbear. Scheduled trigger; 86 nodes.

Source: https://github.com/teampuriofficial-svg/wish/blob/d58805afedfdd9731b45b5f404c71fcb8db18276/n8n-workflows/birthday_final.json — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

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

Web Scraping

Birthday Retry Runner. Uses httpRequest, stopAndError, emailSend. Scheduled trigger; 14 nodes.

HTTP Request, Stop And Error, Email Send
Web Scraping

What To Eat. Uses httpRequest, emailSend, stopAndError. Scheduled trigger; 10 nodes.

HTTP Request, Email Send, Stop And Error
Web Scraping

This workflow is an improvement of this workflow by Greg Brzezinka.

HTTP Request, Email Send, XML +1
Web Scraping

N8N-Self-Updater. Uses ssh, emailSend, httpRequest. Scheduled trigger; 27 nodes.

Ssh, Email Send, HTTP Request
Web Scraping

&gt; An automated n8n workflow originally built for DigitalOcean-based n8n deployments, but fully compatible with any VPS or cloud hosting (e.g., AWS, Google Cloud, Hetzner, Linode, etc.) where n8n ru

Ssh, Email Send, HTTP Request