e
Hey Vapi team – looking for guidance on a tool + identity issue. Context We use Vapi for an AI Voice SDR that calls users after they request a demo. Calls are triggered via n8n and include prospect identity in metadata: metadata.email metadata.firstName metadata.lastName If the user chooses a time, the assistant calls a custom bookMeeting tool, which POSTs to an n8n webhook that books a HubSpot meeting. What works Posting directly to the /book-meeting webhook via Postman with: email firstName / lastName etc works perfectly and sends the calendar invite to the prospect. The problem When bookMeeting is triggered from Vapi during a real call, the payload sometimes contains internal Workleap SDR details (eg. Natalia or Marissa) instead of the prospect. This often happens after the user answers the “how many employees” question (eg. “50 employees”), which we only intend to use for routing logic. Example call log where this happened: 019b2470-6e5a-7dd2-b448-7e836ced2a9a Response we received (actual payload): { "email": "marissa.likoudis@workleap.com ", "firstName": "Marissa", "lastName": "Likoudis", "timezone": "America/Los_Angeles", "startMillisUtc": 1765895400000, "durationMillis": 900000 } Response we expected (prospect identity from metadata): { "email": "ewan.stott@workleap.com ", "firstName": "Ewan", "lastName": "Stott", "timezone": "America/Los_Angeles", "startMillisUtc": 1765895400000, "durationMillis": 900000 } What we suspect bookMeeting requires email / name, and if the model is uncertain it may fall back to defaults or inferred identities. We’ve removed default values but still see this intermittently. Questions Is there a recommended way to force tool args to come only from call metadata? What happens in Vapi if a required tool arg is missing or unclear? Is there a best practice for passing a stable call or session identifier into tools so downstream systems can safely rehydrate identity? Any guidance would be really appreciated – thanks!
v
To force tool arguments to come only from call metadata, define a strict structured output schema for your tool with required fields mapped to the metadata keys. For example:
Copy code
json
{
  "type": "object",
  "properties": {
    "email": { "type": "string", "description": "Prospect email from metadata" },
    "firstName": { "type": "string", "description": "Prospect first name from metadata" },
    "lastName": { "type": "string", "description": "Prospect last name from metadata" }
  },
  "required": ["email", "firstName", "lastName"]
}
This ensures only prospect data from metadata is used in the tool call, preventing fallback to internal identities. See the [structured outputs examples](https://docs.vapi.ai/assistants/structured-outputs-examples) for more details. Source: - [Structured outputs examples](https://docs.vapi.ai/assistants/structured-outputs-examples)
e
I have followed this setup in the tool, but it's taking it literally...
Tool config (bookMeeting): { "body": { "type": "object", "required": [ "language", "startMillisUtc", "timezone", "firstName", "lastName", "email" ], "properties": { "email": { "description": "Prospect email. Must be the real demo requester email from call metadata. Never use placeholder emails.", "type": "string", "default": "" }, "language": { "description": "\"en\" or \"fr\"", "type": "string", "default": "" }, "lastName": { "description": "Prospect last name from call metadata. Never use internal Workleap names", "type": "string", "default": "" }, "timezone": { "description": "Timezone must be a valid IANA timezone string (e.g. \"America/Los_Angeles\", not a numeric offset).", "type": "string", "default": "" }, "firstName": { "description": "Prospect first name from call metadata. Never use internal Workleap names.", "type": "string", "default": "" }, "durationMillis": { "description": "Example: 900000", "type": "number", "default": "" }, "startMillisUtc": { "description": "Example: 1765549200000 (for 9:00 AM PST on Dec 12, 2025)", "type": "number", "default": "" } } }, }