Skip to main content

Settings

Organization settings, the analysis prompts, the extraction fields, and model routing. The last two have the widest blast radius — they change how every call is analysed.

Organization settings​

GET /api/settings

{
"report_recipients": ["[email protected]"],
"call_context": "Outbound calls selling ergonomic office furniture to SMBs.",
"stt_model": "gpu",
"auto_reports_enabled": true,
"is_premium": false,
"dismissed_default_scores": ["small_talk"]
}

PUT /api/settings — manager or above

{
"report_recipients": ["[email protected]", "[email protected]"],
"call_context": "Outbound calls selling ergonomic office furniture to SMBs.",
"auto_reports_enabled": true
}

Report recipients​

Who receives generated reports. Plain addresses; they do not need to be members of the organization.

Dismissed default scores​

PATCH /api/settings/dismissed-default-scores

{ "dismissed_default_scores": ["small_talk", "call_opening"] }

Hides built-in scoring criteria that do not apply to your calls. The score is still computed — this only removes it from display, so dismissing one and changing your mind later costs nothing.


Analysis prompts​

GET /api/settings/analysis-prompts

{
"system_prompt": "You are analysing B2B furniture sales calls…",
"safety_prompt": "Never infer a customer's health, religion, or finances…",
"default_system_prompt": "…",
"default_safety_prompt": "…",
"system_prompt_uses_default": false,
"safety_prompt_uses_default": true,
"combined_prompt_preview": "…"
}

The defaults are returned alongside your overrides, so a UI can show what you are diverging from — and combined_prompt_preview shows exactly what the model will receive, which is the only reliable way to check a prompt edit did what you meant.

PATCH /api/settings/analysis-prompts — manager or above

{
"system_prompt": "You are analysing B2B furniture sales calls…",
"set_system_prompt": true,
"safety_prompt": null,
"set_safety_prompt": false
}

The set_* booleans distinguish "set this to the value I sent" from "leave it alone" — which is what lets you clear a prompt back to the default by sending null with set_*: true, rather than an omitted field meaning two things.

Prompt changes do not re-analyse existing calls

They apply to calls analysed afterwards. Use POST /api/calls/{id}/reanalyze to bring an existing call up to date — superadmin only, because doing it across a deployment is expensive.


Extraction fields​

The structured questions asked of every transcript. Reading them is open to any member; changing them is superadmin only, because they apply across the whole deployment.

List fields​

GET /api/extraction-fields — superadmin

{
"fields": [
{
"id": 7,
"uuid": "ef1a2b3c-…",
"name": "budget_confirmed",
"field_type": "boolean",
"description": "Did the customer confirm they have budget approved?",
"options": [],
"min_value": null,
"max_value": null,
"scope": ["outbound"],
"category": "qualification",
"enabled": true
}
]
}

Create a field​

POST /api/extraction-fields — superadmin

{
"name": "budget_confirmed",
"field_type": "boolean",
"description": "Did the customer confirm they have budget approved?",
"scope": ["outbound"],
"options": [],
"min_value": null,
"max_value": null,
"category": "qualification",
"enabled": true
}
FieldRequiredNotes
name✓The key this lands under in a call's custom_field_values
field_type✓boolean, string, number, enum
scope✓Restricts the field to inbound, outbound, or both
description—What the model is told to look for — write it as an instruction
options—Allowed values for enum
min_value / max_value—Bounds for number
category—Grouping for display
enabled—Disable without deleting, preserving historical values

Update a field​

PATCH /api/extraction-fields/{id} — superadmin

Every field above is optional on update, plus one that only exists here:

FieldNotes
prompt_overrideReplaces the generated instruction for this field with your own wording

Delete a field​

DELETE /api/extraction-fields/{id} — superadmin

Field labels​

GET /api/extraction-field-labels

Available to any member, and to API keys with sales:analyses:read. Returns just the UUID, name, and type of each field — enough to render a call's custom_field_values without exposing the prompts behind them.

description is a prompt, not a label

"Did the customer confirm they have budget approved?" extracts reliably. "Budget" does not. If a field is coming back empty or wrong across many calls, the description is usually why.


Model routing​

GET /api/settings/model-routing

{
"stt_model": "gpu",
"diarization_url": "https://…",
"translation": { "enabled": true, "provider": "openai", "vllm_endpoint": null },
"custom_analysis": { "enabled": true, "provider": "vllm", "vllm_endpoint": "http://localhost:8000/v1" }
}

PATCH /api/settings/model-routing — superadmin only

stt_model accepts gpu or sarvam. The remaining keys choose which model serves each stage: diarization, translation, and the custom analysis pass. provider: "vllm" points a stage at self-hosted inference through vllm_endpoint, which is how an on-prem deployment keeps transcripts inside its own network.

This is superadmin-gated in every binary that has it — a misrouted model breaks analysis for every organization on the deployment, not just the one that changed it.


Call layout​

Control how a call's analysis is arranged in the console. Display only — nothing here changes what is extracted.

List sections​

GET /api/call-sections — any member

{
"sections": [
{ "key": "__section:scores", "label": "Scores" },
{ "key": "__section:key_details", "label": "Key Details" }
]
}

Set the layout​

PUT /api/call-layout — superadmin

{
"hidden": ["waveform"],
"collapsed": ["key_details"]
}
FieldNotes
hiddenSection ids not shown at all
collapsedSection ids shown collapsed by default

Order the signal groups​

PUT /api/signal-group-order — superadmin

{ "group_order": ["qualification", "outcome", "next_action"] }