Skip to content

Commit 0a621e4

Browse files
committed
docs: sanitize learnings for customer-facing use
Remove internal feature flag names, infrastructure references (E2B, Temporal), internal class/function names (TurnTakingBuffer, squadTransferMessagesApply, evaluationPlanPassedCheck), and jargon (normalization, cork/uncork). Replace with customer-safe descriptions of observable platform behavior.
1 parent ad6962d commit 0a621e4

7 files changed

Lines changed: 52 additions & 61 deletions

File tree

docs/learnings/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Gotchas & Best Practices
22

3-
Non-obvious behaviors, silent defaults, and foot-guns in the Vapi backend that affect how your YAML/JSON resources behave at runtime. This is a companion to the API reference — it covers what the docs _don't_ tell you.
3+
Non-obvious behaviors, silent defaults, and foot-guns in the Vapi platform that affect how your YAML/JSON resources behave at runtime. This is a companion to the API reference — it covers what the docs _don't_ tell you.
44

55
Each file in this directory covers a specific resource type so you can load only the context you need:
66

docs/learnings/assistants.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Non-obvious behaviors and silent defaults for Vapi assistant settings.
88

99
### `temperature` defaults to 0, `maxTokens` defaults to 250
1010

11-
If omitted, the backend fills `temperature: 0` and `maxTokens: 250`. This can cause unexpectedly short responses.
11+
If omitted, Vapi defaults to `temperature: 0` and `maxTokens: 250`. This can cause unexpectedly short responses.
1212

1313
**Recommendation:** Set `maxTokens` explicitly (e.g., 1000–4000) for assistants that need longer responses.
1414

@@ -22,7 +22,7 @@ If omitted, the backend fills `temperature: 0` and `maxTokens: 250`. This can ca
2222

2323
### `emotionRecognitionEnabled` has no runtime effect
2424

25-
Despite being a configurable field, the endpointing buffer has an explicit no-op branch for this flag. Don't rely on it for behavior changes.
25+
Despite being a configurable field, this setting currently has **no effect** on call behavior. Don't rely on it.
2626

2727
---
2828

@@ -50,7 +50,7 @@ If omitted, transcripts with confidence below 0.4 may be **ignored** entirely (n
5050

5151
### `smartEndpointingPlan` **owns** turn detection when set
5252

53-
If you configure `startSpeakingPlan.smartEndpointingPlan`, the transcriber's own `endpointing` settings (VAD timeouts, etc.) are **not used** for turn detection. Smart endpointing takes full control.
53+
If you configure `startSpeakingPlan.smartEndpointingPlan`, the transcriber's own `endpointing` settings (voice activity detection timeouts, etc.) are **not used** for turn detection. Smart endpointing takes full control.
5454

5555
**Recommendation:** Don't set both and expect them to combine. Choose one approach.
5656

@@ -76,7 +76,7 @@ Hooks that fail validation are dropped without error. Your call will proceed wit
7676

7777
### Missing hook `toolId` is a warning, not an error
7878

79-
If a hook references a `toolId` that doesn't exist, the backend logs a warning and continues. This is different from `model.toolIds` where a missing ID **errors the call**.
79+
If a hook references a `toolId` that doesn't exist, Vapi logs a warning and continues. This is different from `model.toolIds` where a missing ID **errors the call**.
8080

8181
### Hook events are independent from timeout settings
8282

@@ -123,8 +123,8 @@ If not set, there's a 0.4-second pause after the user stops speaking before the
123123

124124
### `waitSeconds` and `smartEndpointingPlan` control different things
125125

126-
- `waitSeconds` drives the **TurnTakingBuffer**: it corks assistant audio output for that duration after VAD detects speech.
127-
- `smartEndpointingPlan` drives the **EndpointingBuffer**: it determines when the user has finished their turn using an AI model or heuristic.
126+
- `waitSeconds` controls a short pause on assistant audio after voice activity is detected — it delays the assistant's response by that duration.
127+
- `smartEndpointingPlan` controls how end-of-turn is detected — it determines when the user has finished speaking using an AI model or heuristic.
128128

129129
These are complementary, not alternatives.
130130

@@ -152,7 +152,7 @@ Unless you explicitly set `analysisPlan.summaryPlan.enabled: false`, post-call s
152152

153153
### Recording is enabled by default
154154

155-
Unless you set `artifactPlan.recordingEnabled: false`, calls are recorded. The backend falls back to `assistant.recordingEnabled` (deprecated), then defaults to `true`.
155+
Unless you set `artifactPlan.recordingEnabled: false`, calls are recorded. Vapi also checks `assistant.recordingEnabled` (deprecated) before defaulting to `true`.
156156

157157
### Default transcript labels are "AI" and "User"
158158

@@ -198,7 +198,7 @@ Omitting `serverMessages` does **not** mean "no webhooks." It means "all default
198198

199199
## HIPAA / Compliance
200200

201-
When HIPAA is enabled (on org or assistant) **and** the `ENABLE_ASSISTANT_PROVIDER_HIPAA_VALIDATION` feature flag is active, the model, voice, and transcriber providers must be on an approved allowlist. Non-compliant providers will **fail validation** on create/update.
201+
When HIPAA is enabled (on org or assistant) and HIPAA provider validation is enforced for your org, the model, voice, and transcriber providers must be on Vapi's approved allowlist. Non-compliant providers will **fail validation** on create/update.
202202

203203
---
204204

@@ -209,7 +209,7 @@ When HIPAA is enabled (on org or assistant) **and** the `ENABLE_ASSISTANT_PROVID
209209
1. Inline `model.tools` are processed first
210210
2. Each `toolId` is resolved from the org's saved tools — **a missing ID errors the call**
211211
3. Results are merged: `[...inline tools, ...resolved toolIds]`
212-
4. MCP tools are expanded from parent tool metadata
212+
4. MCP tools are expanded from configured integrations
213213

214214
### `toolIds` and inline `tools` can coexist
215215

docs/learnings/simulations.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ Simulation evaluations support these comparators:
3939

4040
Evaluations that require audio (`target: messages-with-audio`) are **skipped** in chat-mode simulations with `passed: false` and `isSkipped: true`.
4141

42-
**Critical:** If **all** required evaluations are audio-dependent and the simulation runs in chat mode, they're all skipped — and `evaluationPlanPassedCheck` returns **`true`** (0 required results = pass). This means your test suite can silently pass with no actual evaluation.
42+
**Critical:** If **all** required evaluations are audio-dependent and the simulation runs in chat mode, they're all skipped — and the run is treated as **passed** (0 required results = pass). This means your test suite can silently pass with no actual evaluation.
4343

4444
**Recommendation:** Include at least one text-based (`target: messages`) evaluation in every simulation that runs in chat mode.
4545

4646
---
4747

4848
## Missing References
4949

50-
- **At run creation (API):** Missing scenario or personality IDs throw `BadRequestException` immediately.
51-
- **At execution (Temporal worker):** Missing references throw `ApplicationFailure` — behavior depends on Temporal retry config.
52-
- **Inline runs:** Require snapshots in metadata or fail with "no inline config" error.
50+
- **At run creation (API):** Missing scenario or personality IDs fail the API request immediately with a validation error.
51+
- **At execution:** Missing references fail the run. Retries depend on platform configuration.
52+
- **Inline runs:** Require snapshots in metadata or fail with a "no inline config" error.

docs/learnings/squads.md

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ assistantOverrides:
4040

4141
## assistantDestinations vs Inline Tools
4242

43-
`member.assistantDestinations` is **sugar** that the backend converts into real tools:
43+
`member.assistantDestinations` is **shorthand** that Vapi converts into real tools:
4444

4545
- Destinations with `assistantId`, `contextEngineeringPlan`, or `variableExtractionPlan` → **handoff tools**
4646
- All other destinations → merged into a **transferCall tool** (creating one if needed)
@@ -51,20 +51,18 @@ Both mechanisms can coexist on the same member. If you use both `assistantDestin
5151

5252
## Context During Handoffs
5353

54-
After a handoff, the new assistant does NOT get a raw copy of all prior messages. The backend applies `squadTransferMessagesApply` which may summarize, filter, or restructure the conversation history. If `handoffMessages` are provided on the destination, they **replace** the active message context entirely.
54+
After a handoff, the new assistant does NOT get a raw copy of all prior messages. Vapi may summarize, filter, or restructure the conversation history during the transfer. If `handoffMessages` are provided on the destination, they **replace** the active message context entirely.
5555

5656
---
5757

5858
## Override Merge Order
5959

60-
```
61-
Final = deepMerge(
62-
assistant,
63-
member.assistantOverrides,
64-
squad.membersOverrides,
65-
call.squadOverrides
66-
)
67-
```
60+
The final assistant configuration is built by merging these layers in order:
61+
62+
1. Base assistant
63+
2. `member.assistantOverrides`
64+
3. `squad.membersOverrides`
65+
4. `call.squadOverrides`
6866

6967
Later layers win on conflicts. `variableValues` from all layers are merged separately.
7068

docs/learnings/structured-outputs.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ Non-obvious behaviors and silent defaults for Vapi structured output extraction.
88

99
### Use single `type` values, not arrays
1010

11-
**Bad:** `type: [string, "null"]`this can cause `TypeError: .toLowerCase is not a function` in the dashboard, and the backend uses only the **first** element of the array for extraction.
11+
**Bad:** `type: [string, "null"]`using an array for `type` is not supported and may cause errors in the dashboard and extraction pipeline. Vapi uses only the **first** element of the array.
1212

1313
**Good:** `type: string` — express nullability in the `description` instead (e.g., "Return null if not applicable").
1414

15-
### `wrapIfPrimitive` behavior
15+
### Primitive schemas are auto-wrapped
1616

1717
Primitive schemas (`string`, `boolean`, `number`) are automatically wrapped in an object for OpenAI structured output mode:
1818
```json
@@ -33,7 +33,7 @@ Structured outputs require `assistant_ids` as **Vapi UUIDs** (v4 format). Assist
3333

3434
## Default Extraction Model
3535

36-
If you omit `model` on a structured output, the default is:
36+
If you omit `model` on a structured output, the default (as of this writing) is:
3737
```yaml
3838
model:
3939
provider: openai
@@ -42,9 +42,9 @@ model:
4242
maxTokens: 4000
4343
```
4444
45-
Fallback sequence: your configured model → `gpt-4.1` → `gemini-2.5-flash`.
45+
Fallback sequence: your configured model → `gpt-4.1` → `gemini-2.5-flash`. These defaults may change over time — check the API reference for the current default.
4646

47-
For multimodal extraction (`messages-with-audio`), the default is **Gemini 2.5 Pro**.
47+
For multimodal extraction (`messages-with-audio`), the default is **Gemini 2.5 Pro** (as of this writing).
4848

4949
---
5050

docs/learnings/tools.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Non-obvious behaviors and silent defaults for Vapi tool types.
1010

1111
**What you might expect:** `function.parameters` and `body` are two independent schemas — one for the LLM, one for the HTTP request.
1212

13-
**What actually happens:** The backend **overwrites** `function.parameters` with a copy of `body` during normalization. Whatever you define in `body.properties` becomes the tool schema the LLM sees. Any `function.parameters` you set on an apiRequest tool is **silently ignored**.
13+
**What actually happens:** Vapi **overwrites** `function.parameters` with a copy of `body` when the tool is processed. Whatever you define in `body.properties` becomes the tool schema the LLM sees. Any `function.parameters` you set on an apiRequest tool is **silently ignored**.
1414

1515
**Recommendation:** Only define your schema in `body`. Do not set `function.parameters` on apiRequest tools.
1616

@@ -28,19 +28,19 @@ If you omit `method`, it defaults to `POST` — not `GET`.
2828

2929
### API response must be JSON
3030

31-
On a successful HTTP response (2xx), the backend calls `response.json()`. If your API returns non-JSON (plain text, HTML, XML), the tool call will error.
31+
On a successful HTTP response (2xx), Vapi expects a JSON body. If your API returns non-JSON (plain text, HTML, XML), the tool call will error.
3232

3333
### `function.strict` is always cleared
3434

35-
The backend sets `strict: undefined` on apiRequest tools during normalization. If you need strict schema enforcement from the LLM provider, use a `function` type tool instead.
35+
Vapi removes `strict` from apiRequest tools when they are processed. If you need strict schema enforcement from the LLM provider, use a `function` type tool instead.
3636

3737
### `async` has no effect
3838

39-
The backend forces `async: undefined` on apiRequest tools. They always execute synchronously (HTTP call → wait for response → return to LLM).
39+
Vapi ignores `async` on apiRequest tools. They always execute synchronously (HTTP call → wait for response → return to LLM).
4040

4141
### Credential fallback behavior
4242

43-
If `credentialId` is set on the tool, that specific credential is used. If omitted, the backend uses the **first** `webhook` or `custom-credential` it finds on the call's credential list. If you have multiple webhook credentials, always set `credentialId` explicitly.
43+
If `credentialId` is set on the tool, that specific credential is used. If omitted, Vapi picks one from the call's available credentials automatically. If you have multiple webhook credentials, always set `credentialId` explicitly to avoid ambiguity.
4444

4545
---
4646

@@ -50,7 +50,7 @@ If `credentialId` is set on the tool, that specific credential is used. If omitt
5050

5151
**What you might expect:** A function tool without `server.url` fails at runtime.
5252

53-
**What actually happens:** The backend falls back through a hierarchy: tool `server.url` → assistant `server.url` → phone number `server.url` → org `server.url`. Omitting `server` on the tool is valid if the assistant or org has a server URL configured.
53+
**What actually happens:** Vapi falls back through a hierarchy: tool `server.url` → assistant `server.url` → phone number `server.url` → org `server.url`. Omitting `server` on the tool is valid if the assistant or org has a server URL configured.
5454

5555
**Recommendation:** Set `server.url` on the tool for clarity, or document which level provides the webhook URL.
5656

@@ -66,9 +66,9 @@ If `credentialId` is set on the tool, that specific credential is used. If omitt
6666
When `strict: true` is set on a function tool:
6767
- Every `object` node must have `additionalProperties: false`
6868
- Every property must be listed in `required`
69-
- The org must have the `ENABLE_TOOL_STRICT_MODE` feature flag enabled
69+
- Strict mode must be enabled for your organization
7070

71-
If the schema doesn't meet these requirements, `strict` is silently stripped and a warning is logged.
71+
If the schema doesn't meet these requirements, `strict` may be silently disabled for that tool.
7272

7373
---
7474

@@ -131,7 +131,7 @@ If you don't set `function.name`:
131131

132132
### Destination resolution is fuzzy
133133

134-
The backend tries multiple matching strategies for the model's destination argument:
134+
Vapi tries multiple matching strategies for the model's destination argument:
135135
1. Match by UUID (`assistantId`, `squadId`)
136136
2. Match by name (`assistantName`, squad `name`)
137137
3. Fall back to first destination if argument is `"dynamic"` or missing
@@ -145,15 +145,15 @@ The backend tries multiple matching strategies for the model's destination argum
145145

146146
### Execution timeout is capped at 30 seconds
147147

148-
Even if you set `timeoutSeconds: 60`, the backend caps it at `Math.min(timeoutSeconds ?? 30, 30)`.
148+
Even if you set `timeoutSeconds: 60`, the effective timeout is capped at **30 seconds**.
149149

150150
### Code must be valid top-level async JavaScript
151151

152-
Code runs in an E2B sandbox as `node /tmp/code.js`. The code receives `args` (tool arguments) and `env` (environment variables) as globals. Return values are captured from the resolved promise.
152+
Code runs in a managed sandbox as Node.js. The code receives `args` (tool arguments) and `env` (environment variables) as globals. Return values are captured from the resolved promise.
153153

154154
### Static validation checks `args.*` and `env.*` references
155155

156-
When saving a code tool, the backend parses your code and validates that every `args.foo` reference has a matching property in `function.parameters.properties`, and every `env.BAR` reference has a matching key in `environmentVariables`. Mismatches are rejected.
156+
When saving a code tool, Vapi parses your code and validates that every `args.foo` reference has a matching property in `function.parameters.properties`, and every `env.BAR` reference has a matching key in `environmentVariables`. Mismatches are rejected.
157157

158158
### `function.strict` is cleared (like apiRequest)
159159

@@ -195,11 +195,11 @@ After one delayed message plays, another won't play for at least 2.5 seconds —
195195

196196
| Tool type | `strict` behavior |
197197
|-----------|-------------------|
198-
| `function` | **Preserved** — passed to LLM provider if org has `ENABLE_TOOL_STRICT_MODE` |
199-
| `apiRequest` | **Cleared** to `undefined` during normalization |
200-
| `code` | **Cleared** to `undefined` during normalization |
201-
| `transferCall` | **Cleared** to `undefined` during normalization |
202-
| `endCall` | **Cleared** to `undefined` during normalization |
203-
| `handoff` | **Cleared** to `undefined` during normalization |
198+
| `function` | **Preserved** — passed to LLM provider if strict mode is enabled for your org |
199+
| `apiRequest` | **Removed** when the tool is processed |
200+
| `code` | **Removed** when the tool is processed |
201+
| `transferCall` | **Removed** when the tool is processed |
202+
| `endCall` | **Removed** when the tool is processed |
203+
| `handoff` | **Removed** when the tool is processed |
204204

205205
Only `function` tools support `strict` mode.

docs/learnings/webhooks.md

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,33 +24,26 @@ These are **two independent settings**:
2424

2525
| Setting | Controls | Default |
2626
|---------|----------|---------|
27-
| `server.timeoutSeconds` | Webhook delivery timeout (status-update, end-of-call-report, etc.) | `DEFAULT_TIMEOUT_SECOND` |
28-
| `tool.timeoutSeconds` (apiRequest) | Individual tool HTTP call timeout | `DEFAULT_TIMEOUT_SECOND` |
27+
| `server.timeoutSeconds` | Webhook delivery timeout (status-update, end-of-call-report, etc.) | Platform default (see API reference) |
28+
| `tool.timeoutSeconds` (apiRequest) | Individual tool HTTP call timeout | Platform default (see API reference) |
2929

3030
They don't inherit from each other.
3131

3232
---
3333

3434
## Unreachable Server Behavior
3535

36-
- **Missing `server.url`:** Returns empty object, no network call. No error.
37-
- **Server returns error:** Error is logged and returned. For `tool-calls` message type, the backend **still attempts to parse** the error response body for tool results.
38-
- **High failure rate:** A circuit breaker kicks in — "noisy" message types (frequent events like `speech-update`) are aborted when the error rate exceeds a threshold. Critical messages like `end-of-call-report` continue attempting.
36+
- **Missing `server.url`:** No network call is made. No error is raised.
37+
- **Server returns error:** The error is logged and returned to the platform. For `tool-calls` events, Vapi still attempts to parse the error response body for tool results.
38+
- **High failure rate:** Under sustained delivery failures, Vapi may reduce delivery of high-frequency events (like `speech-update`) while still attempting critical events (like `end-of-call-report`).
3939

4040
---
4141

4242
## Credential Resolution
4343

44-
For server webhooks:
45-
1. If `server.credentialId` is set → use that specific credential
46-
2. Else → use the first `custom-credential` on the call
47-
3. Else → use the first `webhook` credential on the call
44+
If `credentialId` is set on the server or tool, that specific credential is used. If omitted, Vapi picks one from the call's available credentials automatically.
4845

49-
For apiRequest tools:
50-
1. If `tool.credentialId` is set → use that specific credential
51-
2. Else → use the first `webhook` or `custom-credential` on the call
52-
53-
**Recommendation:** Always set `credentialId` explicitly when multiple credentials exist to avoid ambiguous fallback.
46+
**Recommendation:** Always set `credentialId` explicitly when multiple credentials exist to avoid ambiguous selection.
5447

5548
---
5649

0 commit comments

Comments
 (0)