{
  "name": "Missed-Call Text-Back (Helm Systems)",
  "nodes": [
    {
      "parameters": {
        "content": "## Missed-Call Text-Back\n1. Open **Settings**. Fill in your hours, your Twilio number and a test phone (your own mobile).\n2. Connect Twilio, Gmail and the AI model credentials.\n3. Copy the **Production URL** from the Webhook node. In Twilio, point your number's forwarding TwiML at it (see the page for the exact snippet), or have any phone system POST {\"from\",\"status\"} to it.\n4. While **testMode** is true, every text goes to your test phone, not the caller. Call yourself, let it ring out, check the text.\n5. Texting customers needs their permission and an opt-out. Confirm yours before turning testMode off.\nMade by Helm Systems: helmsystems.biz/field-kit",
        "width": 420,
        "height": 300,
        "color": 4
      },
      "id": "59a0620a-0981-4a0e-ae64-3dd5c7482c78",
      "name": "Note 1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -323
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "missed-call",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "d8491542-c469-4fbe-a4e3-117699cc29e8",
      "name": "Missed call (webhook)",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "webhookId": "2e9efce6-e4c0-4fae-aa51-3789d94b136d"
    },
    {
      "parameters": {
        "respondWith": "text",
        "responseBody": "<Response></Response>",
        "options": {
          "responseCode": 200,
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "text/xml"
              }
            ]
          }
        }
      },
      "id": "62ecbbb6-391d-40f9-adfb-ab43a1cd9c1f",
      "name": "Reply to Twilio",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        260,
        247
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "a73446ee-37a6-4df8-abf1-5bd348e7c2f6",
              "name": "business",
              "value": "Your Business Name",
              "type": "string"
            },
            {
              "id": "a3beec93-487c-4409-a8e3-d9f346df35c2",
              "name": "owner",
              "value": "Your Name",
              "type": "string"
            },
            {
              "id": "c338a8d7-8c58-468a-ae69-5e2dc3e3ce50",
              "name": "ownerEmail",
              "value": "you@yourbusiness.com",
              "type": "string"
            },
            {
              "id": "f59c70c0-63f1-4ccb-a06b-0b57ec282c5b",
              "name": "autoSend",
              "value": false,
              "type": "boolean"
            },
            {
              "id": "acba03e7-3133-4c2c-a5ff-2c7b69c69552",
              "name": "hours",
              "value": "Monday to Friday, 8am to 5pm",
              "type": "string"
            },
            {
              "id": "d4336a5a-b013-4092-ae8d-7696378a1fd6",
              "name": "openDays",
              "value": "1,2,3,4,5",
              "type": "string"
            },
            {
              "id": "ad48684c-1397-416b-a0be-9a528f198dd1",
              "name": "openHour",
              "value": 8,
              "type": "number"
            },
            {
              "id": "aa4e9761-b003-49eb-a544-81c76b7a84c3",
              "name": "closeHour",
              "value": 17,
              "type": "number"
            },
            {
              "id": "a3fc5245-3219-4a16-a642-aa31ef225253",
              "name": "bookingLink",
              "value": "",
              "type": "string"
            },
            {
              "id": "050a6263-9240-4233-aeff-44931ee83842",
              "name": "twilioNumber",
              "value": "+15555550100",
              "type": "string"
            },
            {
              "id": "5f6318e5-1798-41ed-af24-4f9680b677ba",
              "name": "testMode",
              "value": true,
              "type": "boolean"
            },
            {
              "id": "b17630fc-976b-4233-a11c-4494866ed33b",
              "name": "testPhone",
              "value": "+15555550101",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "9eca0194-0026-4861-a0c9-c94d2b389ba8",
      "name": "Settings",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        260,
        0
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const b = $('Missed call (webhook)').first().json.body ?? $('Missed call (webhook)').first().json;\nconst s = $('Settings').first().json;\nconst status = String(b.DialCallStatus || b.CallStatus || b.status || '').toLowerCase();\nconst missed = ['no-answer', 'busy', 'failed', 'missed', 'canceled'].includes(status);\nconst caller = String(b.From || b.from || b.caller || '').trim();\nif (!missed || !caller) return [];\n// Never text the same number twice in 24 hours.\nconst sd = $getWorkflowStaticData('global');\nsd.recent = sd.recent || {};\nconst nowMs = Date.now();\nfor (const k of Object.keys(sd.recent)) if (nowMs - sd.recent[k] > 86400000) delete sd.recent[k];\nif (sd.recent[caller]) return [];\nsd.recent[caller] = nowMs;\nconst days = String(s.openDays).split(',').map(Number);\nconst open = days.includes($now.weekday) && $now.hour >= Number(s.openHour) && $now.hour < Number(s.closeHour);\nreturn [{ json: {\n  caller,\n  to: s.testMode ? s.testPhone : caller,\n  business: s.business, hours: s.hours, bookingLink: s.bookingLink,\n  now: $now.toFormat('cccc h:mm a'),\n  openStatus: open ? 'open right now' : 'closed right now',\n} }];"
      },
      "id": "ea4afda1-03a7-4b23-a35e-b78605574d96",
      "name": "Was it missed?",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        520,
        0
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Business hours: {{ $json.hours }}\nRight now it is {{ $json.now }} and the business is {{ $json.openStatus }}.\nBooking link (may be empty): {{ $json.bookingLink }}\nWrite the text.",
        "hasOutputParser": false,
        "messages": {
          "messageValues": [
            {
              "message": "=You write one text message from {{ $json.business }} to someone who just called and was not answered.\n\nRules:\n- Under 240 characters. Friendly and human. No emojis.\n- Say sorry we missed the call and ask how we can help.\n- Say when someone will get back to them, using only the business hours given. Never promise a specific time.\n- Only include a link if one is provided.\nReturn only the text message."
            }
          ]
        }
      },
      "id": "a80362ce-265f-43e2-aeb6-41061e9bc305",
      "name": "Write the text",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.7,
      "position": [
        780,
        0
      ]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "gpt-5-mini",
          "mode": "list",
          "cachedResultName": "gpt-5-mini"
        },
        "options": {}
      },
      "id": "038b84ae-d268-42d7-a7b5-454d06a70fb7",
      "name": "Write the text model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.2,
      "position": [
        780,
        266
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const base = $('Was it missed?').item.json;\nreturn { json: { ...base, sms: String($json.text || '').trim().slice(0, 320) } };"
      },
      "id": "586e9c05-644c-43b7-aa2d-6370f906def6",
      "name": "Prepare the text",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1040,
        0
      ]
    },
    {
      "parameters": {
        "resource": "sms",
        "operation": "send",
        "from": "={{ $('Settings').first().json.twilioNumber }}",
        "to": "={{ $json.to }}",
        "message": "={{ $json.sms }}",
        "options": {}
      },
      "id": "379db28e-21d0-4c2e-a3ef-3def2bdbc587",
      "name": "Send the text",
      "type": "n8n-nodes-base.twilio",
      "typeVersion": 1,
      "position": [
        1300,
        -76
      ]
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "send",
        "sendTo": "={{ $('Settings').first().json.ownerEmail }}",
        "subject": "={{ 'Missed call from ' + $('Prepare the text').item.json.caller }}",
        "emailType": "text",
        "message": "={{ 'We texted them' + ($('Settings').first().json.testMode ? ' (TEST MODE: sent to your test phone)' : '') + ':\\n\\n' + $('Prepare the text').item.json.sms }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "f2767d6b-8367-47d0-a766-4e8acdd88691",
      "name": "Tell the owner",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1560,
        -76
      ]
    }
  ],
  "connections": {
    "Write the text model": {
      "ai_languageModel": [
        [
          {
            "node": "Write the text",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Missed call (webhook)": {
      "main": [
        [
          {
            "node": "Reply to Twilio",
            "type": "main",
            "index": 0
          },
          {
            "node": "Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Settings": {
      "main": [
        [
          {
            "node": "Was it missed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Was it missed?": {
      "main": [
        [
          {
            "node": "Write the text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write the text": {
      "main": [
        [
          {
            "node": "Prepare the text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare the text": {
      "main": [
        [
          {
            "node": "Send the text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send the text": {
      "main": [
        [
          {
            "node": "Tell the owner",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "tags": []
}
