Structured outputs

Extract structured data from conversations using AI-powered analysis

Overview

Structured outputs enable automatic extraction of specific information from voice conversations in a structured format. Define your data requirements using JSON Schema, and we will identify and extract that information from your calls.

Key benefits:

  • Extract customer information, appointments, and orders automatically
  • Validate data with JSON Schema constraints
  • Use any AI model for extraction (OpenAI, Anthropic, Google, Azure)
  • Reuse extraction definitions across multiple assistants

How it works

1

Define your schema

Create a JSON Schema that describes the data you want to extract

2

Create structured output

Use the API to create a reusable structured output definition

4

Extract from calls

Data is automatically extracted after each call and stored in call artifacts

Quick start

Create a structured output

1import { Vapi } from '@vapi-ai/server-sdk';
2
3const vapi = new Vapi({ apiKey: process.env.VAPI_API_KEY });
4
5const structuredOutput = await vapi.structuredOutputs.create({
6 name: "Customer Info",
7 type: "ai",
8 description: "Extract customer contact information",
9 schema: {
10 type: "object",
11 properties: {
12 firstName: {
13 type: "string",
14 description: "Customer's first name"
15 },
16 lastName: {
17 type: "string",
18 description: "Customer's last name"
19 },
20 email: {
21 type: "string",
22 format: "email",
23 description: "Customer's email address"
24 },
25 phone: {
26 type: "string",
27 pattern: "^\\+?[1-9]\\d{1,14}$",
28 description: "Phone number in E.164 format"
29 }
30 },
31 required: ["firstName", "lastName"]
32 }
33});
34
35console.log('Created structured output:', structuredOutput.id);

Add the structured output ID to your assistant’s configuration:

1const assistant = await vapi.assistants.create({
2 name: "Customer Support Agent",
3 // ... other assistant configuration
4 artifactPlan: {
5 structuredOutputIds: [structuredOutput.id]
6 }
7});

Access extracted data

After a call completes, retrieve the extracted data:

1const call = await vapi.calls.get(callId);
2
3// Access structured outputs from call artifacts
4const outputs = call.artifact?.structuredOutputs;
5
6if (outputs) {
7 for (const [outputId, data] of Object.entries(outputs)) {
8 console.log(`Output: ${data.name}`);
9 console.log(`Result:`, data.result);
10
11 // Handle the extracted data
12 if (data.result) {
13 // Process successful extraction
14 const { firstName, lastName, email, phone } = data.result;
15 // ... save to database, send notifications, etc.
16 }
17 }
18}

Schema types

Primitive types

Extract simple values directly:

1{
2 "type": "string",
3 "minLength": 1,
4 "maxLength": 100,
5 "pattern": "^[A-Z][a-z]+$"
6}

Object types

Extract structured data with multiple fields:

1{
2 "type": "object",
3 "properties": {
4 "name": {
5 "type": "string",
6 "description": "Full name"
7 },
8 "age": {
9 "type": "integer",
10 "minimum": 0,
11 "maximum": 120
12 },
13 "email": {
14 "type": "string",
15 "format": "email"
16 }
17 },
18 "required": ["name", "email"]
19}

Array types

Extract lists of items:

1{
2 "type": "array",
3 "items": {
4 "type": "object",
5 "properties": {
6 "product": {
7 "type": "string"
8 },
9 "quantity": {
10 "type": "integer",
11 "minimum": 1
12 }
13 }
14 },
15 "minItems": 1,
16 "maxItems": 10
17}

Nested structures

Extract complex hierarchical data:

1{
2 "type": "object",
3 "properties": {
4 "customer": {
5 "type": "object",
6 "properties": {
7 "name": {"type": "string"},
8 "contact": {
9 "type": "object",
10 "properties": {
11 "email": {"type": "string", "format": "email"},
12 "phone": {"type": "string"}
13 }
14 }
15 }
16 },
17 "order": {
18 "type": "object",
19 "properties": {
20 "items": {
21 "type": "array",
22 "items": {
23 "type": "object",
24 "properties": {
25 "sku": {"type": "string"},
26 "quantity": {"type": "integer"}
27 }
28 }
29 }
30 }
31 }
32 }
33}

Validation features

String formats

Vapi supports standard JSON Schema formats for validation:

FormatDescriptionExample
emailEmail addressesjohn@example.com
dateDate in YYYY-MM-DD2024-01-15
timeTime in HH:MM:SS14:30:00
date-timeISO 8601 datetime2024-01-15T14:30:00Z
uriValid URIhttps://example.com
uuidUUID format123e4567-e89b-12d3-a456-426614174000

Pattern matching

Use regular expressions for custom validation:

1{
2 "type": "string",
3 "pattern": "^[A-Z]{2}-\\d{6}$",
4 "description": "Order ID like US-123456"
5}

Conditional logic

Use if/then/else for conditional requirements:

1{
2 "type": "object",
3 "properties": {
4 "serviceType": {
5 "type": "string",
6 "enum": ["emergency", "scheduled"]
7 },
8 "appointmentTime": {
9 "type": "string",
10 "format": "date-time"
11 }
12 },
13 "if": {
14 "properties": {
15 "serviceType": {"const": "scheduled"}
16 }
17 },
18 "then": {
19 "required": ["appointmentTime"]
20 }
21}

Custom models

Configure which AI model performs the extraction:

1const structuredOutput = await vapi.structuredOutputs.create({
2 name: "Sentiment Analysis",
3 type: "ai",
4 schema: {
5 type: "object",
6 properties: {
7 sentiment: {
8 type: "string",
9 enum: ["positive", "negative", "neutral"]
10 },
11 confidence: {
12 type: "number",
13 minimum: 0,
14 maximum: 1
15 }
16 }
17 },
18 model: {
19 provider: "openai",
20 model: "gpt-4-turbo-preview",
21 temperature: 0.1,
22 messages: [
23 {
24 role: "system",
25 content: "You are an expert at analyzing customer sentiment. Be precise and consistent."
26 },
27 {
28 role: "user",
29 content: "Analyze the sentiment of this conversation:\n{{transcript}}"
30 }
31 ]
32 }
33});

Available variables

Use these variables in custom prompts:

  • {{transcript}} - Full conversation transcript
  • {{messages}} - Conversation messages array
  • {{callEndedReason}} - How the call ended
  • {{structuredOutput.name}} - Output name
  • {{structuredOutput.description}} - Output description
  • {{structuredOutput.schema}} - Schema definition

API reference

POST
/structured-output
1curl -X POST https://api.vapi.ai/structured-output \
2 -H "Authorization: Bearer <token>" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "name": "foo",
6 "schema": {
7 "type": "string"
8 }
9}'

Create structured output

name
stringRequired

Display name for the structured output (max 40 characters)

type
stringRequired

Must be set to “ai”

description
string

Description of what data to extract

schema
objectRequired

JSON Schema defining the structure of data to extract

assistantIds
array

Array of assistant IDs to link this output to

model
object

Custom model configuration for extraction

Update structured output

PATCH
/structured-output/:id
1curl -X PATCH "https://api.vapi.ai/structured-output/id?schemaOverride=schemaOverride" \
2 -H "Authorization: Bearer <token>" \
3 -H "Content-Type: application/json" \
4 -d '{}'

To update the top level schema type after creation, you must include ?schemaOverride=true as a query parameter in the URL

List structured outputs

GET
/structured-output
1curl https://api.vapi.ai/structured-output \
2 -H "Authorization: Bearer <token>"

Query parameters:

  • page - Page number (default: 1)
  • limit - Results per page (default: 20, max: 100)

Delete structured output

DELETE
/structured-output/:id
1curl -X DELETE https://api.vapi.ai/structured-output/id \
2 -H "Authorization: Bearer <token>"

Common use cases

Customer information collection

1{
2 "name": "Customer Profile",
3 "type": "ai",
4 "schema": {
5 "type": "object",
6 "properties": {
7 "name": {"type": "string"},
8 "email": {"type": "string", "format": "email"},
9 "phone": {"type": "string"},
10 "accountNumber": {"type": "string"},
11 "preferredContactMethod": {
12 "type": "string",
13 "enum": ["email", "phone", "sms"]
14 }
15 }
16 }
17}

Appointment scheduling

1{
2 "name": "Appointment Request",
3 "type": "ai",
4 "schema": {
5 "type": "object",
6 "properties": {
7 "preferredDate": {"type": "string", "format": "date"},
8 "preferredTime": {"type": "string", "format": "time"},
9 "duration": {"type": "integer", "enum": [15, 30, 45, 60]},
10 "serviceType": {
11 "type": "string",
12 "enum": ["consultation", "follow-up", "procedure"]
13 },
14 "notes": {"type": "string"}
15 },
16 "required": ["preferredDate", "preferredTime", "serviceType"]
17 }
18}

Order processing

1{
2 "name": "Order Details",
3 "type": "ai",
4 "schema": {
5 "type": "object",
6 "properties": {
7 "items": {
8 "type": "array",
9 "items": {
10 "type": "object",
11 "properties": {
12 "product": {"type": "string"},
13 "quantity": {"type": "integer", "minimum": 1},
14 "specialInstructions": {"type": "string"}
15 },
16 "required": ["product", "quantity"]
17 }
18 },
19 "deliveryAddress": {
20 "type": "object",
21 "properties": {
22 "street": {"type": "string"},
23 "city": {"type": "string"},
24 "zipCode": {"type": "string", "pattern": "^\\d{5}$"}
25 }
26 },
27 "deliveryInstructions": {"type": "string"}
28 }
29 }
30}

Lead qualification

1{
2 "name": "Lead Information",
3 "type": "ai",
4 "schema": {
5 "type": "object",
6 "properties": {
7 "company": {"type": "string"},
8 "role": {"type": "string"},
9 "budget": {
10 "type": "string",
11 "enum": ["< $10k", "$10k-50k", "$50k-100k", "> $100k"]
12 },
13 "timeline": {
14 "type": "string",
15 "enum": ["immediate", "1-3 months", "3-6 months", "6+ months"]
16 },
17 "painPoints": {
18 "type": "array",
19 "items": {"type": "string"}
20 },
21 "nextSteps": {"type": "string"}
22 }
23 }
24}

Best practices

Start simple

Begin with basic schemas and add complexity as needed. Test with real conversations before adding advanced features.

Use descriptive names

Help the AI understand what to extract by using clear field names and descriptions in your schema.

Set appropriate constraints

Balance flexibility with validation. Too strict and extraction may fail; too loose and data quality suffers.

Handle optional fields

Only mark fields as required if they’re truly essential. Use optional fields for information that might not be mentioned.

Performance tips

  • Keep schemas focused: Extract only what you need to minimize processing time
  • Use appropriate models: GPT-4 for complex schemas, GPT-3.5 for simple ones
  • Set low temperature: Use 0.1 or lower for consistent extraction
  • Monitor success rates: Track extraction failures and adjust schemas accordingly

Error handling

Always check for null results which indicate extraction failure:

1if (data.result === null) {
2 console.log(`Extraction failed for ${data.name}`);
3 // Implement fallback logic
4}

Troubleshooting

No data extracted

1

Verify schema validity

Ensure your JSON Schema is valid and properly formatted

2

Check conversation content

Confirm the required information was actually mentioned

3

Review assistant configuration

Verify the structured output ID is linked to your assistant

4

Test with simpler schema

Try a basic schema to isolate the issue

Incorrect extraction

  • Add more descriptive field descriptions
  • Provide examples in custom prompts
  • Use stricter validation patterns
  • Lower the model temperature

Partial extraction

  • Make fields optional if they might not be mentioned
  • Verify data types match expected values

HIPAA compliance

Important for HIPAA-enabled organizations:

If your organization has HIPAA compliance enabled (hipaaEnabled: true), structured outputs are disabled by default to protect PHI (Protected Health Information).

To use structured outputs with HIPAA compliance:

  • Contact the Vapi team to enable structured outputs
  • Ensure you understand the implications for PHI handling
  • Follow all HIPAA compliance best practices when extracting sensitive health data

Limitations

  • Schema updates require ?schemaOverride=true parameter
  • Extraction occurs after call completion (not real-time)
  • Name field limited to 40 characters