{
 "id": "r2quotefollowupn",
 "name": "AutomateRecipes \u2013 quote-follow-up-nudges",
 "nodes": [
  {
   "parameters": {
    "rule": {
     "interval": [
      {
       "field": "days",
       "triggerAtHour": 9
      }
     ]
    }
   },
   "name": "Schedule Trigger",
   "type": "n8n-nodes-base.scheduleTrigger",
   "typeVersion": 1.2,
   "position": [
    0,
    0
   ],
   "id": "e15ed816-7599-46d5-a489-62972ab24dc2"
  },
  {
   "parameters": {
    "jsCode": "// EDIT THESE SETTINGS\nreturn [{json:{\n \"ntfyTopic\": \"CHANGE-ME-to-a-long-random-topic\",\n \"cfAccountId\": \"YOUR_CLOUDFLARE_ACCOUNT_ID\",\n \"kvNamespaceId\": \"YOUR_KV_NAMESPACE_ID\",\n \"quotesKey\": \"quotes.json\",\n \"nudgeDays\": [\n  2,\n  5,\n  10\n ],\n \"yourName\": \"Your Business\"\n}}];"
   },
   "name": "Config",
   "type": "n8n-nodes-base.code",
   "typeVersion": 2,
   "position": [
    240,
    0
   ],
   "id": "b9fc5d44-eb3e-4ca0-838a-6f4cbb552bfb"
  },
  {
   "parameters": {
    "url": "=https://api.cloudflare.com/client/v4/accounts/{{ $('Config').first().json.cfAccountId }}/storage/kv/namespaces/{{ $('Config').first().json.kvNamespaceId }}/values/{{ $('Config').first().json.quotesKey }}",
    "authentication": "genericCredentialType",
    "genericAuthType": "httpHeaderAuth",
    "options": {
     "response": {
      "response": {
       "responseFormat": "text",
       "outputPropertyName": "raw"
      }
     }
    }
   },
   "name": "Read quotes from KV",
   "type": "n8n-nodes-base.httpRequest",
   "typeVersion": 4.2,
   "position": [
    480,
    0
   ],
   "id": "efae525b-a9f2-4a94-a970-1d03cef6acdb",
   "credentials": {
    "httpHeaderAuth": {
     "id": "REPLACE_WITH_YOUR_CREDENTIAL",
     "name": "Cloudflare API token"
    }
   }
  },
  {
   "parameters": {
    "jsCode": "const c=$('Config').first().json;let q;try{q=JSON.parse($input.first().json.raw)}catch(e){throw new Error('quotes key missing or not JSON')}\nif(!Array.isArray(q))throw new Error('quotes must be a JSON array');\nconst today=new Date();today.setHours(0,0,0,0);const iso=new Date(Date.now()-new Date().getTimezoneOffset()*6e4).toISOString().slice(0,10);const out=[],stopped=[];\nfor(const x of q){x.nudges=x.nudges||0;\n if(['accepted','replied','declined','won','lost'].includes(String(x.status).toLowerCase())){stopped.push(`${x.id} (${x.status})`);continue}\n const days=Math.floor((today-new Date(x.sentDate+'T00:00:00'))/864e5);\n if(x.nudges>=c.nudgeDays.length)continue;\n if(days>=c.nudgeDays[x.nudges]&&x.lastNudgeDate!==iso){\n  x.nudges++;x.lastNudgeDate=iso;\n  const tone=['Quick check-in','Following up','Last follow-up'][x.nudges-1];\n  out.push(`\u2022 ${x.id} ${x.customer} <${x.email}> \u2013 day ${days}, nudge ${x.nudges}/${c.nudgeDays.length}\\n  Draft: \"${tone} on quote ${x.id} (${x.amount}). Any questions? \u2013 ${c.yourName}\"`);}}\nreturn [{json:{quotes:q,actions:out,stopped,body:JSON.stringify(q,null,1)}}];"
   },
   "name": "Decide nudges",
   "type": "n8n-nodes-base.code",
   "typeVersion": 2,
   "position": [
    720,
    0
   ],
   "id": "4f617c0e-b794-4243-bcc9-aa2c9644a8e4"
  },
  {
   "parameters": {
    "method": "PUT",
    "url": "=https://api.cloudflare.com/client/v4/accounts/{{ $('Config').first().json.cfAccountId }}/storage/kv/namespaces/{{ $('Config').first().json.kvNamespaceId }}/values/{{ $('Config').first().json.quotesKey }}",
    "authentication": "genericCredentialType",
    "genericAuthType": "httpHeaderAuth",
    "sendBody": true,
    "contentType": "raw",
    "rawContentType": "text/plain",
    "body": "={{ $json.body }}",
    "options": {}
   },
   "name": "Save updated quotes",
   "type": "n8n-nodes-base.httpRequest",
   "typeVersion": 4.2,
   "position": [
    960,
    0
   ],
   "id": "1de16e1b-150b-4227-ae14-f6a45cb26189",
   "credentials": {
    "httpHeaderAuth": {
     "id": "REPLACE_WITH_YOUR_CREDENTIAL",
     "name": "Cloudflare API token"
    }
   }
  },
  {
   "parameters": {
    "jsCode": "if($input.first().json.success!==true)throw new Error('KV write failed');const d=$('Decide nudges').first().json;if(!d.actions.length)return [];\nreturn [{json:{title:`Send ${d.actions.length} quote follow-up(s) today`,tags:'envelope',message:d.actions.join('\\n')+(d.stopped.length?`\\nStopped (reply/accepted): ${d.stopped.join(', ')}`:'')}}];"
   },
   "name": "Build alert",
   "type": "n8n-nodes-base.code",
   "typeVersion": 2,
   "position": [
    1200,
    0
   ],
   "id": "53f99c19-d01e-4ec0-b05f-ffd06926972c"
  },
  {
   "parameters": {
    "method": "POST",
    "url": "=https://ntfy.sh/{{ $('Config').first().json.ntfyTopic }}",
    "sendHeaders": true,
    "headerParameters": {
     "parameters": [
      {
       "name": "Title",
       "value": "={{ $json.title }}"
      },
      {
       "name": "Tags",
       "value": "={{ $json.tags || 'bell' }}"
      }
     ]
    },
    "sendBody": true,
    "contentType": "raw",
    "rawContentType": "text/plain",
    "body": "={{ $json.message }}",
    "options": {}
   },
   "name": "Send ntfy alert",
   "type": "n8n-nodes-base.httpRequest",
   "typeVersion": 4.2,
   "position": [
    1440,
    0
   ],
   "id": "7490e36b-1bba-4f1e-8970-8480c4701467"
  },
  {
   "parameters": {},
   "name": "Run manually (test)",
   "type": "n8n-nodes-base.manualTrigger",
   "typeVersion": 1,
   "position": [
    0,
    200
   ],
   "id": "fc3c4a5f-a6a2-4e6b-be8c-d58fac060272"
  }
 ],
 "connections": {
  "Schedule Trigger": {
   "main": [
    [
     {
      "node": "Config",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Config": {
   "main": [
    [
     {
      "node": "Read quotes from KV",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Read quotes from KV": {
   "main": [
    [
     {
      "node": "Decide nudges",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Decide nudges": {
   "main": [
    [
     {
      "node": "Save updated quotes",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Save updated quotes": {
   "main": [
    [
     {
      "node": "Build alert",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Build alert": {
   "main": [
    [
     {
      "node": "Send ntfy alert",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Run manually (test)": {
   "main": [
    [
     {
      "node": "Config",
      "type": "main",
      "index": 0
     }
    ]
   ]
  }
 },
 "settings": {
  "executionOrder": "v1"
 },
 "active": false,
 "pinData": {}
}