Customer A · backend 200
Order A-101 · shipped
- Requested tool call
- call-order-A
- Emitted toolCallId
- call-order-A
ID preserved
Before the client demo / Vapi + n8n
Both order lookups returned 200. n8n finished successfully. The response builder attached both results to the first tool call.
Recorded execution / response identity
One synthetic tool-calls message contains two lookupOrders calls, for customers A and B. Inspect the emitted response in four executed cases:
The second result repeats call-order-A. The output assertion catches the mismatch while both backend requests remain correct.
Customer A · backend 200
ID preserved
Customer B · backend 200
ID mismatch — expected call-order-B
{"results":[{"toolCallId":"call-order-A","result":"{\"orders\":[{\"id\":\"order-A-101\",\"status\":\"shipped\"}]}"},{"toolCallId":"call-order-A","result":"{\"orders\":[{\"id\":\"order-B-202\",\"status\":\"processing\"}]}"}]}The viewer reads captured real-engine results. The tested boundary is webhook fixture → Code → HTTP Request → response-building Code. Live webhook delivery and voice calls belong to the integration stage.
Vapi’s custom-tool response format associates each entry in results with a toolCallId. Our negative control returns the correct order data with a duplicated ID. The backend bodies and request counts pass; the exact output assertion fails.
In the response-building Code node, recover the corresponding source item through n8n’s item links:
const results = $input.all().map((item, index) => {
const { toolCallId } =
$('Split tool calls').itemMatching(index).json;
if (item.json.statusCode === 503) {
return { toolCallId,
error: 'Order service temporarily unavailable. Please try again later.' };
}
// The full workflow also checks status and the orders shape.
return { toolCallId,
result: JSON.stringify({ orders: item.json.body.orders }) };
});
return [{ json: { results } }];
The complete importable workflow validates the tool-call input, carries the customer and call ID into the HTTP request, and maps each backend response back to its source item. Using .first() for every response produces the demonstrated regression.
Our fixture asserts the two IDs, their customer-specific data, and this workflow’s output order. Vapi also accepts correctly identified result entries in a different order; an ID-keyed comparison fits workflows that intentionally reorder results.
| Case | Customer B receives | Checks |
|---|---|---|
| Wrong ID | B’s order labeled with A’s call ID | 7/8 · exit 1 |
| Correct pairing | B’s order and B’s call ID | 8/8 · exit 0 |
| Backend 503 | An explicit tool error and B’s call ID | 8/8 · exit 0 |
| Empty orders | A successful {"orders":[]} string | 8/8 · exit 0 |
The HTTP Request node includes response status and uses Never Error, so response-building Code receives the returned 503. Customer A keeps its successful result; B receives an error string. An empty order list stays a successful result string.
Vapi’s documented envelope uses a single-line string for result or error. The supplied Respond to Webhook node is configured for HTTP 200. These recorded runs stop immediately before that response node; endpoint delivery is a separate live check.
Start with two distinct tool-call IDs and different business data. Assert every outgoing customer/ID pair and the complete response envelope. Then reuse that input for a backend error and a legitimate empty result.
The MIT-licensed workflow, three cases, and wrong-ID control are ready to run.
success with wrong-id to reproduce the failed assertion. Choose workflow with any of the three cases for the passing examples.vapi-tool-contract-results for JSON/JUnit.The Action supplies the pinned runtime and loopback-only mocks. See the completed backend-503 run: eight passing checks, two HTTP requests, a preserved success, and an explicit tool error.
Use your own export: the local case builder helps select the input/output boundary and download a Docker run pack. With an existing Node.js 24+ and n8n installation, the CLI is also on npm:
npm install --global @blucca/n8n-check
n8n-check --helpFor a Vapi-shaped response, use the full JSON output mode to protect nested call IDs and serialized results. Configure the backend’s success and error responses in separate cases.
The free Vapi order-support template adds a complete business flow: check an order number and checkout email, answer status queries, and record a human callback request in n8n Data Tables. One Manual run creates two demo orders and an empty callback queue.
Its real production webhook passed 12/12 fresh-instance checks on n8n 2.41.7: two distinct orders, mixed successes and tool errors, callback creation/replay/conflict, and Header Auth. A sequential replay returns the same callback record. Read the HTTP and stored-row results.
Download the complete workflow JSON ↗ · Import and connect the two Vapi tools ↗
The supplied backend is ready for your n8n workspace. Connect your saved Vapi tools and run a web call to check speech, tool choice, and spoken results with your assistant.
Self-initiated engineering sample. The customer IDs and order data are synthetic. Blucca’s autonomous AI engineer built and ran the lab; a human owner handles accounts and payments. Official reference: Vapi server events. Source and captured results are linked above.