diff --git a/fern/assistants/structured-outputs-quickstart.mdx b/fern/assistants/structured-outputs-quickstart.mdx
index f5d0e1886..45cbd54ea 100644
--- a/fern/assistants/structured-outputs-quickstart.mdx
+++ b/fern/assistants/structured-outputs-quickstart.mdx
@@ -10,22 +10,38 @@ This quickstart guide will help you set up structured outputs to automatically e
### What are structured outputs?
-Structured outputs are AI-powered data extraction templates that automatically capture and organize information from conversations. They work by:
+Structured outputs are AI-powered analysis and extraction tools that intelligently process conversation data after calls end. They go beyond simple data extraction to provide intelligent analysis and evaluation. They work by:
-1. **Listening to conversations** - As your assistant talks with customers, structured outputs analyze the conversation in real-time
-2. **Extracting key information** - Based on your defined schema, they identify and extract relevant data points like names, emails, preferences, and issues
-3. **Validating and formatting** - The extracted data is validated against your schema rules and formatted into clean, structured JSON
-4. **Delivering results** - The structured data is available immediately after the call ends via API or webhooks
+1. **Processing complete call context** - After the call ends, structured outputs analyze the full transcript, messages, tool call results, and call metadata
+2. **Intelligent extraction & analysis** - Based on your schema, they can extract data, evaluate outcomes, analyze sentiment, determine success criteria, and summarize complex interactions
+3. **Validating and formatting** - Results are validated against your schema rules and formatted into clean, structured JSON
+4. **Delivering insights** - The processed data and insights are available via API or webhooks once analysis is complete
### When are structured outputs generated?
Structured outputs are processed:
-- **During the call** - Data is extracted in real-time as the conversation happens
-- **After call completion** - Final validation and formatting occurs when the call ends
+- **After call completion** - The full conversation is analyzed once the call ends
+- **Processing time** - Typically completes within a few seconds after call termination
- **Available via** - Call artifacts in the API response or webhook events
+### What data do structured outputs have access to?
+
+When processing, structured outputs can analyze:
+- **Complete transcript** - The full conversation between assistant and customer
+- **Messages history** - All messages exchanged during the call
+- **Tool call results** - Outcomes from any tools or functions executed
+- **Assistant context** - System prompts and configuration used during the call
+
### Why use structured outputs?
+**Beyond simple data extraction:**
+- **Call evaluation** - Determine if objectives were met (appointment booked, issue resolved)
+- **Sentiment analysis** - Understand customer satisfaction and emotional state
+- **CSAT scoring** - Extract customer satisfaction scores from feedback
+- **Intelligent summaries** - Generate contextual summaries of complex conversations
+- **Success metrics** - Evaluate agent performance and call outcomes
+
+**Operational benefits:**
- **Automate data entry** - No more manual transcription or form filling
- **Ensure consistency** - Every call captures the same structured information
- **Enable integrations** - Automatically sync data to CRMs, ticketing systems, or databases
@@ -45,79 +61,88 @@ A customer support assistant that automatically extracts:
Sign up at [dashboard.vapi.ai](https://dashboard.vapi.ai)
- Get your API key from the Dashboard settings
+ Get your API key from **API Keys** on sidebar
## Step 1: Create your structured output
-You can create structured outputs using either the Dashboard UI or the API.
-
-### Option A: Using the Dashboard (Recommended for beginners)
+Define what information you want to extract using a [JSON Schema](https://json-schema.org/learn/getting-started-step-by-step). JSON Schema is a standard for describing data structures - [learn more about JSON Schema here](https://json-schema.org/understanding-json-schema/).
-
-
- 1. Log in to [dashboard.vapi.ai](https://dashboard.vapi.ai)
- 2. Click on **Structured Outputs** in the left sidebar
- 3. Click **Create New Structured Output**
-
-
-
- 1. **Name**: Enter "Support Ticket"
- 2. **Type**: Select "AI" (for automatic extraction)
- 3. **Description**: Add "Extract support ticket information from customer calls"
-
-
-
- Use the visual schema builder or paste this JSON directly:
- ```json
- {
- "type": "object",
- "properties": {
- "customer": {
- "type": "object",
- "properties": {
- "name": {"type": "string", "description": "Customer full name"},
- "email": {"type": "string", "format": "email", "description": "Customer email"},
- "phone": {"type": "string", "description": "Customer phone number"}
- },
- "required": ["name"]
- },
- "issue": {
+
+
+
+
+ 1. Log in to [dashboard.vapi.ai](https://dashboard.vapi.ai)
+ 2. Click on **Structured Outputs** in the left sidebar
+ 3. Click **Create New Structured Output**
+
+
+
+ 1. **Name**: Enter "Support Ticket"
+ 2. **Type**: Select "Object"
+ 3. **Description**: Add "Extract support ticket information from customer calls"
+
+
+
+ Use the visual schema builder:
+ ```json
+ {
"type": "object",
"properties": {
- "description": {"type": "string", "description": "Issue description"},
- "category": {
- "type": "string",
- "enum": ["billing", "technical", "general", "complaint"],
- "description": "Issue category"
+ "customer": {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string", "description": "Customer full name"},
+ "email": {"type": "string", "format": "email", "description": "Customer email"},
+ "phone": {"type": "string", "description": "Customer phone number"}
+ },
+ "required": ["name"]
},
- "priority": {
- "type": "string",
- "enum": ["low", "medium", "high", "urgent"],
- "description": "Priority level"
+ "issue": {
+ "type": "object",
+ "properties": {
+ "description": {"type": "string", "description": "Issue description"},
+ "category": {
+ "type": "string",
+ "enum": ["billing", "technical", "general", "complaint"],
+ "description": "Issue category"
+ },
+ "priority": {
+ "type": "string",
+ "enum": ["low", "medium", "high", "urgent"],
+ "description": "Priority level"
+ }
+ },
+ "required": ["description", "category"]
+ },
+ "followUp": {
+ "type": "object",
+ "properties": {
+ "required": {"type": "boolean", "description": "Whether follow-up is needed"},
+ "method": {
+ "type": "string",
+ "enum": ["email", "phone", "none"],
+ "description": "Preferred follow-up method"
+ },
+ "notes": {"type": "string", "description": "Additional notes for follow-up"}
+ }
}
},
- "required": ["description", "category"]
+ "required": ["customer", "issue"]
}
- },
- "required": ["customer", "issue"]
- }
- ```
-
-
-
- 1. Click **Create Structured Output**
- 2. Copy the generated ID from the details page
- 3. You'll use this ID to link to your assistant
-
-
-
-### Option B: Using the API
-
-Define what information you want to extract using a [JSON Schema](https://json-schema.org/learn/getting-started-step-by-step). JSON Schema is a standard for describing data structures - [learn more about JSON Schema here](https://json-schema.org/understanding-json-schema/).
-
-
+ ```
+
+
+
+ 1. Click **Create Structured Output**
+ 2. In the structured output dialog, you can directly attach it to an assistant or workflow
+ 3. Select an existing assistant to attach this output to that assistant
+
+
+
+
+
```bash title="cURL"
curl -X POST https://api.vapi.ai/structured-output \
-H "Authorization: Bearer $VAPI_API_KEY" \
@@ -191,356 +216,364 @@ curl -X POST https://api.vapi.ai/structured-output \
}
}'
```
-
-```javascript title="Node.js"
-const response = await fetch('https://api.vapi.ai/structured-output', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${process.env.VAPI_API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- name: "Support Ticket",
- type: "ai",
- description: "Extract support ticket information from customer calls",
- schema: {
- type: "object",
- properties: {
- customer: {
- type: "object",
- properties: {
- name: {
- type: "string",
- description: "Customer full name"
- },
- email: {
- type: "string",
- format: "email",
- description: "Customer email address"
- },
- phone: {
- type: "string",
- description: "Customer phone number"
- }
+
+
+
+ ```typescript
+import { VapiClient } from "@vapi-ai/server-sdk";
+
+const vapi = new VapiClient({ token: process.env.VAPI_API_KEY! });
+
+const structuredOutput = await vapi.structuredOutputs.create({
+ name: "Support Ticket",
+ type: "ai",
+ description: "Extract support ticket information from customer calls",
+ schema: {
+ type: "object",
+ properties: {
+ customer: {
+ type: "object",
+ properties: {
+ name: {
+ type: "string",
+ description: "Customer full name"
},
- required: ["name"]
+ email: {
+ type: "string",
+ format: "email",
+ description: "Customer email address"
+ },
+ phone: {
+ type: "string",
+ description: "Customer phone number"
+ }
},
- issue: {
- type: "object",
- properties: {
- description: {
- type: "string",
- description: "Description of the customer issue"
- },
- category: {
- type: "string",
- enum: ["billing", "technical", "general", "complaint"],
- description: "Issue category"
- },
- priority: {
- type: "string",
- enum: ["low", "medium", "high", "urgent"],
- description: "Issue priority level"
- }
+ required: ["name"]
+ },
+ issue: {
+ type: "object",
+ properties: {
+ description: {
+ type: "string",
+ description: "Description of the customer issue"
},
- required: ["description", "category"]
+ category: {
+ type: "string",
+ enum: ["billing", "technical", "general", "complaint"],
+ description: "Issue category"
+ },
+ priority: {
+ type: "string",
+ enum: ["low", "medium", "high", "urgent"],
+ description: "Issue priority level"
+ }
},
- followUp: {
- type: "object",
- properties: {
- required: {
- type: "boolean",
- description: "Whether follow-up is needed"
- },
- method: {
- type: "string",
- enum: ["email", "phone", "none"],
- description: "Preferred follow-up method"
- },
- notes: {
- type: "string",
- description: "Additional notes for follow-up"
- }
+ required: ["description", "category"]
+ },
+ followUp: {
+ type: "object",
+ properties: {
+ required: {
+ type: "boolean",
+ description: "Whether follow-up is needed"
+ },
+ method: {
+ type: "string",
+ enum: ["email", "phone", "none"],
+ description: "Preferred follow-up method"
+ },
+ notes: {
+ type: "string",
+ description: "Additional notes for follow-up"
}
}
- },
- required: ["customer", "issue"]
- }
- })
+ }
+ },
+ required: ["customer", "issue"]
+ }
});
-const structuredOutput = await response.json();
console.log('Created structured output:', structuredOutput.id);
// Save this ID - you'll need it in the next step
```
+
-```python title="Python"
-import requests
+
+ ```python
+from vapi import Vapi
import os
-response = requests.post(
- 'https://api.vapi.ai/structured-output',
- headers={
- 'Authorization': f'Bearer {os.environ["VAPI_API_KEY"]}',
- 'Content-Type': 'application/json'
- },
- json={
- "name": "Support Ticket",
- "type": "ai",
- "description": "Extract support ticket information from customer calls",
- "schema": {
- "type": "object",
- "properties": {
- "customer": {
- "type": "object",
- "properties": {
- "name": {
- "type": "string",
- "description": "Customer full name"
- },
- "email": {
- "type": "string",
- "format": "email",
- "description": "Customer email address"
- },
- "phone": {
- "type": "string",
- "description": "Customer phone number"
- }
+vapi = Vapi(token=os.environ.get("VAPI_API_KEY"))
+
+structured_output = vapi.structured_outputs.create(
+ name="Support Ticket",
+ type="ai",
+ description="Extract support ticket information from customer calls",
+ schema={
+ "type": "object",
+ "properties": {
+ "customer": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Customer full name"
+ },
+ "email": {
+ "type": "string",
+ "format": "email",
+ "description": "Customer email address"
},
- "required": ["name"]
+ "phone": {
+ "type": "string",
+ "description": "Customer phone number"
+ }
},
- "issue": {
- "type": "object",
- "properties": {
- "description": {
- "type": "string",
- "description": "Description of the customer issue"
- },
- "category": {
- "type": "string",
- "enum": ["billing", "technical", "general", "complaint"],
- "description": "Issue category"
- },
- "priority": {
- "type": "string",
- "enum": ["low", "medium", "high", "urgent"],
- "description": "Issue priority level"
- }
+ "required": ["name"]
+ },
+ "issue": {
+ "type": "object",
+ "properties": {
+ "description": {
+ "type": "string",
+ "description": "Description of the customer issue"
+ },
+ "category": {
+ "type": "string",
+ "enum": ["billing", "technical", "general", "complaint"],
+ "description": "Issue category"
},
- "required": ["description", "category"]
+ "priority": {
+ "type": "string",
+ "enum": ["low", "medium", "high", "urgent"],
+ "description": "Issue priority level"
+ }
},
- "followUp": {
- "type": "object",
- "properties": {
- "required": {
- "type": "boolean",
- "description": "Whether follow-up is needed"
- },
- "method": {
- "type": "string",
- "enum": ["email", "phone", "none"],
- "description": "Preferred follow-up method"
- },
- "notes": {
- "type": "string",
- "description": "Additional notes for follow-up"
- }
+ "required": ["description", "category"]
+ },
+ "followUp": {
+ "type": "object",
+ "properties": {
+ "required": {
+ "type": "boolean",
+ "description": "Whether follow-up is needed"
+ },
+ "method": {
+ "type": "string",
+ "enum": ["email", "phone", "none"],
+ "description": "Preferred follow-up method"
+ },
+ "notes": {
+ "type": "string",
+ "description": "Additional notes for follow-up"
}
}
- },
- "required": ["customer", "issue"]
- }
+ }
+ },
+ "required": ["customer", "issue"]
}
)
-structured_output = response.json()
-print(f'Created structured output: {structured_output["id"]}')
+print(f'Created structured output: {structured_output.id}')
# Save this ID - you'll need it in the next step
```
-
+
+
-
-Save the returned `id` from the response - you'll need it to link to your assistant.
-
+
+In the API approach, you'll need to save the returned `id` to attach it to an assistant. In the Dashboard, you can attach it directly when creating the structured output.
+
-## Step 2: Create an assistant with structured outputs
+## Step 2: Create and test a call
-Now create an assistant that uses your structured output:
+Now test your structured output by making a call.
-
-```bash title="cURL"
-curl -X POST https://api.vapi.ai/assistant \
- -H "Authorization: Bearer $VAPI_API_KEY" \
- -H "Content-Type: application/json" \
- -d '{
- "name": "Customer Support Agent",
- "firstMessage": "Hello! I'\''m here to help you with your support request. Can you please tell me your name and describe the issue you'\''re experiencing?",
- "model": {
- "provider": "openai",
- "model": "gpt-4-turbo-preview",
- "messages": [
- {
- "role": "system",
- "content": "You are a helpful customer support agent. Gather the customer'\''s information and understand their issue. Be empathetic and professional."
- }
- ]
- },
- "voice": {
- "provider": "vapi",
- "voiceId": "jennifer"
- },
- "artifactPlan": {
- "structuredOutputIds": ["YOUR_STRUCTURED_OUTPUT_ID_HERE"]
- }
- }'
-```
+
+**Prerequisites**: You need an assistant already created with:
+- The structured output from Step 1 attached in `artifactPlan.structuredOutputIds`
+- A model and voice configured
+- System prompt appropriate for your use case
-```javascript title="Node.js"
-const assistant = await fetch('https://api.vapi.ai/assistant', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${process.env.VAPI_API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- name: "Customer Support Agent",
- firstMessage: "Hello! I'm here to help you with your support request. Can you please tell me your name and describe the issue you're experiencing?",
- model: {
- provider: "openai",
- model: "gpt-4-turbo-preview",
- messages: [
- {
- role: "system",
- content: "You are a helpful customer support agent. Gather the customer's information and understand their issue. Be empathetic and professional."
- }
- ]
- },
- voice: {
- provider: "vapi",
- voiceId: "jennifer"
- },
- artifactPlan: {
- structuredOutputIds: [structuredOutput.id] // Use the ID from step 1
- }
- })
-}).then(res => res.json());
+You can create an assistant via the Dashboard or API, then use its ID in the examples below.
+
-console.log('Created assistant:', assistant.id);
+
+
+
+
+ 1. Navigate to your assistant (from **Assistants** in the sidebar)
+ 2. Ensure your structured output is attached in the **Artifact Plan** section
+ 3. Click **Talk to Assistant** in the top right corner
+ 4. The assistant will start speaking
+
+
+
+ Try saying: "Hi, my name is John Smith. My email is john@example.com. I'm having trouble logging into my account - it keeps showing an error message. This is pretty urgent for me."
+
+
+
+ Click **End Call** when you're done testing
+
+
+
+
+
+ ```typescript
+import { VapiClient } from "@vapi-ai/server-sdk";
+
+const vapi = new VapiClient({ token: process.env.VAPI_API_KEY! });
+
+// Start a web call with your assistant (replace with your assistant ID)
+const call = await vapi.calls.create({
+ assistantId: "your-assistant-id", // Use an assistant with structured outputs attached
+ type: "webCall"
+});
+
+console.log('Call started:', call.id);
+console.log('Join URL:', call.webCallUrl);
+
+// For phone calls, use:
+// const call = await vapi.calls.create({
+// assistantId: "your-assistant-id",
+// type: "outboundPhoneCall",
+// phoneNumberId: "your-phone-number-id",
+// customer: {
+// number: "+1234567890"
+// }
+// });
```
+
-```python title="Python"
-assistant_response = requests.post(
- 'https://api.vapi.ai/assistant',
- headers={
- 'Authorization': f'Bearer {os.environ["VAPI_API_KEY"]}',
- 'Content-Type': 'application/json'
- },
- json={
- "name": "Customer Support Agent",
- "firstMessage": "Hello! I'm here to help you with your support request. Can you please tell me your name and describe the issue you're experiencing?",
- "model": {
- "provider": "openai",
- "model": "gpt-4-turbo-preview",
- "messages": [
- {
- "role": "system",
- "content": "You are a helpful customer support agent. Gather the customer's information and understand their issue. Be empathetic and professional."
- }
- ]
- },
- "voice": {
- "provider": "vapi",
- "voiceId": "jennifer"
- },
- "artifactPlan": {
- "structuredOutputIds": [structured_output["id"]] # Use the ID from step 1
- }
- }
-)
+
+ ```python
+from vapi import Vapi
+import os
-assistant = assistant_response.json()
-print(f'Created assistant: {assistant["id"]}')
-```
-
+vapi = Vapi(token=os.environ.get("VAPI_API_KEY"))
-## Step 3: Test with a phone call
+# Start a web call with your assistant (replace with your assistant ID)
+call = vapi.calls.create(
+ assistant_id="your-assistant-id", # Use an assistant with structured outputs attached
+ type="webCall"
+)
-Make a test call to your assistant:
+print(f'Call started: {call.id}')
+print(f'Join URL: {call.web_call_url}')
+
+# For phone calls, use:
+# call = vapi.calls.create(
+# assistant_id="your-assistant-id",
+# type="outboundPhoneCall",
+# phone_number_id="your-phone-number-id",
+# customer={
+# "number": "+1234567890"
+# }
+# )
+```
+
-
-```bash title="cURL"
+
+ ```bash
+# Start a web call
curl -X POST https://api.vapi.ai/call \
-H "Authorization: Bearer $VAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
- "assistantId": "YOUR_ASSISTANT_ID_HERE",
- "customer": {
- "number": "+1234567890"
- }
+ "assistantId": "your-assistant-id",
+ "type": "webCall"
}'
-```
-```javascript title="Node.js"
-const call = await fetch('https://api.vapi.ai/call', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${process.env.VAPI_API_KEY}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- assistantId: assistant.id,
- customer: {
- number: "+1234567890" // Replace with your phone number
- }
- })
-}).then(res => res.json());
-
-console.log('Call initiated:', call.id);
-```
-
-```python title="Python"
-call_response = requests.post(
- 'https://api.vapi.ai/call',
- headers={
- 'Authorization': f'Bearer {os.environ["VAPI_API_KEY"]}',
- 'Content-Type': 'application/json'
- },
- json={
- "assistantId": assistant["id"],
- "customer": {
- "number": "+1234567890" # Replace with your phone number
- }
- }
-)
-
-call = call_response.json()
-print(f'Call initiated: {call["id"]}')
+# For phone calls:
+# curl -X POST https://api.vapi.ai/call \
+# -H "Authorization: Bearer $VAPI_API_KEY" \
+# -H "Content-Type: application/json" \
+# -d '{
+# "assistantId": "your-assistant-id",
+# "type": "outboundPhoneCall",
+# "phoneNumberId": "your-phone-number-id",
+# "customer": {
+# "number": "+1234567890"
+# }
+# }'
```
-
+
+
During the call, try saying something like: "Hi, my name is John Smith. My email is john@example.com. I'm having trouble logging into my account - it keeps showing an error message. This is pretty urgent for me."
-## Step 4: Retrieve extracted data
+## Step 3: Retrieve extracted data
After the call ends, retrieve the extracted information:
-
-```bash title="cURL"
-curl -X GET "https://api.vapi.ai/call/YOUR_CALL_ID_HERE" \
- -H "Authorization: Bearer $VAPI_API_KEY"
-```
+
+
+
+
+ 1. Navigate to **Call Logs** in the left sidebar
+ 2. Click on your recent call to view details
+
+
+
+ 1. In the call details, find the **Structured Outputs** section
+ 2. View the extracted JSON data for your "Support Ticket" output
+ 3. The data will be displayed in a formatted JSON view showing each output with its ID, name, and result
+
+
+
+ ### How structured outputs appear in Call Logs
+
+ When you view a call in the Call Logs page, structured outputs are displayed in the following format:
+
+ ```json
+ {
+ "550e8400-e29b-41d4-a716-446655440001": {
+ "name": "Support Ticket",
+ "result": {
+ "customer": {
+ "name": "John Smith",
+ "email": "john@example.com",
+ "phone": "+1234567890"
+ },
+ "issue": {
+ "description": "Unable to login to account, receiving error message",
+ "category": "technical",
+ "priority": "urgent"
+ },
+ "followUp": {
+ "required": true,
+ "method": "email",
+ "notes": "Customer needs immediate assistance with login issue"
+ }
+ }
+ }
+ }
+ ```
+
+ **Structure explanation:**
+ - **Root level**: Contains output IDs (UUIDs) as keys
+ - **name**: The name of the structured output configuration
+ - **result**: The actual extracted data based on your schema
+ - For object schemas: Contains the nested structure with all extracted fields
+ - For boolean schemas: Contains `true` or `false`
+ - For string schemas: Contains the extracted text
+ - For number schemas: Contains the numeric value
+
+
+ If you have multiple structured outputs attached to an assistant, each will appear with its own UUID key in the structuredOutputs object.
+
+
+
+
+ ```typescript
+import { VapiClient } from "@vapi-ai/server-sdk";
+
+const vapi = new VapiClient({ token: process.env.VAPI_API_KEY! });
-```javascript title="Node.js"
// Wait a few seconds after call ends for processing
setTimeout(async () => {
- const callData = await fetch(`https://api.vapi.ai/call/${call.id}`, {
- headers: {
- 'Authorization': `Bearer ${process.env.VAPI_API_KEY}`
- }
- }).then(res => res.json());
+ const callData = await vapi.calls.get(call.id);
const outputs = callData.artifact?.structuredOutputs;
@@ -552,32 +585,41 @@ setTimeout(async () => {
}
}, 5000);
```
+
-```python title="Python"
+
+ ```python
+from vapi import Vapi
import time
import json
+import os
+
+vapi = Vapi(token=os.environ.get("VAPI_API_KEY"))
# Wait a few seconds after call ends for processing
time.sleep(5)
-call_data = requests.get(
- f'https://api.vapi.ai/call/{call["id"]}',
- headers={
- 'Authorization': f'Bearer {os.environ["VAPI_API_KEY"]}'
- }
-).json()
+call_data = vapi.calls.get(call.id)
-outputs = call_data.get('artifact', {}).get('structuredOutputs', {})
+outputs = call_data.artifact.get('structuredOutputs', {}) if call_data.artifact else {}
for output_id, data in outputs.items():
print('Extracted Support Ticket:')
print(json.dumps(data['result'], indent=2))
```
-
+
+
+
+ ```bash
+curl -X GET "https://api.vapi.ai/call/YOUR_CALL_ID_HERE" \
+ -H "Authorization: Bearer $VAPI_API_KEY"
+```
+
+
### Expected output
-You should see extracted data like this:
+The extracted data (the `result` field from the API response) will look like this:
```json
{
@@ -599,103 +641,9 @@ You should see extracted data like this:
}
```
-## Step 5: Set up webhook (optional)
-
-To automatically receive extracted data when calls end, set up a webhook:
-
-
-```javascript title="Express.js webhook handler"
-const express = require('express');
-const app = express();
-
-app.use(express.json());
-
-app.post('/vapi/webhook', (req, res) => {
- const { type, call } = req.body;
-
- if (type === 'call.ended') {
- const outputs = call.artifact?.structuredOutputs;
-
- if (outputs) {
- Object.entries(outputs).forEach(([outputId, data]) => {
- if (data.result) {
- // Process the extracted support ticket
- console.log('New support ticket:', data.result);
-
- // Example: Create ticket in your system
- createSupportTicket({
- customer: data.result.customer,
- issue: data.result.issue,
- priority: data.result.issue.priority,
- followUp: data.result.followUp
- });
- }
- });
- }
- }
-
- res.status(200).send('OK');
-});
-
-function createSupportTicket(ticketData) {
- // Your ticket creation logic here
- console.log('Creating ticket in system:', ticketData);
-}
-
-app.listen(3000, () => {
- console.log('Webhook server running on port 3000');
-});
-```
-
-```python title="Flask webhook handler"
-from flask import Flask, request, jsonify
-
-app = Flask(__name__)
-
-@app.route('/vapi/webhook', methods=['POST'])
-def vapi_webhook():
- data = request.json
-
- if data.get('type') == 'call.ended':
- call = data.get('call', {})
- outputs = call.get('artifact', {}).get('structuredOutputs', {})
-
- for output_id, output_data in outputs.items():
- if output_data.get('result'):
- # Process the extracted support ticket
- print('New support ticket:', output_data['result'])
-
- # Example: Create ticket in your system
- create_support_ticket({
- 'customer': output_data['result']['customer'],
- 'issue': output_data['result']['issue'],
- 'priority': output_data['result']['issue']['priority'],
- 'followUp': output_data['result']['followUp']
- })
-
- return jsonify({'status': 'ok'}), 200
-
-def create_support_ticket(ticket_data):
- # Your ticket creation logic here
- print('Creating ticket in system:', ticket_data)
-
-if __name__ == '__main__':
- app.run(port=3000)
-```
-
-
-Then update your assistant with the webhook URL:
-
-```bash
-curl -X PATCH "https://api.vapi.ai/assistant/YOUR_ASSISTANT_ID" \
- -H "Authorization: Bearer $VAPI_API_KEY" \
- -H "Content-Type: application/json" \
- -d '{
- "server": {
- "url": "https://your-domain.com/vapi/webhook"
- }
- }'
-```
+
+When accessing via API, this data is nested inside the structured output object at `call.artifact.structuredOutputs[outputId].result`. The Dashboard shows the complete structure including the output ID and name.
+
## Next steps
@@ -737,6 +685,49 @@ You can attach multiple structured outputs to extract different types of data:
The `structuredOutputIds` are UUIDs returned when you create each structured output configuration.
+### Example: Intelligent analysis with multiple outputs
+
+Structured outputs can perform sophisticated analysis beyond simple data extraction. Here's a real example showing various types of intelligent evaluation:
+
+```json
+{
+ "2ca00f20-f2c3-4d74-af2e-52842be5885c": {
+ "name": "informationOnFileIsCorrect",
+ "result": false
+ },
+ "4748e1aa-6c7a-49e6-bbde-c4365ef69c6e": {
+ "name": "Appointment Rescheduled",
+ "result": false
+ },
+ "4d4bac33-2cea-43d4-a3b3-4554932b8933": {
+ "name": "CSAT",
+ "result": 8
+ },
+ "7898e478-c8dc-4ff8-a3f6-4a46555a957f": {
+ "name": "Appointment Booked",
+ "result": true
+ },
+ "a0ca58b1-c343-4628-b088-bf53aabacab9": {
+ "name": "Call Summary",
+ "result": "The user called to schedule a consultation appointment for next week, specifically on Wednesday afternoon..."
+ },
+ "b5a390d8-87c5-4015-b1ad-ed237201bdf0": {
+ "name": "Success Evaluation - Pass/Fail",
+ "result": true
+ }
+}
+```
+
+This example demonstrates intelligent extraction capabilities:
+- **Call outcome evaluation**: `Appointment Booked` (true) - Analyzed if the call's objective was achieved
+- **Data verification**: `informationOnFileIsCorrect` (false) - Evaluated if customer data needed updates
+- **Success metrics**: `Success Evaluation - Pass/Fail` (true) - Determined overall call success based on multiple criteria
+- **CSAT extraction**: `CSAT` (8) - Extracted satisfaction score from customer feedback
+- **Intelligent summarization**: `Call Summary` - Generated contextual summary of the conversation
+- **Process tracking**: `Appointment Rescheduled` (false) - Tracked specific actions taken during the call
+
+Each output analyzes the complete call context including transcript, tool results, and metadata to provide actionable insights.
+
### Validation patterns
Common validation patterns for reliable extraction:
diff --git a/fern/calls/websocket-transport.mdx b/fern/calls/websocket-transport.mdx
index 115849fd1..7ac27279a 100644
--- a/fern/calls/websocket-transport.mdx
+++ b/fern/calls/websocket-transport.mdx
@@ -196,10 +196,12 @@ function hangupCall() {
## Ending the Call
-To gracefully end the WebSocket call:
+The recommended way to end a call is using [Live Call Control](/calls/call-features#end-call) which provides more control and proper cleanup.
+
+Alternatively, you can end the WebSocket call directly:
```javascript
-sendControlMessage({ type: "hangup" });
+sendControlMessage({ type: "end-call" });
socket.close();
```