Structured outputs
A structured output turns a conversation into a record: a JSON Schema you define, filled in from what was said. It is how a call becomes a booking, a lead, a support ticket or a qualification score instead of a paragraph somebody has to read.
Defining one
GET /api/v1/structured-outputs
POST /api/v1/structured-outputs
GET /api/v1/structured-outputs/templates
GET /api/v1/structured-outputs/{structuredOutput}
PATCH /api/v1/structured-outputs/{structuredOutput}
DELETE /api/v1/structured-outputs/{structuredOutput}
POST /api/v1/structured-outputs/{structuredOutput}/publishcurl -X POST "$CALLAGENT_API_URL/structured-outputs" \
-H "Authorization: Bearer $CALLAGENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"key": "appointment",
"name": "Appointment request",
"prompt": "Extract the appointment the caller asked for. Leave a field null if it was not stated; never guess a date.",
"schema": {
"type": "object",
"properties": {
"service": { "type": "string", "description": "What they want done." },
"preferred_date": { "type": "string", "format": "date" },
"urgency": { "type": "string", "enum": ["routine", "soon", "emergency"] },
"callback_number": { "type": "string" }
},
"required": ["service"],
"additionalProperties": false
}
}'const output = await fetch(`${process.env.CALLAGENT_API_URL}/structured-outputs`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
key: 'appointment',
name: 'Appointment request',
prompt: 'Extract the appointment the caller asked for. Leave a field null if it was not stated.',
schema: {
type: 'object',
properties: {
service: { type: 'string' },
preferred_date: { type: 'string', format: 'date' },
urgency: { type: 'string', enum: ['routine', 'soon', 'emergency'] },
},
required: ['service'],
additionalProperties: false,
},
}),
}).then((response) => response.json());The schema is validated structurally when you save it, so a malformed schema is rejected at definition time rather than discovered on a live call.
GET /structured-outputs/templates returns starting points for the common shapes.
Fields
| Field | Notes |
|---|---|
key | Stable identifier, unique per organization. This is what you look for in the result. |
name | Human label. |
schema | JSON Schema. Keep it flat and keep additionalProperties: false. |
prompt | The extraction instruction. Say explicitly what to do when a field was not mentioned. |
target | What the extraction reads. |
model | { provider, model } — the extraction can use a cheaper model than the conversation did. |
enabled |
Like assistants and tools, structured outputs are versioned: publish freezes the current definition, so changing a schema does not retroactively alter how last month's calls were parsed.
Attaching to an assistant
Either through the pivot the API exposes, or in the assistant's analysisPlan:
analysisPlan:
summaryEnabled: true
structuredOutputs:
- name: appointment
schema: { … }Writing a schema that survives contact with speech
Four rules, learned the expensive way:
- Make almost everything optional. A caller who did not mention a date has not given you a date. A
requiredfield the model cannot fill is a field the model will invent. - Use enums for anything categorical.
urgencyas a free string produces "pretty urgent", "ASAP", "not urgent I guess". As an enum it produces one of three values you can branch on. - Say what "unknown" looks like in the prompt. "Leave a field null if it was not stated; never guess a date" is worth more than any schema constraint.
- Keep it flat. Nested objects extracted from speech are where accuracy goes to die. Two structured outputs beat one with a sub-object.
Reading the result
A call's extraction lands on the call record and appears in the structured-outputs tab of the dashboard, in GET /calls/{call} and in the analysis.completed event.
Extraction is not currently run by the control plane
Definitions, versioning, publication, the attachment to an assistant and the columns that store a result are all implemented. What is missing is the trigger: the post-call analysis pipeline that would perform the extraction is not dispatched after a call finishes.
So a structured output you define today will not be filled in by the control plane. It is populated only if the gateway supplies it in the finalisation payload. See Analytics & cost and Implementation status.
Next
- Assistants — the
analysisPlanthis attaches to. - Analytics & cost — what else happens after a call.
- Webhooks — being told when a result exists.