diff --git a/fern/assistants/structured-outputs-examples.mdx b/fern/assistants/structured-outputs-examples.mdx
index 26560fc24..36472a2f5 100644
--- a/fern/assistants/structured-outputs-examples.mdx
+++ b/fern/assistants/structured-outputs-examples.mdx
@@ -1122,6 +1122,67 @@ Process loan or credit applications with financial information.
```
+## Conditional extraction
+
+Attach [conditions](/assistants/structured-outputs#conditional-generation) to a structured output so it only runs when a call is worth analyzing. This keeps noisy, incomplete calls out of your data and makes the reason for a skip visible in the assistant preview, call logs, and sessions.
+
+This example only extracts a post-call outcome when the customer actually reached and ended the conversation (at least 6 messages, 15 seconds, and a customer-ended call):
+
+
+```typescript title="TypeScript (Server SDK)"
+const outcome = await vapi.structuredOutputs.create({
+ name: "Call Outcome",
+ type: "ai",
+ description: "Capture the outcome and any follow-up requested by the customer",
+ schema: {
+ type: "object",
+ properties: {
+ outcome: {
+ type: "string",
+ enum: ["resolved", "callback-requested", "escalated", "no-resolution"]
+ },
+ followUpNotes: { type: "string" }
+ },
+ required: ["outcome"]
+ },
+ conditions: [
+ { type: "minMessages", count: 6 },
+ { type: "minCallDuration", seconds: 15 },
+ { type: "endedReason", operator: "oneOf", values: ["customer-ended-call"] }
+ ]
+});
+```
+
+```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": "Call Outcome",
+ "type": "ai",
+ "description": "Capture the outcome and any follow-up requested by the customer",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "outcome": {
+ "type": "string",
+ "enum": ["resolved", "callback-requested", "escalated", "no-resolution"]
+ },
+ "followUpNotes": { "type": "string" }
+ },
+ "required": ["outcome"]
+ },
+ "conditions": [
+ { "type": "minMessages", "count": 6 },
+ { "type": "minCallDuration", "seconds": 15 },
+ { "type": "endedReason", "operator": "oneOf", "values": ["customer-ended-call"] }
+ ]
+ }'
+```
+
+
+On a call that ends before those thresholds are met — a wrong number, a hang-up, a voicemail — the output is skipped instead of producing an empty or misleading result.
+
## Best practices for complex schemas
diff --git a/fern/assistants/structured-outputs.mdx b/fern/assistants/structured-outputs.mdx
index d93d43cb9..ad7e04f59 100644
--- a/fern/assistants/structured-outputs.mdx
+++ b/fern/assistants/structured-outputs.mdx
@@ -38,9 +38,9 @@ Structured outputs enable automatic extraction of specific information from voic
```typescript title="TypeScript (Server SDK)"
-import { Vapi } from '@vapi-ai/server-sdk';
+import { VapiClient } from '@vapi-ai/server-sdk';
-const vapi = new Vapi({ apiKey: process.env.VAPI_API_KEY });
+const vapi = new VapiClient({ token: process.env.VAPI_API_KEY });
const structuredOutput = await vapi.structuredOutputs.create({
name: "Customer Info",
@@ -76,9 +76,10 @@ console.log('Created structured output:', structuredOutput.id);
```
```python title="Python (Server SDK)"
-from vapi_python import Vapi
+import os
+from vapi import Vapi
-vapi = Vapi(api_key=os.environ['VAPI_API_KEY'])
+vapi = Vapi(token=os.environ['VAPI_API_KEY'])
structured_output = vapi.structured_outputs.create(
name="Customer Info",
@@ -237,10 +238,10 @@ for output_id, data in outputs.items():
```javascript title="Webhook Response"
// In your webhook handler
app.post('/vapi/webhook', (req, res) => {
- const { type, call } = req.body;
+ const { message } = req.body;
- if (type === 'call.ended') {
- const outputs = call.artifact?.structuredOutputs;
+ if (message.type === 'end-of-call-report') {
+ const outputs = message.artifact?.structuredOutputs;
if (outputs) {
Object.entries(outputs).forEach(([outputId, data]) => {
@@ -440,9 +441,100 @@ Use `if/then/else` for conditional requirements:
}
```
+## Conditional generation
+
+By default, every linked structured output runs after each call. Attach **conditions** to a structured output so it only generates when the call meets your criteria — for example, skip extraction on calls that barely started, or only run an output when the call ended a certain way.
+
+
+Conditions gate **whether the output runs at all**. This is different from the [`if/then/else` schema logic](#conditional-logic) above, which shapes the data *within* a single extraction.
+
+
+### How conditions work
+
+- Add a `conditions` array to a structured output.
+- **Every condition must pass** for the output to run (AND semantics).
+- When `conditions` is omitted or empty, no user-defined conditions gate the output (runtime defaults still apply).
+- On update (`PATCH`), send `conditions: null` to clear a previously saved gate.
+
+When a condition isn't met, the output is **skipped** rather than failed. Skipped outputs are surfaced in the **assistant preview**, **call logs**, and **sessions**, so you can see which outputs ran and which were gated out.
+
+### Condition types
+
+| Type | Fields | Output runs when |
+|------|--------|------------------|
+| `minMessages` | `count` (integer ≥ 0) | The conversation has at least `count` messages. `count: 0` removes the runtime default minimum. |
+| `minCallDuration` | `seconds` (integer ≥ 0) | The call lasted at least `seconds` seconds. |
+| `endedReason` | `operator` (`oneOf` or `notOneOf`), `values` (array of strings) | The call's [ended reason](/calls/call-ended-reason) passes the membership test against `values`. `oneOf` runs the output only if the ended reason is in `values`; `notOneOf` runs it only if the ended reason is not in `values`. |
+
+### Example
+
+Only extract a call summary when the call had a real conversation (at least 4 messages and 10 seconds) and the customer ended it:
+
+
+```typescript title="TypeScript (Server SDK)"
+const structuredOutput = await vapi.structuredOutputs.create({
+ name: "Call Summary",
+ type: "ai",
+ description: "Summarize the conversation",
+ schema: {
+ type: "object",
+ properties: {
+ summary: { type: "string" }
+ }
+ },
+ conditions: [
+ { type: "minMessages", count: 4 },
+ { type: "minCallDuration", seconds: 10 },
+ { type: "endedReason", operator: "oneOf", values: ["customer-ended-call"] }
+ ]
+});
+```
+
+```python title="Python (Server SDK)"
+structured_output = vapi.structured_outputs.create(
+ name="Call Summary",
+ type="ai",
+ description="Summarize the conversation",
+ schema={
+ "type": "object",
+ "properties": {
+ "summary": {"type": "string"}
+ }
+ },
+ conditions=[
+ {"type": "minMessages", "count": 4},
+ {"type": "minCallDuration", "seconds": 10},
+ {"type": "endedReason", "operator": "oneOf", "values": ["customer-ended-call"]}
+ ]
+)
+```
+
+```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": "Call Summary",
+ "type": "ai",
+ "description": "Summarize the conversation",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "summary": { "type": "string" }
+ }
+ },
+ "conditions": [
+ { "type": "minMessages", "count": 4 },
+ { "type": "minCallDuration", "seconds": 10 },
+ { "type": "endedReason", "operator": "oneOf", "values": ["customer-ended-call"] }
+ ]
+ }'
+```
+
+
## Custom models
-Configure which AI model performs the extraction:
+By default, structured outputs are extracted with GPT-4.1. Configure the `model` to use a different provider or model, or to supply your own extraction prompts:
```typescript title="TypeScript"
@@ -465,7 +557,7 @@ const structuredOutput = await vapi.structuredOutputs.create({
},
model: {
provider: "openai",
- model: "gpt-4-turbo-preview",
+ model: "gpt-4.1",
temperature: 0.1,
messages: [
{
@@ -474,7 +566,7 @@ const structuredOutput = await vapi.structuredOutputs.create({
},
{
role: "user",
- content: "Analyze the sentiment of this conversation:\n{{transcript}}"
+ content: "Extract {{structuredOutput.name}} using this schema:\n{{structuredOutput.schema}}\n\nAnalyze the sentiment of this conversation:\n{{transcript}}"
}
]
}
@@ -501,7 +593,7 @@ structured_output = vapi.structured_outputs.create(
},
model={
"provider": "openai",
- "model": "gpt-4-turbo-preview",
+ "model": "gpt-4.1",
"temperature": 0.1,
"messages": [
{
@@ -510,7 +602,7 @@ structured_output = vapi.structured_outputs.create(
},
{
"role": "user",
- "content": "Analyze the sentiment of this conversation:\n{{transcript}}"
+ "content": "Extract {{structuredOutput.name}} using this schema:\n{{structuredOutput.schema}}\n\nAnalyze the sentiment of this conversation:\n{{transcript}}"
}
]
}
@@ -523,57 +615,44 @@ structured_output = vapi.structured_outputs.create(
Use these variables in custom prompts:
- `{{transcript}}` - Full conversation transcript
-- `{{messages}}` - Conversation messages array
-- `{{callEndedReason}}` - How the call ended
+- `{{messages}}` - Conversation messages array (JSON)
+- `{{endedReason}}` - How the call ended
+- `{{duration}}` - Call duration in seconds
+- `{{startedAt}}` - Call start time (ISO 8601)
+- `{{endedAt}}` - Call end time (ISO 8601)
+- `{{systemPrompt}}` - The assistant's system prompt
+- `{{structuredOutput}}` - The full structured output definition
- `{{structuredOutput.name}}` - Output name
- `{{structuredOutput.description}}` - Output description
- `{{structuredOutput.schema}}` - Schema definition
+
+When you supply custom `messages`, reference either `{{transcript}}` or `{{messages}}` for the conversation, and a variation of `{{structuredOutput}}` so the model has the schema definition.
+
+
## API reference
-
+The full set of request fields, response types, and query parameters lives in the API reference. Refer there for all possible values rather than duplicating them here.
### 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
-
+See [Create structured output](/api-reference/structured-outputs/structured-output-controller-create) for every request field, including `type`, `conditions`, `model`, and `assistantIds`.
### Update structured output
-
-To update the top level schema type after creation, you must include `?schemaOverride=true` as a query parameter in the URL
-
+
+Updating the top-level schema type after creation requires the `?schemaOverride=true` query parameter. See [Update structured output](/api-reference/structured-outputs/structured-output-controller-update).
+
### List structured outputs
-Query parameters:
-- `page` - Page number (default: 1)
-- `limit` - Results per page (default: 20, max: 100)
+See [List structured outputs](/api-reference/structured-outputs/structured-output-controller-find-all) for all query parameters, including filtering, sorting, and pagination.
### Delete structured output
@@ -713,7 +792,7 @@ Query parameters:
### 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
+- **Use appropriate models**: use a capable model (for example, GPT-4.1) for complex schemas; lighter models can handle simpler ones
- **Set low temperature**: Use 0.1 or lower for consistent extraction
- **Monitor success rates**: Track extraction failures and adjust schemas accordingly
@@ -759,14 +838,6 @@ if (data.result === null) {
- Make fields optional if they might not be mentioned
- Verify data types match expected values
-## HIPAA compliance
-
-
-**Important for HIPAA-enabled organizations:** When HIPAA mode is enabled, structured outputs are generated normally but **not stored** by default, to protect PHI. You can still receive them via webhooks during the call.
-
-To store a specific structured output that doesn't contain PHI, see [HIPAA Compliance & Storage Settings](/assistants/structured-outputs-quickstart#hipaa-compliance-storage-settings) for the full walkthrough and safe/unsafe use cases.
-
-
## Limitations
diff --git a/fern/docs.yml b/fern/docs.yml
index 0216e7724..c1e620ac1 100644
--- a/fern/docs.yml
+++ b/fern/docs.yml
@@ -563,6 +563,7 @@ navigation:
icon: fa-light fa-chart-line
- section: Structured outputs
icon: fa-light fa-database
+ path: assistants/structured-outputs.mdx
contents:
- page: Quickstart
path: assistants/structured-outputs-quickstart.mdx