{
  "id": "B6kxRpvzyTbaNwJY",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "AI Re-engagement Email for Inactive Users",
  "tags": [],
  "nodes": [
    {
      "id": "demo-video-note-email",
      "name": "\ud83c\udfa5 Demo Video",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1408,
        -260
      ],
      "parameters": {
        "width": 480,
        "height": 380,
        "content": "## \ud83c\udfa5 Watch the demo\n\n@[youtube](avVhnsZIcv8)\n\nBuilt by **Aumadi** \u2014 pj@aumadi.com"
      },
      "typeVersion": 1
    },
    {
      "id": "1fde30f7-cfa0-40e3-accc-e68e70915a59",
      "name": "\ud83d\udcd8 Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1408,
        160
      ],
      "parameters": {
        "width": 480,
        "height": 320,
        "content": "## \u2709\ufe0f AI Re-engagement Email for Inactive Users\n\n### Who's it for\nBusinesses that want to win back inactive customers automatically \u2014 with on-brand, personal emails and no manual writing.\n\n### How it works\n1. Runs daily on a schedule.\n2. Finds users inactive for X days (Supabase), capped per run.\n3. Reads your 3 brand **directions** from `email_guidance`.\n4. **Claude** writes a unique email per user following those directions.\n5. Sends it (SMTP) and marks the user done \u2014 *failed sends are NOT marked, so they retry next run*.\n\n### Requirements\n- Supabase (`users` + `email_guidance`)\n- Anthropic API key (Claude)\n- SMTP (Gmail, Resend, SendGrid, \u2026)"
      },
      "typeVersion": 1
    },
    {
      "id": "c542c89f-1791-4f08-b10c-29ba21bc6208",
      "name": "\ud83d\udd27 Setup",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1408,
        560
      ],
      "parameters": {
        "width": 480,
        "height": 320,
        "content": "## \ud83d\udd27 Setup\n1. Run `new-workflows-setup.sql` (creates `email_guidance`, adds user columns).\n2. Edit the `email_guidance` row \u2192 set your 3 **directions** + subject hint.\n3. Credentials:\n   - **Claude (Anthropic)** \u2192 Header Auth, name `x-api-key` (your Anthropic key).\n   - **Supabase** \u2192 host + service_role key.\n   - **SMTP** \u2192 your email host/user/pass (Gmail, Resend, SendGrid, etc.).\n4. In **\u2699\ufe0f Config**: set `inactiveDays`, `mailType`, `fromEmail`.\n5. Make sure your `users` table has: `email`, `name`, `last_active_at`, `reengagement_sent`.\n6. Activate \u2014 it runs daily."
      },
      "typeVersion": 1
    },
    {
      "id": "3d4a19a9-5d5b-4da2-80b0-25bf9b46ee26",
      "name": "Every day 9am",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -880,
        320
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "efa077b3-9d56-4aa0-9825-27e82227fb0e",
      "name": "\u2699\ufe0f Config",
      "type": "n8n-nodes-base.set",
      "position": [
        -656,
        320
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "c_days",
              "name": "inactiveDays",
              "type": "number",
              "value": 30
            },
            {
              "id": "c_type",
              "name": "mailType",
              "type": "string",
              "value": "reengagement"
            },
            {
              "id": "c_from",
              "name": "fromEmail",
              "type": "string",
              "value": "hello@yourbusiness.com"
            },
            {
              "id": "c_model",
              "name": "model",
              "type": "string",
              "value": "claude-haiku-4-5-20251001"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "e986ac20-a1b1-4f23-9d48-ff1879f76e75",
      "name": "Get Email Guidance (3 directions)",
      "type": "n8n-nodes-base.supabase",
      "position": [
        -448,
        320
      ],
      "parameters": {
        "limit": 1,
        "filters": {
          "conditions": [
            {
              "keyName": "mail_type",
              "keyValue": "={{ $json.mailType }}",
              "condition": "eq"
            }
          ]
        },
        "tableId": "email_guidance",
        "operation": "getAll"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "48096dfd-db0f-473c-957d-fcc30b5b7134",
      "name": "Get Inactive Users",
      "type": "n8n-nodes-base.supabase",
      "position": [
        -224,
        320
      ],
      "parameters": {
        "limit": 100,
        "filters": {
          "conditions": [
            {
              "keyName": "last_active_at",
              "keyValue": "={{ $now.minus({ days: $('\u2699\ufe0f Config').first().json.inactiveDays }).toISO() }}",
              "condition": "lt"
            },
            {
              "keyName": "reengagement_sent",
              "keyValue": "false",
              "condition": "is"
            }
          ]
        },
        "tableId": "users",
        "matchType": "allFilters",
        "operation": "getAll",
        "returnAll": false
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "020bacce-1e77-4592-85ff-58f8b3366626",
      "name": "\ud83e\udde0 Claude: Write Email",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        0,
        320
      ],
      "parameters": {
        "url": "https://api.anthropic.com/v1/messages",
        "method": "POST",
        "options": {
          "timeout": 30000
        },
        "jsonBody": "={{ JSON.stringify({ model: $('\u2699\ufe0f Config').first().json.model, max_tokens: 600, system: 'You write short, warm re-engagement emails for inactive customers. Follow the business directions exactly. Return ONLY valid JSON with keys \"subject\" and \"body\". body = plain-text email copy, no markdown.', messages: [ { role: 'user', content: 'Customer name: ' + ($json.name || 'there') + '\\n\\nFollow these 3 directions exactly:\\n1) ' + ($('Get Email Guidance (3 directions)').first().json.direction_1 || '') + '\\n2) ' + ($('Get Email Guidance (3 directions)').first().json.direction_2 || '') + '\\n3) ' + ($('Get Email Guidance (3 directions)').first().json.direction_3 || '') + '\\nSubject hint: ' + ($('Get Email Guidance (3 directions)').first().json.subject_hint || '') } ] }) }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "content-type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "183bdd02-300d-4079-95db-10a18faaece3",
      "name": "Extract Email",
      "type": "n8n-nodes-base.code",
      "position": [
        224,
        320
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst users = $('Get Inactive Users').all();\nconst fromEmail = ($('\u2699\ufe0f Config').first().json.fromEmail) || 'hello@yourbusiness.com';\nconst out = [];\nfor (let i = 0; i < items.length; i++) {\n  const u = (users[i] && users[i].json) || {};\n  if (!u.email) { continue; }\n  const r = items[i].json;\n  let txt = '';\n  try {\n    if (Array.isArray(r.content)) { txt = r.content.map(c => c.text || '').join(''); }\n    else if (r.candidates) { txt = (r.candidates[0].content.parts || []).map(p => p.text || '').join(''); }\n  } catch (e) {}\n  txt = String(txt).trim().replace(/^```(?:json)?/i, '').replace(/```$/, '').trim();\n  let subject = 'We miss you!', body = txt;\n  try { const o = JSON.parse(txt); subject = o.subject || subject; body = o.body || body; } catch (e) {}\n  const paras = String(body).split(/\\n{2,}/).map(p => p.replace(/\\n/g, '<br>').trim()).filter(Boolean);\n  const htmlBody = paras.map(p => `<p style='margin:0 0 16px;line-height:1.6;color:#333;font-size:15px;'>${p}</p>`).join('');\n  const html = `<!DOCTYPE html><html><body style='margin:0;padding:0;background:#f4f4f5;'><table role='presentation' width='100%' cellpadding='0' cellspacing='0' style='background:#f4f4f5;padding:24px 0;'><tr><td align='center'><table role='presentation' width='600' cellpadding='0' cellspacing='0' style='max-width:600px;background:#fff;border-radius:12px;overflow:hidden;font-family:Arial,Helvetica,sans-serif;'><tr><td style='background:#111827;padding:24px 32px;color:#fff;font-size:20px;font-weight:bold;'>Aumadi</td></tr><tr><td style='padding:32px;'>${htmlBody}</td></tr><tr><td style='padding:20px 32px;background:#f9fafb;color:#9ca3af;font-size:12px;line-height:1.5;'>You are receiving this because you have an account with us.<br><a href='mailto:${fromEmail}?subject=Unsubscribe' style='color:#9ca3af;text-decoration:underline;'>Unsubscribe</a> &middot; &copy; Aumadi</td></tr></table></td></tr></table></body></html>`;\n    out.push({ json: { to: u.email, name: u.name, userId: u.id, subject, body, html }, pairedItem: { item: i } });\n\n}\nreturn out;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "7c05f083-f87f-4290-82fe-363d1f3f70fa",
      "name": "\u2709\ufe0f Send Email",
      "type": "n8n-nodes-base.emailSend",
      "onError": "continueErrorOutput",
      "position": [
        448,
        320
      ],
      "parameters": {
        "text": "={{ $json.body }}",
        "options": {},
        "subject": "={{ $json.subject }}",
        "toEmail": "={{ $json.to }}",
        "fromEmail": "={{ $('\u2699\ufe0f Config').first().json.fromEmail }}",
        "emailFormat": "both"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "c11fc425-8d56-4a68-ac27-ec99b36acceb",
      "name": "Mark Re-engaged",
      "type": "n8n-nodes-base.supabase",
      "position": [
        672,
        320
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "id",
              "keyValue": "={{ $('Extract Email').item.json.userId }}",
              "condition": "eq"
            }
          ]
        },
        "tableId": "users",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "reengagement_sent",
              "fieldValue": "true"
            },
            {
              "fieldId": "reengaged_at",
              "fieldValue": "={{ $now.toISO() }}"
            }
          ]
        },
        "operation": "update"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "1e06f80f-5cc4-427e-93bc-fa5291b8ad36",
      "name": "\ud83d\udcd2 The twist",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1408,
        920
      ],
      "parameters": {
        "width": 480,
        "height": 220,
        "content": "## \u270d\ufe0f Client-guided AI (the selling point)\nThe 3 **directions** come from your `email_guidance` table \u2014 the client controls tone, offer, and length **without editing the workflow**. The AI writes a unique, on-brand email per user following those directions, returns clean JSON (subject + body), and we send it. Change the directions in Supabase \u2192 every future email changes."
      },
      "typeVersion": 1
    },
    {
      "id": "sec-find",
      "name": "Section \u2014 Find inactive users",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -910,
        200
      ],
      "parameters": {
        "color": 7,
        "width": 800,
        "height": 240,
        "content": "## \u2460 Find inactive users\n\nRuns daily, reads your 3 brand **directions** from `email_guidance`, then pulls every user inactive for `inactiveDays` who hasn't been re-engaged yet (capped per run)."
      },
      "typeVersion": 1
    },
    {
      "id": "sec-ai",
      "name": "Section \u2014 AI writes the email",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -90,
        200
      ],
      "parameters": {
        "color": 7,
        "width": 430,
        "height": 240,
        "content": "## \u2461 AI writes the email\n\n**Claude** writes a unique, on-brand email per user following the 3 directions. **Extract Email** parses the reply, builds the HTML, and skips anyone with no email address."
      },
      "typeVersion": 1
    },
    {
      "id": "sec-send",
      "name": "Section \u2014 Send & mark done",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        410,
        200
      ],
      "parameters": {
        "color": 7,
        "width": 440,
        "height": 240,
        "content": "## \u2462 Send & mark done\n\n**Send Email** (SMTP) delivers it. Only on a **successful send** do we **Mark Re-engaged** \u2014 failed sends are left unmarked so the next run retries them."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "fd301cd2-5a95-483b-868c-ac281793fd5f",
  "connections": {
    "Every day 9am": {
      "main": [
        [
          {
            "node": "\u2699\ufe0f Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Email": {
      "main": [
        [
          {
            "node": "\u2709\ufe0f Send Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2699\ufe0f Config": {
      "main": [
        [
          {
            "node": "Get Email Guidance (3 directions)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2709\ufe0f Send Email": {
      "main": [
        [
          {
            "node": "Mark Re-engaged",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Inactive Users": {
      "main": [
        [
          {
            "node": "\ud83e\udde0 Claude: Write Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\udde0 Claude: Write Email": {
      "main": [
        [
          {
            "node": "Extract Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Email Guidance (3 directions)": {
      "main": [
        [
          {
            "node": "Get Inactive Users",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}