{
  "name": "Text back missed calls forwarded to your mobile with Twilio and Google Sheets",
  "nodes": [
    {
      "parameters": {
        "content": "## Text back missed calls forwarded to your mobile with Twilio and Google Sheets\n\nThis workflow texts every caller you couldn't answer within seconds, with a daytime or after-hours message, and logs the call so you can ring back in order.\n\n### How it works\n1. Your Twilio number forwards calls to your mobile through a TwiML Bin with a short ring timeout.\n2. When the forward ends, Twilio posts `DialCallStatus` to the Webhook. The workflow replies with TwiML: a short spoken message if the call was missed, a hang-up if it was answered.\n3. Only missed calls (no-answer, busy, failed) that carry your Account SID and a real caller ID continue.\n4. A business-hours check picks the text, Twilio sends it, and a row is added to Google Sheets.\n\n### Setup\n1. Fill in **Settings**: business name, booking link, Twilio number, Account SID, opening hours. Set the workflow time zone.\n2. Connect Twilio and Google Sheets credentials, select your spreadsheet in **Log missed call**, and add a **Missed calls** tab with headers `Time | Caller | Status | Text sent | Called back?`\n3. Activate, copy the Webhook **Production URL** and create a TwiML Bin:\n```\n<Response><Dial timeout=\"12\" action=\"YOUR_PRODUCTION_URL\">+1YOURMOBILE</Dial></Response>\n```\n4. Set your number's **A call comes in** to that bin. Twilio adds about 5 s to `timeout`; the total must stay below your phone's voicemail delay, or voicemail \"answers\" and nothing fires.\n5. Call from another phone, let it ring out, and check the text and new row.\n\nUS business texting needs A2P 10DLC registration or toll-free verification first.\n\n### Customization\nEdit the wording in **Daytime text** and **After-hours text**, add an owner alert after the log, or swap Google Sheets for your CRM.",
        "height": 1040,
        "width": 600
      },
      "id": "8eeef611-57be-46f2-a5ad-01a4d53071f4",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -680,
        -420
      ]
    },
    {
      "parameters": {
        "content": "## 1. Receive the call result\nTwilio posts the forwarded call's outcome. Settings holds everything you edit, and Twilio gets its TwiML reply straight away.",
        "height": 400,
        "width": 660,
        "color": 7
      },
      "id": "5b1b2cf0-44e0-4ae0-b55c-4d94cdc20791",
      "name": "Section: receive",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -40,
        -200
      ]
    },
    {
      "parameters": {
        "content": "## 2. Real missed call?\nAnswered calls, hidden numbers and requests without your Account SID stop here.",
        "height": 640,
        "width": 280,
        "color": 7
      },
      "id": "a6975810-35ca-4659-9c73-f2b4b2c7e472",
      "name": "Section: filter",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        660,
        -200
      ]
    },
    {
      "parameters": {
        "content": "## 3. Pick and send the text\nDaytime or after-hours wording, sent by Twilio to the caller's number.",
        "height": 640,
        "width": 660,
        "color": 7
      },
      "id": "baca2e52-f643-46a0-adf6-6fcaf5b4769a",
      "name": "Section: text",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        980,
        -420
      ]
    },
    {
      "parameters": {
        "content": "## 4. Log for call-backs\nOne row per missed call in your Missed calls tab.",
        "height": 640,
        "width": 460,
        "color": 7
      },
      "id": "b65cc2b0-a75d-4bdc-8fdd-c85564e590f8",
      "name": "Section: log",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1680,
        -420
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "missed-call",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "5ae5a479-5516-4eaf-9f58-9f0b38a4e8ce",
      "name": "Twilio Dial Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        40
      ],
      "webhookId": "9d0411a9-efd9-4f70-ad84-df4ebff383f9"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "2b68d0f4-320a-48ae-b784-87a663465aac",
              "name": "businessName",
              "value": "[Your Business Name]",
              "type": "string"
            },
            {
              "id": "fd5c8d96-822e-4bdb-83dd-699d276ae816",
              "name": "bookingLink",
              "value": "https://example.com/book",
              "type": "string"
            },
            {
              "id": "2f4a3439-5947-4b1c-8e6e-ac9487c36069",
              "name": "twilioNumber",
              "value": "+15555550100",
              "type": "string"
            },
            {
              "id": "feb28e7e-da3c-42e7-93b1-56133375cc95",
              "name": "twilioAccountSid",
              "value": "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
              "type": "string"
            },
            {
              "id": "7d9c212f-8ef8-4bfe-9cd6-1e65446bff34",
              "name": "openHour",
              "value": 8,
              "type": "number"
            },
            {
              "id": "ae36ae72-ef42-4183-9c37-8bc34c6630c8",
              "name": "closeHour",
              "value": 18,
              "type": "number"
            },
            {
              "id": "881001a6-57a3-470e-a50d-8471377f31c0",
              "name": "voiceMessage",
              "value": "Sorry we missed your call. We'll text you in a moment.",
              "type": "string"
            }
          ]
        },
        "options": {},
        "includeOtherFields": true
      },
      "id": "f061e2a8-ab0d-4b0e-bcfb-ac61163a0c22",
      "name": "Settings",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        220,
        40
      ]
    },
    {
      "parameters": {
        "respondWith": "text",
        "responseBody": "={{ ['no-answer','busy','failed'].includes($json.body.DialCallStatus) ? '<Response><Say>' + $json.voiceMessage + '</Say><Hangup/></Response>' : '<Response><Hangup/></Response>' }}",
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "text/xml"
              }
            ]
          }
        }
      },
      "id": "1fd4d8f2-5768-46e4-a536-982f796c5c1e",
      "name": "Reply to Twilio (TwiML)",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        440,
        40
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "b41834ae-47c7-4239-8779-04e83e88a5bd",
              "leftValue": "={{ $json.body.AccountSid }}",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "rightValue": "={{ $json.twilioAccountSid }}"
            },
            {
              "id": "b292cfcd-4019-4c5d-a959-58164a336951",
              "leftValue": "={{ ['no-answer','busy','failed'].includes($json.body.DialCallStatus) }}",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "rightValue": ""
            },
            {
              "id": "dc37e5fe-d0db-47f7-b90e-35547ca53418",
              "leftValue": "={{ $json.body.From }}",
              "operator": {
                "type": "string",
                "operation": "startsWith"
              },
              "rightValue": "+"
            },
            {
              "id": "43419763-3cbd-49e5-b12d-4d6c51ee00b7",
              "leftValue": "={{ $json.body.From }}",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "rightValue": "+266696687"
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "7f76b26e-89c2-4771-b446-b1d2a462aad4",
      "name": "Missed call from Twilio?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        720,
        40
      ]
    },
    {
      "parameters": {},
      "id": "5b5a00a2-e3b0-4565-8042-950547d1dd31",
      "name": "Answered: do nothing",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        740,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "cc1960d0-d0db-467e-b719-718489008e34",
              "leftValue": "={{ $now.hour >= $json.openHour && $now.hour < $json.closeHour }}",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "rightValue": ""
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "4e110394-2d6e-479d-9fc7-4a2db810c578",
      "name": "During business hours?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1020,
        -120
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "91aafa08-a9c8-494c-8513-e2d423f888d0",
              "name": "smsText",
              "value": "=Hi, this is {{ $json.businessName }}. Sorry we missed your call! We're probably with a customer. What can we help with? Reply here or book online: {{ $json.bookingLink }}. Reply STOP to opt out.",
              "type": "string"
            }
          ]
        },
        "options": {},
        "includeOtherFields": true
      },
      "id": "64fd4fc8-7314-4430-8532-d45372afc173",
      "name": "Daytime text",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1240,
        -240
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "9944ee7f-76c4-4aad-8ecc-6e579dfc9469",
              "name": "smsText",
              "value": "=Thanks for calling {{ $json.businessName }}. We're closed right now but we'll call you back first thing. Want to skip the wait? Book here: {{ $json.bookingLink }}. Reply STOP to opt out.",
              "type": "string"
            }
          ]
        },
        "options": {},
        "includeOtherFields": true
      },
      "id": "ba3ca2d9-24c3-4f37-90fc-aca003ed180a",
      "name": "After-hours text",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1240,
        0
      ]
    },
    {
      "parameters": {
        "resource": "sms",
        "operation": "send",
        "from": "={{ $json.twilioNumber }}",
        "to": "={{ $json.body.From }}",
        "toWhatsapp": false,
        "message": "={{ $json.smsText }}",
        "options": {}
      },
      "id": "203e95ca-3f10-4e7b-b0d6-8b8114e2d6ff",
      "name": "Send text to caller",
      "type": "n8n-nodes-base.twilio",
      "typeVersion": 1,
      "position": [
        1460,
        -120
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "6b59cc6a-3267-4957-89b9-3549416621b4",
              "name": "Time",
              "value": "={{ $now.toFormat('yyyy-LL-dd HH:mm') }}",
              "type": "string"
            },
            {
              "id": "e129fb0b-3d1a-4466-b533-b46d9fee13bd",
              "name": "Caller",
              "value": "={{ $('Missed call from Twilio?').item.json.body.From }}",
              "type": "string"
            },
            {
              "id": "af1e3a69-dcc1-4139-83d9-93adce15846d",
              "name": "Status",
              "value": "={{ $('Missed call from Twilio?').item.json.body.DialCallStatus }}",
              "type": "string"
            },
            {
              "id": "3bc37269-9809-47c7-b774-ab16b5adef9d",
              "name": "Text sent",
              "value": "={{ $('Send text to caller').item.json.body }}",
              "type": "string"
            },
            {
              "id": "a39eb555-76cd-4982-b06a-aa59948fcafb",
              "name": "Called back?",
              "value": "No",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "be78906a-a2fb-4d5c-89a4-ed65e3f86982",
      "name": "Log row",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1740,
        -120
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list",
          "cachedResultName": "Select your spreadsheet"
        },
        "sheetName": {
          "__rl": true,
          "value": "Missed calls",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "08cc3966-1db7-4564-b491-24f8434d3b3d",
      "name": "Log missed call",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1960,
        -120
      ]
    }
  ],
  "connections": {
    "Twilio Dial Webhook": {
      "main": [
        [
          {
            "node": "Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Settings": {
      "main": [
        [
          {
            "node": "Reply to Twilio (TwiML)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reply to Twilio (TwiML)": {
      "main": [
        [
          {
            "node": "Missed call from Twilio?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Missed call from Twilio?": {
      "main": [
        [
          {
            "node": "During business hours?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Answered: do nothing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "During business hours?": {
      "main": [
        [
          {
            "node": "Daytime text",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "After-hours text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daytime text": {
      "main": [
        [
          {
            "node": "Send text to caller",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "After-hours text": {
      "main": [
        [
          {
            "node": "Send text to caller",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send text to caller": {
      "main": [
        [
          {
            "node": "Log row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log row": {
      "main": [
        [
          {
            "node": "Log missed call",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "active": false,
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}