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 →
{
"name": "[PROD] sms / sarah-tool",
"description": null,
"active": true,
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "sarah-send-sms-v3",
"responseMode": "responseNode",
"options": {},
"authentication": "headerAuth"
},
"id": "webhook-trigger",
"name": "ElevenLabs Tool Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
208,
952
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "request_id",
"name": "request_id",
"value": "={{ $now.toMillis() + '-' + Math.random().toString(36).substring(7) }}",
"type": "string"
},
{
"id": "phone_raw",
"name": "phone_raw",
"value": "={{ ($json.body?.phone_number || $json.phone_number || '').toString() }}",
"type": "string"
},
{
"id": "phone_clean",
"name": "phone_number",
"value": "={{ ($json.body?.phone_number || $json.phone_number || '').toString().replace(/[^0-9+]/g, '') }}",
"type": "string"
},
{
"id": "first_name",
"name": "first_name",
"value": "={{ $json.body?.caller_name || $json.body?.first_name || $json.caller_name || $json.first_name || 'there' }}",
"type": "string"
},
{
"id": "industry",
"name": "industry",
"value": "={{ $json.body?.industry || $json.industry || 'service business' }}",
"type": "string"
},
{
"id": "company_name",
"name": "company_name",
"value": "={{ $json.body?.company_name || $json.company_name || '' }}",
"type": "string"
},
{
"id": "email",
"name": "email",
"value": "={{ $json.body?.email || $json.email || '' }}",
"type": "string"
},
{
"id": "template",
"name": "template",
"value": "={{ $json.body?.template || $json.template || 'demo' }}",
"type": "string"
},
{
"id": "timestamp",
"name": "timestamp",
"value": "={{ $now.toISO() }}",
"type": "string"
}
]
},
"options": {}
},
"id": "extract-params",
"name": "Extract & Normalize Parameters",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
656,
832
]
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "us-ca-format",
"leftValue": "={{ $json.phone_number }}",
"rightValue": "^\\+1[2-9][0-9]{9}$",
"operator": {
"type": "string",
"operation": "regex"
}
}
]
}
},
{
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "intl-format",
"leftValue": "={{ $json.phone_number }}",
"rightValue": "^\\+[1-9][0-9]{6,14}$",
"operator": {
"type": "string",
"operation": "regex"
}
}
]
}
},
{
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "empty-phone",
"leftValue": "={{ $json.phone_number }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
}
}
]
},
"options": {
"fallbackOutput": "extra"
}
},
"id": "validate-phone",
"name": "Validate Phone Format",
"type": "n8n-nodes-base.switch",
"typeVersion": 3.2,
"position": [
880,
800
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { success: false, error: 'MISSING_PHONE_NUMBER', message: 'No phone number provided', request_id: $json.request_id, hint: 'Provide phone_number in E.164 format: +1XXXXXXXXXX' } }}",
"options": {
"responseCode": 400
}
},
"id": "respond-missing-phone",
"name": "Error: Missing Phone",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
1104,
736
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { success: false, error: 'INVALID_PHONE_FORMAT', message: 'Phone number format invalid', provided: $json.phone_raw, cleaned: $json.phone_number, request_id: $json.request_id, expected: '+1XXXXXXXXXX or international E.164' } }}",
"options": {
"responseCode": 400
}
},
"id": "respond-invalid-phone",
"name": "Error: Invalid Phone",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
1104,
928
]
},
{
"parameters": {},
"id": "merge-valid-phones",
"name": "Merge Valid Paths",
"type": "n8n-nodes-base.merge",
"typeVersion": 3,
"position": [
1104,
544
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "phone_number",
"name": "phone_number",
"value": "={{ $json.phone_number }}",
"type": "string"
},
{
"id": "first_name",
"name": "first_name",
"value": "={{ $json.first_name }}",
"type": "string"
},
{
"id": "company_name",
"name": "company_name",
"value": "={{ $json.company_name }}",
"type": "string"
},
{
"id": "request_id",
"name": "request_id",
"value": "={{ $json.request_id }}",
"type": "string"
},
{
"id": "sms_message",
"name": "sms_message",
"value": "={{ (() => { const templates = { 'demo': `Hi ${$json.first_name}! Try Wranngle's AI demo - call (888) 266-2193 and pretend you're a customer with an emergency. Then call back with a routine request and see the difference! Questions? wranngle.com`, 'recap': `Hi ${$json.first_name}! Thanks for chatting about ${$json.company_name || 'your business'}. Demo: (888) 266-2193 - try calling as if you're a customer. More info: wranngle.com`, 'followup': `Hi ${$json.first_name}! Following up on Wranngle for ${$json.industry}. Demo: (888) 266-2193. Questions? Reply here or call us.` }; return templates[$json.template] || templates['demo']; })() }}",
"type": "string"
},
{
"id": "attempt",
"name": "attempt_number",
"value": "=1",
"type": "number"
}
]
},
"options": {
"dotNotation": true
}
},
"id": "compose-message",
"name": "Compose SMS Message",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1328,
544
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.twilio.com/2010-04-01/Accounts/<REDACTED:twilio-account-sid>/Messages.json",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendBody": true,
"contentType": "form-urlencoded",
"bodyParameters": {
"parameters": [
{
"name": "To",
"value": "={{ $json.phone_number }}"
},
{
"name": "From",
"value": "<REDACTED:phone>"
},
{
"name": "Body",
"value": "={{ $json.sms_message }}"
}
]
},
"options": {}
},
"id": "send-sms-twilio",
"name": "Send SMS via Twilio",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1552,
544
],
"onError": "continueErrorOutput"
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "has-sid",
"leftValue": "={{ $json.sid }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty"
}
}
]
}
},
{
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "is-retryable",
"leftValue": "={{ [30003, 30005, 30008, 30009, 30010].includes($json.code || $json.error_code) || ($json.message || '').toLowerCase().includes('temporarily') }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
]
}
}
]
},
"options": {
"fallbackOutput": "extra"
}
},
"id": "check-send-result",
"name": "Check Send Result",
"type": "n8n-nodes-base.switch",
"typeVersion": 3.2,
"position": [
1776,
280
]
},
{
"parameters": {
"conditions": {
"options": {
"leftValue": ""
},
"combinator": "and",
"conditions": [
{
"id": "under-max",
"leftValue": "={{ $('Compose SMS Message').first().json.attempt_number || 1 }}",
"rightValue": 3,
"operator": {
"type": "number",
"operation": "lt"
}
}
]
},
"options": {}
},
"id": "retry-logic",
"name": "Retry: Check Attempts",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
2000,
496
]
},
{
"parameters": {
"amount": "={{ Math.pow(2, $('Compose SMS Message').first().json.attempt_number || 1) }}"
},
"id": "wait-before-retry",
"name": "Wait Before Retry",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [
2224,
400
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "attempt",
"name": "attempt_number",
"value": "={{ ($('Compose SMS Message').first().json.attempt_number || 1) + 1 }}",
"type": "number"
}
]
},
"options": {}
},
"id": "increment-attempt",
"name": "Increment Attempt Counter",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
2448,
664
]
},
{
"parameters": {
"url": "=https://api.twilio.com/2010-04-01/Accounts/<REDACTED:twilio-account-sid>/Messages/{{ $json.sid }}.json",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"options": {
"timeout": 10000
}
},
"id": "verify-delivery",
"name": "Verify: Fetch Status",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2000,
-32
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "or",
"conditions": [
{
"id": "is-delivered",
"leftValue": "={{ $json.status }}",
"rightValue": "delivered",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"id": "is-sent",
"leftValue": "={{ $json.status }}",
"rightValue": "sent",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
}
},
{
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "or",
"conditions": [
{
"id": "is-failed",
"leftValue": "={{ $json.status }}",
"rightValue": "failed",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"id": "is-undelivered",
"leftValue": "={{ $json.status }}",
"rightValue": "undelivered",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
}
}
]
},
"options": {
"fallbackOutput": "extra"
}
},
"id": "route-by-delivery",
"name": "Route: Delivery Status",
"type": "n8n-nodes-base.switch",
"typeVersion": 3.2,
"position": [
2224,
-48
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { success: true, status: 'delivered', message_sid: $('Send SMS via Twilio').first().json.sid, recipient: $('Extract & Normalize Parameters').first().json.phone_number, first_name: $('Extract & Normalize Parameters').first().json.first_name, company_name: $('Extract & Normalize Parameters').first().json.company_name, request_id: $('Extract & Normalize Parameters').first().json.request_id, delivery_status: $json.status } }}",
"options": {
"responseCode": 200
}
},
"id": "respond-success",
"name": "Success: Delivered",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
2448,
-128
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { success: true, status: 'pending', note: 'Message accepted, delivery not yet confirmed', message_sid: $('Send SMS via Twilio').first().json.sid, recipient: $('Extract & Normalize Parameters').first().json.phone_number, request_id: $('Extract & Normalize Parameters').first().json.request_id, delivery_status: $json.status || 'unknown' } }}",
"options": {
"responseCode": 202
}
},
"id": "respond-pending",
"name": "Warning: Pending",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
2448,
256
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "troubleshoot",
"name": "troubleshooting",
"value": "={{ (() => { const code = $json.error_code; const map = { 30032: 'CRITICAL: Toll-free number not verified for A2P. Complete verification at Twilio console.', 21211: 'Invalid phone format - needs E.164 (+1XXXXXXXXXX)', 21614: 'Landline or VoIP number - not SMS-capable', 30003: 'Carrier unreachable - temporary, retry later', 30004: 'Blocked by carrier spam filter', 30005: 'Number does not exist', 30007: 'Carrier violation - check message content' }; return map[code] || 'Check https://twilio.com/docs/api/errors/' + code; })() }}",
"type": "string"
}
]
},
"options": {}
},
"id": "build-error-response",
"name": "Build: Error Details",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
2448,
64
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { success: false, error: 'DELIVERY_FAILED', message_sid: $('Send SMS via Twilio').first().json.sid, recipient: $('Extract & Normalize Parameters').first().json.phone_number, error_code: $json.error_code, error_message: $json.error_message, request_id: $('Extract & Normalize Parameters').first().json.request_id, troubleshooting: $json.troubleshooting } }}",
"options": {
"responseCode": 500
}
},
"id": "respond-delivery-failed",
"name": "Error: Delivery Failed",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
2672,
64
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { success: false, error: 'MAX_RETRIES_EXCEEDED', message: 'Failed after 3 attempts', last_error_code: $json.code || $json.error_code, last_error_message: $json.message || $json.error_message, recipient: $('Extract & Normalize Parameters').first().json.phone_number, request_id: $('Extract & Normalize Parameters').first().json.request_id } }}",
"options": {
"responseCode": 500
}
},
"id": "respond-max-retries",
"name": "Error: Max Retries",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
2224,
592
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { success: false, error: 'TWILIO_PERMANENT_ERROR', error_code: $json.code || $json.error_code || 'UNKNOWN', error_message: $json.message || $json.error_message || 'Permanent failure - do not retry', recipient: $('Extract & Normalize Parameters').first().json.phone_number, request_id: $('Extract & Normalize Parameters').first().json.request_id, troubleshooting: (() => { const code = $json.code || $json.error_code; const map = { 30032: 'Toll-free not A2P verified - complete at Twilio console', 21211: 'Invalid phone format', 21614: 'Not SMS-capable (landline)', 21610: 'Unsubscribed recipient' }; return map[code] || 'Check Twilio error docs'; })() } }}",
"options": {
"responseCode": 400
}
},
"id": "respond-permanent-error",
"name": "Error: Permanent Failure",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
2000,
688
]
}
],
"connections": {
"ElevenLabs Tool Webhook": {
"main": [
[
{
"node": "Extract & Normalize Parameters",
"type": "main",
"index": 0
}
]
]
},
"Extract & Normalize Parameters": {
"main": [
[
{
"node": "Validate Phone Format",
"type": "main",
"index": 0
}
]
]
},
"Validate Phone Format": {
"main": [
[
{
"node": "Merge Valid Paths",
"type": "main",
"index": 0
}
],
[
{
"node": "Merge Valid Paths",
"type": "main",
"index": 1
}
],
[
{
"node": "Error: Missing Phone",
"type": "main",
"index": 0
}
],
[
{
"node": "Error: Invalid Phone",
"type": "main",
"index": 0
}
]
]
},
"Merge Valid Paths": {
"main": [
[
{
"node": "Compose SMS Message",
"type": "main",
"index": 0
}
]
]
},
"Compose SMS Message": {
"main": [
[
{
"node": "Send SMS via Twilio",
"type": "main",
"index": 0
}
]
]
},
"Send SMS via Twilio": {
"main": [
[
{
"node": "Check Send Result",
"type": "main",
"index": 0
}
],
[
{
"node": "Check Send Result",
"type": "main",
"index": 0
}
]
]
},
"Check Send Result": {
"main": [
[
{
"node": "Verify: Fetch Status",
"type": "main",
"index": 0
}
],
[
{
"node": "Retry: Check Attempts",
"type": "main",
"index": 0
}
],
[
{
"node": "Error: Permanent Failure",
"type": "main",
"index": 0
}
]
]
},
"Retry: Check Attempts": {
"main": [
[
{
"node": "Wait Before Retry",
"type": "main",
"index": 0
}
],
[
{
"node": "Error: Max Retries",
"type": "main",
"index": 0
}
]
]
},
"Wait Before Retry": {
"main": [
[
{
"node": "Increment Attempt Counter",
"type": "main",
"index": 0
}
]
]
},
"Increment Attempt Counter": {
"main": [
[
{
"node": "Send SMS via Twilio",
"type": "main",
"index": 0
}
]
]
},
"Verify: Fetch Status": {
"main": [
[
{
"node": "Route: Delivery Status",
"type": "main",
"index": 0
}
]
]
},
"Route: Delivery Status": {
"main": [
[
{
"node": "Success: Delivered",
"type": "main",
"index": 0
}
],
[
{
"node": "Build: Error Details",
"type": "main",
"index": 0
}
],
[
{
"node": "Warning: Pending",
"type": "main",
"index": 0
}
]
]
},
"Build: Error Details": {
"main": [
[
{
"node": "Error: Delivery Failed",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"saveManualExecutions": true,
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "all",
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false
},
"tags": [
"ALPHA"
]
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
[PROD] sms / sarah-tool. Uses httpRequest. Webhook trigger; 20 nodes.
Source: https://github.com/wranngle/n8n_showcase/blob/main/workflows/sms/sarah-tool.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This n8n template provides enterprise-level version control for your workflows using GitHub integration. Stop losing hours to broken workflows and manual exports – get proper commit history, visual di
This flow creates dummy files for every item added in your *Arrs (Radarr/Sonarr) with the tag .
eek-Go v2 (Batch-Then-Review). Uses httpRequest. Webhook trigger; 75 nodes.
This workflow receives webhook requests from a content calendar and uses the X API v2 to publish text posts, threads, image/video posts, and polls, as well as delete existing posts and run a credentia
This workflow acts as a central API gateway for all technical indicator agents in the Binance Spot Market Quant AI system. It listens for incoming webhook requests and dynamically routes them to the c