Rodrigo
06/04/2026, 2:33 PMJarrah
06/04/2026, 2:36 PMRodrigo
06/04/2026, 2:50 PMChiranjeet Mishra
06/04/2026, 4:18 PM{{ JSON.stringify($json) }} inside hand-written JSON breaks the response when the substituted value itself contains double quotes. The inner quotes do not get escaped, so Vapi receives invalid JSON and cannot parse the response.
Replace your entire Response Body with this single line:
={{ JSON.stringify({ results: [{ toolCallId: json.toolCallId,result:JSON.stringify(json.toolCallId, result: JSON.stringify( json.toolCallId,result:JSON.stringify(json) }] }) }}
Make sure the field starts with = so n8n treats it as an expression rather than plain text. The outer JSON.stringify handles all the escaping correctly, so Vapi receives properly formatted JSON every time.
Keep the Content-Type header as application/json and keep Respond With set to Text. That should resolve it.
Let me know how it goes.
Regards,
Chiranjeet
Vapi SupportRodrigo
06/04/2026, 5:02 PMRodrigo
06/04/2026, 5:04 PMChiranjeet Mishra
06/05/2026, 9:45 AMRodrigo
06/05/2026, 2:31 PMRodrigo
06/05/2026, 2:35 PMChiranjeet Mishra
06/05/2026, 3:02 PM01JXYZ... (a long alphanumeric string), listed next to each call entry. Note that this is different from the toolCallId values inside your n8n workflow.
While you grab that, also check two things in n8n:
1. Open the execution history for this workflow and confirm every node ran to completion with no errors. If the workflow is failing on an earlier node, the Respond to Webhook node may never actually fire.
2. Confirm that the webhook URL configured in your Vapi tool matches the n8n URL you are testing with. n8n uses a different URL for test mode (/webhook-test/) versus live production mode (/webhook/). These need to match.
Once you share the call ID I can check what Vapi actually received and pinpoint exactly where it is breaking down.
Regards,
Chiranjeet
Vapi SupportRodrigo
06/05/2026, 9:49 PMChiranjeet Mishra
06/06/2026, 9:48 AMRodrigo
06/06/2026, 3:04 PMRodrigo
06/06/2026, 3:04 PMChiranjeet Mishra
06/06/2026, 3:28 PM{
"toolCallId": "<the id from the incoming request>",
"result": "<your tool output>"
}
]
}
The toolCallId must match exactly what Vapi sent. In n8n, that value arrives in the webhook body and can be read as {{ $json.message.toolCalls[0].id }}.
Can you check the execution history in n8n for the run that happened during this test, and share what your Respond to Webhook node is actually sending back? That will let me confirm whether the format matches or point to the exact field causing the mismatch.
Regards,
Chiranjeet
Vapi SupportRodrigo
06/06/2026, 7:31 PMRodrigo
06/06/2026, 7:32 PMChiranjeet Mishra
06/08/2026, 8:47 AM{
toolCallId: $json.toolCallId,
result: JSON.stringify($json)
}
]
} }}
After making that change, test it with a real call (not just the n8n test button) and share the call ID. I want to confirm Vapi receives and accepts the response during an actual conversation.
Regards,
Chiranjeet
Vapi SupportRodrigo
06/08/2026, 1:49 PMChiranjeet Mishra
06/08/2026, 2:42 PMtoolCallId: $('Webhook').item.json.toolCalls\[0\].id,
result: JSON.stringify($json)
}]
} }}
$('Webhook') references your Webhook trigger node by name and reads the live data from the actual current request every time, so the toolCallId will always match what Vapi sent.
Please also check your Edit Fields or Code node upstream. If any of those are extracting toolCallId using $json.message.toolCalls[0].id, change that to $json.toolCalls[0].id as well.
After making this change, run a new test call and share the call ID. That will confirm whether the mismatch is resolved.
Apologies for the incorrect path earlier.
Regards,
Chiranjeet
Vapi SupportRodrigo
06/09/2026, 6:58 PM