diff --git a/fern/assistants/structured-outputs-examples.mdx b/fern/assistants/structured-outputs-examples.mdx new file mode 100644 index 000000000..26560fc24 --- /dev/null +++ b/fern/assistants/structured-outputs-examples.mdx @@ -0,0 +1,1233 @@ +--- +title: Structured outputs examples +subtitle: Real-world examples and templates for common use cases +slug: assistants/structured-outputs-examples +--- + +## Overview + +This page provides production-ready examples of structured outputs for common business scenarios. Each example includes the complete schema, configuration, and integration code. + +## Healthcare appointment booking + +Extract patient information and appointment preferences from healthcare calls. + + +```json title="Schema" +{ + "name": "Healthcare Appointment", + "type": "ai", + "description": "Extract patient and appointment information for medical scheduling", + "schema": { + "type": "object", + "properties": { + "patient": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "description": "Patient's first name" + }, + "lastName": { + "type": "string", + "description": "Patient's last name" + }, + "dateOfBirth": { + "type": "string", + "format": "date", + "description": "Patient's date of birth (YYYY-MM-DD)" + }, + "phoneNumber": { + "type": "string", + "pattern": "^\\+?[1-9]\\d{1,14}$", + "description": "Patient's contact number" + }, + "insuranceProvider": { + "type": "string", + "description": "Insurance provider name" + }, + "memberID": { + "type": "string", + "description": "Insurance member ID" + } + }, + "required": ["firstName", "lastName", "phoneNumber"] + }, + "appointment": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["new-patient", "follow-up", "annual-checkup", "urgent-care", "specialist"], + "description": "Type of appointment" + }, + "department": { + "type": "string", + "enum": ["general", "cardiology", "dermatology", "orthopedics", "pediatrics", "obgyn"], + "description": "Medical department" + }, + "preferredDates": { + "type": "array", + "items": { + "type": "string", + "format": "date" + }, + "maxItems": 3, + "description": "Up to 3 preferred appointment dates" + }, + "preferredTimeSlot": { + "type": "string", + "enum": ["morning", "afternoon", "evening", "any"], + "description": "Preferred time of day" + }, + "symptoms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of symptoms or reasons for visit" + }, + "urgency": { + "type": "string", + "enum": ["routine", "soon", "urgent"], + "description": "How urgent is the appointment" + } + }, + "required": ["type", "department", "urgency"] + }, + "additionalNotes": { + "type": "string", + "description": "Any additional notes or special requirements" + } + }, + "required": ["patient", "appointment"] + } +} +``` + +```json title="Example Output" +{ + "patient": { + "firstName": "Sarah", + "lastName": "Johnson", + "dateOfBirth": "1985-03-15", + "phoneNumber": "+14155551234", + "insuranceProvider": "Blue Cross Blue Shield", + "memberID": "BCB123456789" + }, + "appointment": { + "type": "follow-up", + "department": "cardiology", + "preferredDates": ["2024-01-15", "2024-01-16", "2024-01-17"], + "preferredTimeSlot": "morning", + "symptoms": ["chest pain", "shortness of breath"], + "urgency": "urgent" + }, + "additionalNotes": "Patient prefers female doctor if available" +} +``` + + +## E-commerce order processing + +Capture order details, shipping information, and payment preferences. + + +```json title="Schema" +{ + "name": "E-commerce Order", + "type": "ai", + "description": "Extract complete order information from sales calls", + "schema": { + "type": "object", + "properties": { + "customer": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Customer full name" + }, + "email": { + "type": "string", + "format": "email", + "description": "Customer email for order confirmation" + }, + "phone": { + "type": "string", + "description": "Contact number" + }, + "loyaltyNumber": { + "type": "string", + "description": "Loyalty program member number if mentioned" + } + }, + "required": ["name", "email"] + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "productName": { + "type": "string", + "description": "Name or description of the product" + }, + "sku": { + "type": "string", + "description": "Product SKU if mentioned" + }, + "quantity": { + "type": "integer", + "minimum": 1, + "description": "Quantity ordered" + }, + "size": { + "type": "string", + "enum": ["XS", "S", "M", "L", "XL", "XXL", "custom"], + "description": "Size if applicable" + }, + "color": { + "type": "string", + "description": "Color preference" + }, + "customization": { + "type": "string", + "description": "Any customization requests" + } + }, + "required": ["productName", "quantity"] + }, + "minItems": 1, + "description": "List of items being ordered" + }, + "shipping": { + "type": "object", + "properties": { + "method": { + "type": "string", + "enum": ["standard", "express", "overnight", "pickup"], + "description": "Shipping method" + }, + "address": { + "type": "object", + "properties": { + "street": { + "type": "string" + }, + "apartment": { + "type": "string", + "description": "Apartment or suite number" + }, + "city": { + "type": "string" + }, + "state": { + "type": "string", + "pattern": "^[A-Z]{2}$" + }, + "zipCode": { + "type": "string", + "pattern": "^\\d{5}(-\\d{4})?$" + }, + "country": { + "type": "string", + "default": "USA" + } + }, + "required": ["street", "city", "state", "zipCode"] + }, + "instructions": { + "type": "string", + "description": "Special delivery instructions" + }, + "giftWrap": { + "type": "boolean", + "description": "Whether gift wrapping was requested" + }, + "giftMessage": { + "type": "string", + "description": "Gift message if applicable" + } + }, + "required": ["method", "address"] + }, + "payment": { + "type": "object", + "properties": { + "method": { + "type": "string", + "enum": ["credit_card", "debit_card", "paypal", "apple_pay", "google_pay", "invoice"], + "description": "Payment method" + }, + "cardLastFour": { + "type": "string", + "pattern": "^\\d{4}$", + "description": "Last 4 digits of card if provided" + }, + "billingAddressSameAsShipping": { + "type": "boolean", + "description": "Whether billing address is same as shipping" + } + }, + "required": ["method"] + }, + "promotions": { + "type": "object", + "properties": { + "promoCode": { + "type": "string", + "description": "Promotional code mentioned" + }, + "referralSource": { + "type": "string", + "description": "How customer heard about us" + } + } + }, + "specialRequests": { + "type": "string", + "description": "Any special requests or notes" + } + }, + "required": ["customer", "items", "shipping"] + } +} +``` + +```json title="Example Output" +{ + "customer": { + "name": "Michael Chen", + "email": "michael.chen@example.com", + "phone": "+14155552468", + "loyaltyNumber": "LOYAL123456" + }, + "items": [ + { + "productName": "Wireless Headphones XL", + "quantity": 1, + "size": null, + "color": "Black", + "price": 149.99 + }, + { + "productName": "USB-C Cable 6ft", + "quantity": 2, + "size": null, + "color": null, + "price": 19.99 + } + ], + "shipping": { + "address": { + "street": "123 Market Street", + "city": "San Francisco", + "state": "CA", + "zip": "94102", + "country": "USA" + }, + "method": "express", + "priority": "standard", + "giftWrap": false + }, + "payment": { + "method": "credit-card", + "lastFourDigits": "4242" + }, + "promotions": { + "promoCode": "SAVE20", + "giftWrap": false + }, + "specialInstructions": "Please leave package with doorman" +} +``` + + +## Real estate lead qualification + +Qualify real estate leads and capture property preferences. + + +```json title="Schema" +{ + "name": "Real Estate Lead", + "type": "ai", + "description": "Qualify real estate leads and extract property preferences", + "schema": { + "type": "object", + "properties": { + "contact": { + "type": "object", + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "phone": { + "type": "string" + }, + "preferredContactMethod": { + "type": "string", + "enum": ["phone", "email", "text", "any"] + }, + "bestTimeToContact": { + "type": "string", + "enum": ["morning", "afternoon", "evening", "weekends", "any"] + } + }, + "required": ["firstName", "phone"] + }, + "propertySearch": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["buy", "rent", "sell", "invest"] + }, + "propertyType": { + "type": "array", + "items": { + "type": "string", + "enum": ["single-family", "condo", "townhouse", "multi-family", "commercial", "land"] + } + }, + "locations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "area": { + "type": "string", + "description": "Neighborhood, city, or region" + }, + "schools": { + "type": "boolean", + "description": "Important to be near good schools" + }, + "commute": { + "type": "string", + "description": "Commute requirements mentioned" + } + } + } + }, + "budget": { + "type": "object", + "properties": { + "min": { + "type": "number", + "minimum": 0 + }, + "max": { + "type": "number" + }, + "preApproved": { + "type": "boolean", + "description": "Whether they have mortgage pre-approval" + } + } + }, + "features": { + "type": "object", + "properties": { + "bedrooms": { + "type": "integer", + "minimum": 0 + }, + "bathrooms": { + "type": "number", + "minimum": 0 + }, + "squareFeet": { + "type": "integer", + "minimum": 0 + }, + "garage": { + "type": "boolean" + }, + "pool": { + "type": "boolean" + }, + "yard": { + "type": "boolean" + }, + "mustHaves": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of must-have features" + }, + "dealBreakers": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of deal-breakers" + } + } + } + }, + "required": ["type", "propertyType", "budget"] + }, + "timeline": { + "type": "object", + "properties": { + "urgency": { + "type": "string", + "enum": ["immediate", "1-3months", "3-6months", "6-12months", "browsing"] + }, + "moveInDate": { + "type": "string", + "format": "date", + "description": "Target move-in date if mentioned" + }, + "reason": { + "type": "string", + "description": "Reason for moving/buying" + } + } + }, + "currentSituation": { + "type": "object", + "properties": { + "currentlyOwns": { + "type": "boolean", + "description": "Whether they currently own property" + }, + "needToSell": { + "type": "boolean", + "description": "Need to sell before buying" + }, + "firstTimeBuyer": { + "type": "boolean" + } + } + }, + "leadScore": { + "type": "object", + "properties": { + "motivation": { + "type": "string", + "enum": ["high", "medium", "low"], + "description": "Buyer/seller motivation level" + }, + "qualified": { + "type": "boolean", + "description": "Whether lead seems qualified" + }, + "followUpPriority": { + "type": "string", + "enum": ["hot", "warm", "cold"], + "description": "Follow-up priority" + } + } + } + }, + "required": ["contact", "propertySearch", "timeline"] + } +} +``` + +```json title="Example Output" +{ + "contact": { + "name": "Jennifer Martinez", + "email": "jmartinez@example.com", + "phone": "+14085551234", + "preferredContactMethod": "email" + }, + "propertySearch": { + "propertyType": "single-family", + "locations": [ + { + "area": "Palo Alto", + "importance": "high" + }, + { + "area": "Mountain View", + "importance": "medium" + } + ], + "budget": { + "min": 1500000, + "max": 2200000, + "preApproved": true + }, + "features": { + "bedrooms": 4, + "bathrooms": 3, + "squareFeet": 2500, + "mustHaves": ["garage", "backyard", "good schools"], + "niceToHaves": ["pool", "home office"] + } + }, + "timeline": { + "urgency": "3-6-months", + "moveInDate": "2024-06-01", + "reasonForMove": "job relocation" + }, + "currentSituation": { + "currentlyOwns": false, + "renting": true, + "firstTimeBuyer": false, + "needToSell": false + }, + "leadScore": { + "motivation": "high", + "financialReadiness": "qualified", + "decisionTimeframe": "actively-looking", + "followUpPriority": "warm" + } +} +``` + + +## Insurance claim intake + +Capture insurance claim details and incident information. + + +```json title="Schema" +{ + "name": "Insurance Claim", + "type": "ai", + "description": "Extract insurance claim information and incident details", + "schema": { + "type": "object", + "properties": { + "policyholder": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "policyNumber": { + "type": "string", + "description": "Insurance policy number" + }, + "dateOfBirth": { + "type": "string", + "format": "date" + }, + "contactPhone": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "required": ["name", "policyNumber"] + }, + "incident": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["auto", "property", "theft", "injury", "liability", "other"] + }, + "date": { + "type": "string", + "format": "date", + "description": "Date of incident" + }, + "time": { + "type": "string", + "format": "time", + "description": "Approximate time of incident" + }, + "location": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "city": { + "type": "string" + }, + "state": { + "type": "string" + }, + "zipCode": { + "type": "string" + } + } + }, + "description": { + "type": "string", + "description": "Detailed description of what happened" + }, + "policeReportFiled": { + "type": "boolean" + }, + "policeReportNumber": { + "type": "string", + "description": "Police report number if available" + } + }, + "required": ["type", "date", "description"] + }, + "damages": { + "type": "object", + "properties": { + "propertyDamage": { + "type": "array", + "items": { + "type": "object", + "properties": { + "item": { + "type": "string", + "description": "Damaged item or property" + }, + "estimatedValue": { + "type": "number", + "description": "Estimated value or repair cost" + }, + "description": { + "type": "string", + "description": "Description of damage" + } + } + } + }, + "injuries": { + "type": "array", + "items": { + "type": "object", + "properties": { + "person": { + "type": "string", + "description": "Injured person's name" + }, + "relationship": { + "type": "string", + "enum": ["self", "family", "passenger", "pedestrian", "other-driver", "other"], + "description": "Relationship to policyholder" + }, + "injuryDescription": { + "type": "string" + }, + "medicalTreatment": { + "type": "boolean", + "description": "Whether medical treatment was received" + }, + "hospital": { + "type": "string", + "description": "Hospital or clinic name if treated" + } + } + } + }, + "estimatedTotalLoss": { + "type": "number", + "description": "Total estimated loss amount" + } + } + }, + "otherParties": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "role": { + "type": "string", + "enum": ["other-driver", "witness", "property-owner", "passenger"], + "description": "Role in incident" + }, + "contactInfo": { + "type": "string", + "description": "Phone or email" + }, + "insuranceCompany": { + "type": "string", + "description": "Their insurance company if known" + }, + "policyNumber": { + "type": "string", + "description": "Their policy number if known" + } + } + } + }, + "documentation": { + "type": "object", + "properties": { + "photosAvailable": { + "type": "boolean" + }, + "receiptsAvailable": { + "type": "boolean" + }, + "witnessStatements": { + "type": "boolean" + } + } + }, + "urgency": { + "type": "string", + "enum": ["emergency", "urgent", "standard"], + "description": "Claim urgency level" + } + }, + "required": ["policyholder", "incident"] + } +} +``` + +```json title="Example Output" +{ + "policyholder": { + "name": "Robert Thompson", + "policyNumber": "POL-2024-789456", + "dateOfBirth": "1975-08-22", + "email": "rthompson@example.com", + "phone": "+15105551234" + }, + "incident": { + "type": "auto", + "date": "2024-01-10", + "time": "14:30", + "location": { + "street": "Highway 101 North", + "city": "San Jose", + "state": "CA", + "zip": "95110" + }, + "description": "Rear-ended at traffic light, other driver at fault", + "policeReportNumber": "SJ-2024-001234", + "otherPartyInvolved": true + }, + "damages": { + "vehicleDamage": { + "description": "Rear bumper and trunk damage", + "driveable": true, + "airbagDeployed": false + }, + "injuries": [ + { + "person": "policyholder", + "type": "whiplash", + "medicalTreatment": true, + "hospital": "Valley Medical Center" + } + ], + "estimatedTotalLoss": 8500, + "propertyDamage": null + }, + "witnesses": [ + { + "name": "Maria Garcia", + "phone": "+14085559876", + "statement": "Saw the other car hit from behind at red light" + } + ], + "urgency": "standard", + "additionalInfo": "Other driver admitted fault, have dashcam footage available" +} +``` + + +## Financial services application + +Process loan or credit applications with financial information. + + +```json title="Schema" +{ + "name": "Financial Application", + "type": "ai", + "description": "Extract loan or credit application information", + "schema": { + "type": "object", + "properties": { + "applicant": { + "type": "object", + "properties": { + "personalInfo": { + "type": "object", + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "ssn": { + "type": "string", + "pattern": "^\\d{3}-\\d{2}-\\d{4}$", + "description": "Social Security Number (XXX-XX-XXXX)" + }, + "dateOfBirth": { + "type": "string", + "format": "date" + }, + "email": { + "type": "string", + "format": "email" + }, + "phone": { + "type": "string" + }, + "currentAddress": { + "type": "object", + "properties": { + "street": { + "type": "string" + }, + "city": { + "type": "string" + }, + "state": { + "type": "string" + }, + "zipCode": { + "type": "string" + }, + "yearsAtAddress": { + "type": "number" + }, + "rentOrOwn": { + "type": "string", + "enum": ["rent", "own", "other"] + } + } + } + }, + "required": ["firstName", "lastName", "dateOfBirth"] + }, + "employment": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": ["employed", "self-employed", "unemployed", "retired", "student"] + }, + "employer": { + "type": "string", + "description": "Employer name" + }, + "position": { + "type": "string", + "description": "Job title" + }, + "yearsEmployed": { + "type": "number" + }, + "annualIncome": { + "type": "number", + "minimum": 0, + "description": "Annual gross income" + }, + "otherIncome": { + "type": "number", + "description": "Other income sources" + }, + "incomeVerifiable": { + "type": "boolean", + "description": "Can provide income verification" + } + }, + "required": ["status", "annualIncome"] + }, + "financial": { + "type": "object", + "properties": { + "creditScore": { + "type": "integer", + "minimum": 300, + "maximum": 850, + "description": "Self-reported credit score" + }, + "monthlyDebt": { + "type": "number", + "description": "Total monthly debt payments" + }, + "bankruptcyHistory": { + "type": "boolean", + "description": "Any bankruptcy in past 7 years" + }, + "existingAccounts": { + "type": "array", + "items": { + "type": "string", + "enum": ["checking", "savings", "credit-card", "mortgage", "auto-loan", "student-loan"] + }, + "description": "Existing accounts with institution" + } + } + } + } + }, + "loanDetails": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["personal", "auto", "mortgage", "home-equity", "business", "student"], + "description": "Type of loan" + }, + "amount": { + "type": "number", + "minimum": 0, + "description": "Requested loan amount" + }, + "purpose": { + "type": "string", + "description": "Purpose of the loan" + }, + "term": { + "type": "integer", + "description": "Desired loan term in months" + }, + "collateral": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["vehicle", "property", "savings", "none"], + "description": "Type of collateral" + }, + "value": { + "type": "number", + "description": "Estimated value of collateral" + }, + "description": { + "type": "string", + "description": "Description of collateral" + } + } + } + }, + "required": ["type", "amount", "purpose"] + }, + "coApplicant": { + "type": "object", + "properties": { + "hasCoApplicant": { + "type": "boolean" + }, + "relationship": { + "type": "string", + "enum": ["spouse", "partner", "family", "business-partner", "other"] + }, + "name": { + "type": "string" + }, + "income": { + "type": "number" + } + } + }, + "preferences": { + "type": "object", + "properties": { + "preferredRate": { + "type": "string", + "enum": ["fixed", "variable", "no-preference"] + }, + "automaticPayment": { + "type": "boolean", + "description": "Interested in automatic payment" + }, + "paperless": { + "type": "boolean", + "description": "Prefers paperless statements" + } + } + } + }, + "required": ["applicant", "loanDetails"] + } +} +``` + +```json title="Example Output" +{ + "applicant": { + "personalInfo": { + "firstName": "David", + "lastName": "Kim", + "dateOfBirth": "1988-11-15", + "ssn": "***-**-6789", + "email": "dkim@example.com", + "phone": "+12065551234", + "currentAddress": { + "street": "456 Pine Street", + "city": "Seattle", + "state": "WA", + "zip": "98101", + "yearsAtAddress": 3 + } + }, + "employment": { + "status": "full-time", + "employerName": "Tech Corp", + "jobTitle": "Senior Engineer", + "yearsEmployed": 5, + "annualIncome": 150000, + "otherIncome": 12000, + "incomeVerifiable": true + }, + "financial": { + "creditScore": 750, + "monthlyDebt": 2500, + "bankruptcyHistory": false, + "existingAccounts": ["checking", "savings", "credit-card"], + "accountNumbers": ["****1234", "****5678"] + } + }, + "loanDetails": { + "type": "mortgage", + "amount": 450000, + "term": 30, + "purpose": "home-purchase", + "propertyAddress": { + "street": "789 Oak Avenue", + "city": "Bellevue", + "state": "WA", + "zip": "98004" + }, + "propertyValue": 550000, + "downPayment": 100000, + "collateral": { + "type": "real-estate", + "value": 550000, + "description": "Single family home" + } + }, + "coApplicant": { + "hasCoApplicant": true, + "relationship": "spouse", + "name": "Sarah Kim", + "income": 85000 + }, + "preferences": { + "preferredRate": "fixed", + "preferredPaymentDate": 1, + "autopayInterest": true + }, + "additionalInfo": "Looking to close within 45 days, have pre-approval from another lender" +} +``` + + +## Best practices for complex schemas + + + + Break complex schemas into reusable object definitions for maintainability + + + + Start with essential fields as required, make detailed fields optional + + + + Add descriptions to every field to help AI understand context + + + + Use constraints for data quality but avoid being too restrictive + + + +## Testing recommendations + +### Test scenarios + +Always test your structured outputs with these scenarios: + +1. **Complete information** - All fields mentioned clearly +2. **Partial information** - Some required fields missing +3. **Ambiguous data** - Unclear or conflicting information +4. **Edge cases** - Boundary values, special characters +5. **Real conversations** - Actual call recordings or transcripts + +### Monitoring checklist + +Track these metrics for production deployments: + +- Extraction success rate per field +- Average extraction time +- Token usage and costs +- Schema validation failures +- Most commonly missing fields + +## Output data structure + +### Webhook payload format + +When structured outputs are extracted, they're delivered in this format: + +```json +{ + "type": "call.ended", + "call": { + "id": "call_abc123", + "artifact": { + "structuredOutputs": { + "550e8400-e29b-41d4-a716-446655440001": { + "name": "Customer Support Ticket", + "result": { + "customer": { + "name": "John Smith", + "email": "john@example.com" + }, + "issue": { + "description": "Login issues", + "priority": "high" + } + } + }, + "550e8400-e29b-41d4-a716-446655440002": { + "name": "Satisfaction Score", + "result": { + "score": 8, + "feedback": "Very helpful agent" + } + } + } + } + } +} +``` + +### API response format + +When retrieving call data via API: + +```json +{ + "id": "call_abc123", + "status": "ended", + "endedAt": "2024-01-10T15:30:00Z", + "artifact": { + "structuredOutputs": { + "outputId1": { + "name": "Output Name", + "result": { + // Your extracted data here + } + } + } + } +} +``` + +## Related resources + +- [Structured outputs overview](/assistants/structured-outputs) - Main documentation +- [Quickstart guide](/assistants/structured-outputs-quickstart) - Get started quickly +- [API reference](/api-reference#structured-output) - Complete API documentation +- [JSON Schema specification](https://json-schema.org/) - JSON Schema standard \ No newline at end of file diff --git a/fern/assistants/structured-outputs-quickstart.mdx b/fern/assistants/structured-outputs-quickstart.mdx new file mode 100644 index 000000000..30ae147d1 --- /dev/null +++ b/fern/assistants/structured-outputs-quickstart.mdx @@ -0,0 +1,804 @@ +--- +title: Structured outputs quickstart +subtitle: Get started with structured data extraction in 5 minutes +slug: assistants/structured-outputs-quickstart +--- + +## Overview + +This quickstart guide will help you set up structured outputs to automatically extract customer information from phone calls. In just a few minutes, you'll create a structured output, link it to an assistant, and test data extraction. + +### What are structured outputs? + +Structured outputs are AI-powered data extraction templates that automatically capture and organize information from conversations. 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 + +### 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 +- **Available via** - Call artifacts in the API response or webhook events + +### Why use structured outputs? + +- **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 +- **Improve analytics** - Structured data is easier to analyze and report on + +## What you'll build + +A customer support assistant that automatically extracts: +- Customer name and contact details +- Issue description and priority +- Requested follow-up actions + +## Prerequisites + + + + Sign up at [dashboard.vapi.ai](https://dashboard.vapi.ai) + + + Get your API key from the Dashboard settings + + + +## 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) + + + + 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": { + "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"] + } + }, + "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/). + + +```bash title="cURL" +curl -X POST https://api.vapi.ai/structured-output \ + -H "Authorization: Bearer $VAPI_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "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" + } + }, + "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" + }, + "priority": { + "type": "string", + "enum": ["low", "medium", "high", "urgent"], + "description": "Issue 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": ["customer", "issue"] + } + }' +``` + +```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" + } + }, + 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" + }, + priority: { + type: "string", + enum: ["low", "medium", "high", "urgent"], + description: "Issue 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: ["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 +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" + } + }, + "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" + }, + "priority": { + "type": "string", + "enum": ["low", "medium", "high", "urgent"], + "description": "Issue 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": ["customer", "issue"] + } + } +) + +structured_output = response.json() +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. + + +## Step 2: Create an assistant with structured outputs + +Now create an assistant that uses your structured output: + + +```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"] + } + }' +``` + +```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()); + +console.log('Created assistant:', assistant.id); +``` + +```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 + } + } +) + +assistant = assistant_response.json() +print(f'Created assistant: {assistant["id"]}') +``` + + +## Step 3: Test with a phone call + +Make a test call to your assistant: + + +```bash title="cURL" +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" + } + }' +``` + +```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"]}') +``` + + + +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 + +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" +``` + +```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 outputs = callData.artifact?.structuredOutputs; + + if (outputs) { + Object.entries(outputs).forEach(([outputId, data]) => { + console.log('Extracted Support Ticket:'); + console.log(JSON.stringify(data.result, null, 2)); + }); + } +}, 5000); +``` + +```python title="Python" +import time +import json + +# 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() + +outputs = call_data.get('artifact', {}).get('structuredOutputs', {}) + +for output_id, data in outputs.items(): + print('Extracted Support Ticket:') + print(json.dumps(data['result'], indent=2)) +``` + + +### Expected output + +You should see extracted data like this: + +```json +{ + "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" + } +} +``` + +## 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" + } + }' +``` + +## Next steps + + + + Learn about different data types and validation options + + + + Configure different AI models for extraction + + + + See complex real-world extraction scenarios + + + + Complete API documentation for structured outputs + + + +## Common patterns + +### Multiple extractions + +You can attach multiple structured outputs to extract different types of data: + +```javascript +{ + artifactPlan: { + structuredOutputIds: [ + "550e8400-e29b-41d4-a716-446655440001", // Customer details extraction + "550e8400-e29b-41d4-a716-446655440002", // Appointment requests extraction + "550e8400-e29b-41d4-a716-446655440003" // Satisfaction feedback extraction + ] + } +} +``` + +The `structuredOutputIds` are UUIDs returned when you create each structured output configuration. + +### Conditional extraction + +Use conditional logic in your schema to handle different scenarios: + +```json +{ + "if": { + "properties": { + "requestType": {"const": "appointment"} + } + }, + "then": { + "required": ["preferredDate", "preferredTime"] + } +} +``` + +### Validation patterns + +Common validation patterns for reliable extraction: + +```json +{ + "email": { + "type": "string", + "format": "email", + "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" + }, + "phone": { + "type": "string", + "pattern": "^\\+?[1-9]\\d{1,14}$" + }, + "zipCode": { + "type": "string", + "pattern": "^\\d{5}(-\\d{4})?$" + } +} +``` + +## Tips for success + + +**Best practices for reliable extraction:** +- Start with required fields only for critical data +- Use enums for categorical data to ensure consistency +- Add descriptions to help the AI understand context +- Test with real conversations before production use +- Monitor extraction success rates and iterate on schemas + + +## Troubleshooting + +| Issue | Solution | +|-------|----------| +| No data extracted | Verify the information was mentioned in the call and check schema validity | +| Partial extraction | Make non-critical fields optional and simplify nested structures | +| Incorrect values | Add more specific validation patterns and field descriptions | +| Extraction fails | Check API logs, verify assistant configuration, and test with simpler schema | + +## Get help + +Need assistance? We're here to help: +- [API Documentation](/api-reference) +- [Discord Community](https://discord.gg/pUFNcf2WmH) +- [Support](mailto:support@vapi.ai) \ No newline at end of file diff --git a/fern/assistants/structured-outputs.mdx b/fern/assistants/structured-outputs.mdx new file mode 100644 index 000000000..b7a53ea8e --- /dev/null +++ b/fern/assistants/structured-outputs.mdx @@ -0,0 +1,787 @@ +--- +title: Structured outputs +subtitle: Extract structured data from conversations using AI-powered analysis +slug: assistants/structured-outputs +--- + +## 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 + + + + Create a JSON Schema that describes the data you want to extract + + + Use the API to create a reusable structured output definition + + + Connect the structured output to one or more assistants or workflows + + + Data is automatically extracted after each call and stored in call artifacts + + + +## Quick start + +### Create a structured output + + +```typescript title="TypeScript (Server SDK)" +import { Vapi } from '@vapi-ai/server-sdk'; + +const vapi = new Vapi({ apiKey: process.env.VAPI_API_KEY }); + +const structuredOutput = await vapi.structuredOutputs.create({ + name: "Customer Info", + type: "ai", + description: "Extract customer contact information", + schema: { + type: "object", + properties: { + firstName: { + type: "string", + description: "Customer's first name" + }, + lastName: { + type: "string", + description: "Customer's last name" + }, + email: { + type: "string", + format: "email", + description: "Customer's email address" + }, + phone: { + type: "string", + pattern: "^\\+?[1-9]\\d{1,14}$", + description: "Phone number in E.164 format" + } + }, + required: ["firstName", "lastName"] + } +}); + +console.log('Created structured output:', structuredOutput.id); +``` + +```python title="Python (Server SDK)" +from vapi_python import Vapi + +vapi = Vapi(api_key=os.environ['VAPI_API_KEY']) + +structured_output = vapi.structured_outputs.create( + name="Customer Info", + type="ai", + description="Extract customer contact information", + schema={ + "type": "object", + "properties": { + "firstName": { + "type": "string", + "description": "Customer's first name" + }, + "lastName": { + "type": "string", + "description": "Customer's last name" + }, + "email": { + "type": "string", + "format": "email", + "description": "Customer's email address" + }, + "phone": { + "type": "string", + "pattern": "^\\+?[1-9]\\d{1,14}$", + "description": "Phone number in E.164 format" + } + }, + "required": ["firstName", "lastName"] + } +) + +print(f"Created structured output: {structured_output.id}") +``` + +```bash title="cURL" +curl -X POST https://api.vapi.ai/structured-output \ + -H "Authorization: Bearer $VAPI_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Customer Info", + "type": "ai", + "description": "Extract customer contact information", + "schema": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "description": "Customer'\''s first name" + }, + "lastName": { + "type": "string", + "description": "Customer'\''s last name" + }, + "email": { + "type": "string", + "format": "email", + "description": "Customer'\''s email address" + }, + "phone": { + "type": "string", + "pattern": "^\\+?[1-9]\\d{1,14}$", + "description": "Phone number in E.164 format" + } + }, + "required": ["firstName", "lastName"] + } + }' +``` + + +### Link to an assistant + +Add the structured output ID to your assistant's configuration: + + +```typescript title="TypeScript (Server SDK)" +const assistant = await vapi.assistants.create({ + name: "Customer Support Agent", + // ... other assistant configuration + artifactPlan: { + structuredOutputIds: [structuredOutput.id] + } +}); +``` + +```python title="Python (Server SDK)" +assistant = vapi.assistants.create( + name="Customer Support Agent", + # ... other assistant configuration + artifact_plan={ + "structuredOutputIds": [structured_output.id] + } +) +``` + +```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", + "artifactPlan": { + "structuredOutputIds": ["output-id-here"] + } + }' +``` + + +### Access extracted data + +After a call completes, retrieve the extracted data: + + +```typescript title="TypeScript (Server SDK)" +const call = await vapi.calls.get(callId); + +// Access structured outputs from call artifacts +const outputs = call.artifact?.structuredOutputs; + +if (outputs) { + for (const [outputId, data] of Object.entries(outputs)) { + console.log(`Output: ${data.name}`); + console.log(`Result:`, data.result); + + // Handle the extracted data + if (data.result) { + // Process successful extraction + const { firstName, lastName, email, phone } = data.result; + // ... save to database, send notifications, etc. + } + } +} +``` + +```python title="Python (Server SDK)" +call = vapi.calls.get(call_id) + +# Access structured outputs from call artifacts +outputs = call.artifact.get('structuredOutputs', {}) + +for output_id, data in outputs.items(): + print(f"Output: {data['name']}") + print(f"Result: {data['result']}") + + # Handle the extracted data + if data['result']: + # Process successful extraction + result = data['result'] + first_name = result.get('firstName') + last_name = result.get('lastName') + email = result.get('email') + phone = result.get('phone') + # ... save to database, send notifications, etc. +``` + +```javascript title="Webhook Response" +// In your webhook handler +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]) => { + console.log(`Extracted ${data.name}:`, data.result); + // Process the extracted data + }); + } + } + + res.status(200).send('OK'); +}); +``` + + +## Schema types + +### Primitive types + +Extract simple values directly: + + +```json title="String" +{ + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[A-Z][a-z]+$" +} +``` + +```json title="Number" +{ + "type": "number", + "minimum": 0, + "maximum": 100, + "multipleOf": 0.5 +} +``` + +```json title="Boolean" +{ + "type": "boolean", + "description": "Whether customer agreed to terms" +} +``` + +```json title="Enum" +{ + "type": "string", + "enum": ["small", "medium", "large", "extra-large"] +} +``` + + +### Object types + +Extract structured data with multiple fields: + +```json +{ + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Full name" + }, + "age": { + "type": "integer", + "minimum": 0, + "maximum": 120 + }, + "email": { + "type": "string", + "format": "email" + } + }, + "required": ["name", "email"] +} +``` + +### Array types + +Extract lists of items: + +```json +{ + "type": "array", + "items": { + "type": "object", + "properties": { + "product": { + "type": "string" + }, + "quantity": { + "type": "integer", + "minimum": 1 + } + } + }, + "minItems": 1, + "maxItems": 10 +} +``` + +### Nested structures + +Extract complex hierarchical data: + +```json +{ + "type": "object", + "properties": { + "customer": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "contact": { + "type": "object", + "properties": { + "email": {"type": "string", "format": "email"}, + "phone": {"type": "string"} + } + } + } + }, + "order": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "sku": {"type": "string"}, + "quantity": {"type": "integer"} + } + } + } + } + } + } +} +``` + +## Validation features + +### String formats + +Vapi supports standard JSON Schema formats for validation: + +| Format | Description | Example | +|--------|-------------|---------| +| `email` | Email addresses | john@example.com | +| `date` | Date in YYYY-MM-DD | 2024-01-15 | +| `time` | Time in HH:MM:SS | 14:30:00 | +| `date-time` | ISO 8601 datetime | 2024-01-15T14:30:00Z | +| `uri` | Valid URI | https://example.com | +| `uuid` | UUID format | 123e4567-e89b-12d3-a456-426614174000 | + +### Pattern matching + +Use regular expressions for custom validation: + +```json +{ + "type": "string", + "pattern": "^[A-Z]{2}-\\d{6}$", + "description": "Order ID like US-123456" +} +``` + +### Conditional logic + +Use `if/then/else` for conditional requirements: + +```json +{ + "type": "object", + "properties": { + "serviceType": { + "type": "string", + "enum": ["emergency", "scheduled"] + }, + "appointmentTime": { + "type": "string", + "format": "date-time" + } + }, + "if": { + "properties": { + "serviceType": {"const": "scheduled"} + } + }, + "then": { + "required": ["appointmentTime"] + } +} +``` + +## Custom models + +Configure which AI model performs the extraction: + + +```typescript title="TypeScript" +const structuredOutput = await vapi.structuredOutputs.create({ + name: "Sentiment Analysis", + type: "ai", + schema: { + type: "object", + properties: { + sentiment: { + type: "string", + enum: ["positive", "negative", "neutral"] + }, + confidence: { + type: "number", + minimum: 0, + maximum: 1 + } + } + }, + model: { + provider: "openai", + model: "gpt-4-turbo-preview", + temperature: 0.1, + messages: [ + { + role: "system", + content: "You are an expert at analyzing customer sentiment. Be precise and consistent." + }, + { + role: "user", + content: "Analyze the sentiment of this conversation:\n{{transcript}}" + } + ] + } +}); +``` + +```python title="Python" +structured_output = vapi.structured_outputs.create( + name="Sentiment Analysis", + type="ai", + schema={ + "type": "object", + "properties": { + "sentiment": { + "type": "string", + "enum": ["positive", "negative", "neutral"] + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + } + } + }, + model={ + "provider": "openai", + "model": "gpt-4-turbo-preview", + "temperature": 0.1, + "messages": [ + { + "role": "system", + "content": "You are an expert at analyzing customer sentiment. Be precise and consistent." + }, + { + "role": "user", + "content": "Analyze the sentiment of this conversation:\n{{transcript}}" + } + ] + } +) +``` + + +### 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 + + + +### Create structured output + + + Display name for the structured output (max 40 characters) + + + + Must be set to "ai" + + + + Description of what data to extract + + + + JSON Schema defining the structure of data to extract + + + + Array of assistant IDs to link this output to + + + + Custom model configuration for extraction + + +### Update structured output + + + + +To update the top level schema type after creation, you must include `?schemaOverride=true` as a query parameter in the URL + + +### List structured outputs + + + +Query parameters: +- `page` - Page number (default: 1) +- `limit` - Results per page (default: 20, max: 100) + +### Delete structured output + + + +## Common use cases + +### Customer information collection + +```json +{ + "name": "Customer Profile", + "type": "ai", + "schema": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "email": {"type": "string", "format": "email"}, + "phone": {"type": "string"}, + "accountNumber": {"type": "string"}, + "preferredContactMethod": { + "type": "string", + "enum": ["email", "phone", "sms"] + } + } + } +} +``` + +### Appointment scheduling + +```json +{ + "name": "Appointment Request", + "type": "ai", + "schema": { + "type": "object", + "properties": { + "preferredDate": {"type": "string", "format": "date"}, + "preferredTime": {"type": "string", "format": "time"}, + "duration": {"type": "integer", "enum": [15, 30, 45, 60]}, + "serviceType": { + "type": "string", + "enum": ["consultation", "follow-up", "procedure"] + }, + "notes": {"type": "string"} + }, + "required": ["preferredDate", "preferredTime", "serviceType"] + } +} +``` + +### Order processing + +```json +{ + "name": "Order Details", + "type": "ai", + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "product": {"type": "string"}, + "quantity": {"type": "integer", "minimum": 1}, + "specialInstructions": {"type": "string"} + }, + "required": ["product", "quantity"] + } + }, + "deliveryAddress": { + "type": "object", + "properties": { + "street": {"type": "string"}, + "city": {"type": "string"}, + "zipCode": {"type": "string", "pattern": "^\\d{5}$"} + } + }, + "deliveryInstructions": {"type": "string"} + } + } +} +``` + +### Lead qualification + +```json +{ + "name": "Lead Information", + "type": "ai", + "schema": { + "type": "object", + "properties": { + "company": {"type": "string"}, + "role": {"type": "string"}, + "budget": { + "type": "string", + "enum": ["< $10k", "$10k-50k", "$50k-100k", "> $100k"] + }, + "timeline": { + "type": "string", + "enum": ["immediate", "1-3 months", "3-6 months", "6+ months"] + }, + "painPoints": { + "type": "array", + "items": {"type": "string"} + }, + "nextSteps": {"type": "string"} + } + } +} +``` + +## Best practices + + + + Begin with basic schemas and add complexity as needed. Test with real conversations before adding advanced features. + + + + Help the AI understand what to extract by using clear field names and descriptions in your schema. + + + + Balance flexibility with validation. Too strict and extraction may fail; too loose and data quality suffers. + + + + 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: + +```typescript +if (data.result === null) { + console.log(`Extraction failed for ${data.name}`); + // Implement fallback logic +} +``` + +## Troubleshooting + +### No data extracted + + + + Ensure your JSON Schema is valid and properly formatted + + + Confirm the required information was actually mentioned + + + Verify the structured output ID is linked to your assistant + + + 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 + + +## Related + +- [Call analysis](/assistants/call-analysis) - Summarize and evaluate calls +- [Custom tools](/tools/custom-tools) - Trigger actions during calls +- [Webhooks](/server-url) - Receive extracted data via webhooks +- [Variables](/assistants/dynamic-variables) - Use dynamic data in conversations \ No newline at end of file diff --git a/fern/docs.yml b/fern/docs.yml index 9692fae99..d1ae8ee03 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -213,6 +213,16 @@ navigation: - page: Trieve integration path: knowledge-base/integrating-with-trieve.mdx icon: fa-light fa-brain + - section: Structured outputs + icon: fa-light fa-database + path: assistants/structured-outputs.mdx + contents: + - page: Quickstart + path: assistants/structured-outputs-quickstart.mdx + icon: fa-light fa-rocket + - page: Examples + path: assistants/structured-outputs-examples.mdx + icon: fa-light fa-code - page: Custom keywords path: customization/custom-keywords.mdx icon: fa-light fa-bullseye