AutomationFlowsGeneral › Main Vapi Handler

Main Vapi Handler

main-vapi-handler. Webhook trigger; 20 nodes.

Webhook trigger★★★★☆ complexity20 nodes
General Trigger: Webhook Nodes: 20 Complexity: ★★★★☆ Added:

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 →

Download .json
{
  "name": "main-vapi-handler",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "vapi-server-url",
        "responseMode": "responseNode"
      },
      "id": "webhook-vapi",
      "name": "Webhook: VAPI Server URL",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        100,
        300
      ],
      "notes": "Receives all VAPI events. Verify x-vapi-secret header in production."
    },
    {
      "parameters": {
        "jsCode": "// Extract VAPI event type and validate\nconst body = $input.first().json.body;\nconst messageType = body.message?.type || 'unknown';\nconst call = body.message?.call || {};\nconst customerPhone = call.customer?.number || null;\nconst callId = call.id || null;\n\nreturn [{\n  json: {\n    messageType,\n    customerPhone,\n    callId,\n    fullPayload: body,\n    functionCall: body.message?.functionCall || null,\n    transcript: body.message?.transcript || null,\n    endOfCallReport: body.message?.endOfCallReport || null\n  }\n}];"
      },
      "id": "parse-vapi-event",
      "name": "Code: Parse VAPI Event",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        320,
        300
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "conditions": [
                  {
                    "leftValue": "={{ $json.messageType }}",
                    "rightValue": "assistant-request",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": "Assistant Request"
            },
            {
              "conditions": {
                "conditions": [
                  {
                    "leftValue": "={{ $json.messageType }}",
                    "rightValue": "function-call",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": "Function Call"
            },
            {
              "conditions": {
                "conditions": [
                  {
                    "leftValue": "={{ $json.messageType }}",
                    "rightValue": "end-of-call-report",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": "End of Call"
            },
            {
              "conditions": {
                "conditions": [
                  {
                    "leftValue": "={{ $json.messageType }}",
                    "rightValue": "status-update",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": "Status Update"
            }
          ]
        },
        "mode": "rules",
        "options": {
          "fallbackOutputType": "extra"
        }
      },
      "id": "switch-event-type",
      "name": "Switch: VAPI Event Type",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        540,
        300
      ]
    },
    {
      "parameters": {
        "workflowId": "={{ $vars.SUB_CUSTOMER_LOOKUP_ID }}"
      },
      "id": "ar-customer-lookup",
      "name": "Assistant Request: Customer Lookup",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1,
      "position": [
        760,
        100
      ]
    },
    {
      "parameters": {
        "workflowId": "={{ $vars.SUB_MEMORY_RETRIEVAL_ID }}"
      },
      "id": "ar-memory-retrieval",
      "name": "Assistant Request: Memory Retrieval",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1,
      "position": [
        980,
        100
      ]
    },
    {
      "parameters": {
        "jsCode": "// Build dynamic VAPI assistant config with RAG context\nconst customer = $('Assistant Request: Customer Lookup').first().json.customer_profile;\nconst memory = $('Assistant Request: Memory Retrieval').first().json;\nconst isNew = $('Assistant Request: Customer Lookup').first().json.is_new_customer;\n\nconst firstName = customer?.name?.split(' ')[0] || 'love';\nconst ragContext = memory?.rag_context || 'No previous interaction history.';\nconst lastAppt = memory?.last_appointment;\n\nlet firstMessage = isNew\n  ? `Hey love! Welcome to Sweet Hand Braids \u2014 I'm your AI stylist! Whether you're looking for a fresh new look or need to book an appointment, I've got you. What style are you thinking about?`\n  : `Hey ${firstName}! Welcome back to Sweet Hand Braids! ${lastAppt ? `Last time you got ${lastAppt.service_type} and looked amazing!` : ''} What can I do for you today?`;\n\nconst systemPrompt = `You are SweetHand, the AI hair braiding stylist at Sweet Hand Braids salon.\n\nPERSONALITY:\n- Warm, confident, knowledgeable \u2014 like a best friend who is an expert braider\n- Use natural, friendly language. Keep responses SHORT (2-3 sentences) since this is voice\n- Be enthusiastic about hair \u2014 you LOVE what you do\n- Give honest advice with kindness\n\nCUSTOMER CONTEXT:\n${ragContext}\n\nCURRENT CUSTOMER: ${customer?.name || 'New Customer'}\nHair Profile: ${JSON.stringify(customer?.hair_profile || 'Unknown \u2014 ask about their hair')}\nVisit Count: ${customer?.lifetime_visits || 0}\n\nRULES:\n1. Keep responses to 2-3 sentences MAX \u2014 this is a phone call\n2. If they want to book, use the book_appointment function\n3. Always confirm details before booking: style, date, time\n4. If unsure about their hair type, ask them to describe it\n5. When discussing styles, mention estimated time and price range\n6. End interactions with a clear next step`;\n\nreturn [{\n  json: {\n    assistant: {\n      firstMessage: firstMessage,\n      model: {\n        provider: 'openai',\n        model: 'gpt-4o',\n        temperature: 0.7,\n        maxTokens: 250,\n        systemMessage: systemPrompt,\n        tools: [\n          {\n            type: 'function',\n            function: {\n              name: 'book_appointment',\n              description: 'Book a hair braiding appointment for the customer',\n              parameters: {\n                type: 'object',\n                properties: {\n                  service_type: { type: 'string', description: 'Type of braiding service (e.g., box_braids, goddess_locs, cornrows, passion_twists)' },\n                  preferred_date: { type: 'string', description: 'Preferred date in YYYY-MM-DD format' },\n                  preferred_time: { type: 'string', description: 'Preferred time in HH:MM format' },\n                  notes: { type: 'string', description: 'Style details: color, length, thickness preferences' }\n                },\n                required: ['service_type', 'preferred_date', 'preferred_time']\n              }\n            }\n          },\n          {\n            type: 'function',\n            function: {\n              name: 'check_availability',\n              description: 'Check available appointment slots for a given date',\n              parameters: {\n                type: 'object',\n                properties: {\n                  start_date: { type: 'string', description: 'Start date YYYY-MM-DD' },\n                  end_date: { type: 'string', description: 'End date YYYY-MM-DD (optional, defaults to start_date)' },\n                  service_duration_minutes: { type: 'integer', description: 'Estimated service duration in minutes' }\n                },\n                required: ['start_date']\n              }\n            }\n          },\n          {\n            type: 'function',\n            function: {\n              name: 'cancel_appointment',\n              description: 'Cancel the customers most recent or specified appointment',\n              parameters: {\n                type: 'object',\n                properties: {\n                  reason: { type: 'string', description: 'Reason for cancellation' }\n                }\n              }\n            }\n          },\n          {\n            type: 'function',\n            function: {\n              name: 'reschedule_appointment',\n              description: 'Reschedule an existing appointment to a new date and time',\n              parameters: {\n                type: 'object',\n                properties: {\n                  new_date: { type: 'string', description: 'New date YYYY-MM-DD' },\n                  new_time: { type: 'string', description: 'New time HH:MM' }\n                },\n                required: ['new_date', 'new_time']\n              }\n            }\n          }\n        ]\n      },\n      voice: {\n        provider: '11labs',\n        voiceId: 'EXAVITQu4vr4xnSDxMaL'\n      },\n      silenceTimeoutSeconds: 30,\n      maxDurationSeconds: 600,\n      endCallFunctionEnabled: true\n    }\n  }\n}];"
      },
      "id": "ar-build-config",
      "name": "Code: Build Assistant Config",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        100
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}"
      },
      "id": "ar-respond",
      "name": "Respond: Assistant Config",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        1420,
        100
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "conditions": [
                  {
                    "leftValue": "={{ $json.functionCall.name }}",
                    "rightValue": "book_appointment",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": "Book"
            },
            {
              "conditions": {
                "conditions": [
                  {
                    "leftValue": "={{ $json.functionCall.name }}",
                    "rightValue": "check_availability",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": "Check"
            },
            {
              "conditions": {
                "conditions": [
                  {
                    "leftValue": "={{ $json.functionCall.name }}",
                    "rightValue": "cancel_appointment",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": "Cancel"
            },
            {
              "conditions": {
                "conditions": [
                  {
                    "leftValue": "={{ $json.functionCall.name }}",
                    "rightValue": "reschedule_appointment",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": "Reschedule"
            }
          ]
        },
        "mode": "rules"
      },
      "id": "fc-switch-function",
      "name": "Switch: Function Name",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        760,
        300
      ]
    },
    {
      "parameters": {
        "workflowId": "={{ $vars.SUB_CUSTOMER_LOOKUP_ID }}"
      },
      "id": "fc-customer-lookup",
      "name": "FC: Customer Lookup",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1,
      "position": [
        980,
        220
      ]
    },
    {
      "parameters": {
        "workflowId": "={{ $vars.SUB_APPOINTMENT_MANAGER_ID }}"
      },
      "id": "fc-book",
      "name": "FC: Book Appointment",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1,
      "position": [
        1200,
        220
      ]
    },
    {
      "parameters": {
        "workflowId": "={{ $vars.SUB_APPOINTMENT_MANAGER_ID }}"
      },
      "id": "fc-check",
      "name": "FC: Check Availability",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1,
      "position": [
        1200,
        340
      ]
    },
    {
      "parameters": {
        "workflowId": "={{ $vars.SUB_APPOINTMENT_MANAGER_ID }}"
      },
      "id": "fc-cancel",
      "name": "FC: Cancel Appointment",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1,
      "position": [
        1200,
        460
      ]
    },
    {
      "parameters": {
        "workflowId": "={{ $vars.SUB_APPOINTMENT_MANAGER_ID }}"
      },
      "id": "fc-reschedule",
      "name": "FC: Reschedule Appointment",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1,
      "position": [
        1200,
        580
      ]
    },
    {
      "parameters": {
        "jsCode": "// Format function result for VAPI\nconst result = $input.first().json;\nreturn [{ json: { result: JSON.stringify(result) } }];"
      },
      "id": "fc-format-result",
      "name": "Code: Format Function Result",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1420,
        380
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ result: $json.result }) }}"
      },
      "id": "fc-respond",
      "name": "Respond: Function Result",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        1640,
        380
      ]
    },
    {
      "parameters": {
        "workflowId": "={{ $vars.SUB_CUSTOMER_LOOKUP_ID }}"
      },
      "id": "eoc-customer-lookup",
      "name": "EOC: Customer Lookup",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1,
      "position": [
        760,
        560
      ]
    },
    {
      "parameters": {
        "workflowId": "={{ $vars.SUB_MEMORY_WRITER_ID }}"
      },
      "id": "eoc-memory-writer",
      "name": "EOC: Store Transcript in Memory",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1,
      "position": [
        980,
        560
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ status: 'ok' }) }}"
      },
      "id": "eoc-respond",
      "name": "Respond: EOC Ack",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        1200,
        560
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ status: 'ok' }) }}"
      },
      "id": "status-respond",
      "name": "Respond: Status Ack",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        760,
        700
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ status: 'ok' }) }}"
      },
      "id": "fallback-respond",
      "name": "Respond: Fallback Ack",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        760,
        820
      ]
    }
  ],
  "connections": {
    "Webhook: VAPI Server URL": {
      "main": [
        [
          {
            "node": "Code: Parse VAPI Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Parse VAPI Event": {
      "main": [
        [
          {
            "node": "Switch: VAPI Event Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch: VAPI Event Type": {
      "main": [
        [
          {
            "node": "Assistant Request: Customer Lookup",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Switch: Function Name",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "EOC: Customer Lookup",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond: Status Ack",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond: Fallback Ack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Assistant Request: Customer Lookup": {
      "main": [
        [
          {
            "node": "Assistant Request: Memory Retrieval",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Assistant Request: Memory Retrieval": {
      "main": [
        [
          {
            "node": "Code: Build Assistant Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Build Assistant Config": {
      "main": [
        [
          {
            "node": "Respond: Assistant Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch: Function Name": {
      "main": [
        [
          {
            "node": "FC: Customer Lookup",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "FC: Check Availability",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "FC: Customer Lookup",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "FC: Customer Lookup",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FC: Customer Lookup": {
      "main": [
        [
          {
            "node": "FC: Book Appointment",
            "type": "main",
            "index": 0
          },
          {
            "node": "FC: Cancel Appointment",
            "type": "main",
            "index": 0
          },
          {
            "node": "FC: Reschedule Appointment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FC: Book Appointment": {
      "main": [
        [
          {
            "node": "Code: Format Function Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FC: Check Availability": {
      "main": [
        [
          {
            "node": "Code: Format Function Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FC: Cancel Appointment": {
      "main": [
        [
          {
            "node": "Code: Format Function Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FC: Reschedule Appointment": {
      "main": [
        [
          {
            "node": "Code: Format Function Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Format Function Result": {
      "main": [
        [
          {
            "node": "Respond: Function Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "EOC: Customer Lookup": {
      "main": [
        [
          {
            "node": "EOC: Store Transcript in Memory",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "EOC: Store Transcript in Memory": {
      "main": [
        [
          {
            "node": "Respond: EOC Ack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "errorWorkflow": "sub-error-handler"
  },
  "tags": [
    {
      "name": "sweethandbraids"
    },
    {
      "name": "main-workflow"
    },
    {
      "name": "vapi"
    },
    {
      "name": "production"
    }
  ]
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

main-vapi-handler. Webhook trigger; 20 nodes.

Source: https://github.com/rdmahpcengineer-gpu/sweethandbraidsMainProject/blob/9c327e7b2190848ae227fe277b0662173be78f38/n8n/main-vapi-handler.json — original creator credit. Request a take-down →

More General workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

General

A production-ready authentication workflow implementing secure user registration, login, token verification, and refresh token mechanisms. Perfect for adding authentication to any application without

Crypto, Data Table, Execute Workflow Trigger
General

Portfolio Orchestrator. Uses httpRequest. Webhook trigger; 59 nodes.

HTTP Request
General

This n8n template demonstrates how a simple Multi-Layer Perceptron (MLP) neural network can predict housing prices. The prediction is based on four key features, processed through a three-layer model.

General

github code Try yourself

Google Calendar
General

This workflow contains community nodes that are only compatible with the self-hosted version of n8n.

N8N Nodes 1Shot