Skip to content

Commit f753754

Browse files
author
Noah Raynor
committed
added assistant request section
1 parent 04d0ef4 commit f753754

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed

fern/docs.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,8 @@ navigation:
485485
contents:
486486
- page: Setting server URLs
487487
path: server-url/setting-server-urls.mdx
488+
- page: Assistant request
489+
path: server-url/assistant-request.mdx
488490
- page: Server events
489491
path: server-url/events.mdx
490492
- page: Spam call rejection
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
---
2+
title: Assistant Request
3+
subtitle: Learn how inbound resources fetch assistants dynamically from your server.
4+
slug: webhooks/assistant-request
5+
---
6+
7+
The Assistant Request webhook lets you decide which assistant (or transfer destination) should handle an inbound phone call at runtime. When a phone number or org has a **Server URL** configured, Vapi pauses the call setup, POSTs an `assistant-request` message to your server, and expects you to reply with the assistant configuration to use.
8+
9+
## Prerequisites
10+
11+
1. A phone number imported into your [Vapi Dashboard](https://dashboard.vapi.ai).
12+
2. A publicly reachable HTTPS endpoint (ngrok, Cloudflare tunnel, etc.).
13+
3. Optional: a shared secret or webhook credential to authenticate requests.
14+
15+
## Step 1: Configure the Number
16+
17+
1. Open **Phone Numbers** → select your number.
18+
2. Clear the **Assistant** field (so Vapi relies on the webhook).
19+
3. Under **Server URL**, enter your tunnel URL (e.g., `https://example.ngrok-free.app/webhook/vapi`).
20+
4. Set a **Server Secret** or attach a **Custom Credential** for request verification.
21+
5. Save.
22+
23+
> **Note:** If you leave the phone number’s Server URL blank, Vapi automatically falls back to the org-level server settings (`org.server.url`, secret, headers, etc.). Assistant requests still fire as long as either the number or the org has a Server URL configured.
24+
25+
## Step 2: Build a Webhook Endpoint
26+
27+
Any framework works; here’s a minimal Express server that logs the request and always replies with an existing assistant ID.
28+
29+
```javascript
30+
import express from 'express';
31+
32+
const ASSISTANT_ID = 'your-assistant-id';
33+
const SECRET = process.env.VAPI_SERVER_SECRET ?? 'my-secret';
34+
35+
const app = express();
36+
app.use(express.json({ limit: '1mb' }));
37+
38+
app.post('/webhook/vapi', (req, res) => {
39+
if (SECRET && req.headers['vapi-secret'] !== SECRET) {
40+
return res.status(401).json({ error: 'Unauthorized' });
41+
}
42+
43+
console.log('Assistant request:', JSON.stringify(req.body, null, 2));
44+
res.json({ assistantId: ASSISTANT_ID });
45+
});
46+
47+
app.listen(8000, () =>
48+
console.log('Listening on http://localhost:8000/webhook/vapi'),
49+
);
50+
```
51+
52+
## Step 3: Understand the Request Payload
53+
54+
The POST body always contains a `message` object whose `type` is `assistant-request`. Vapi includes call, phone number, and customer context so you can make routing decisions.
55+
56+
```524:533:libs/core/src/types/message.types.ts
57+
export class AssistantRequestMessage extends OutboundMessageBase {
58+
@IsIn(['assistant-request'])
59+
type: 'assistant-request';
60+
}
61+
```
62+
63+
Sample payload (truncated for brevity):
64+
65+
```json
66+
{
67+
"message": {
68+
"timestamp": 1723595100000,
69+
"type": "assistant-request",
70+
"call": { "id": "call_uuid", "orgId": "org_uuid", "transport": {...} },
71+
"phoneNumber": { "id": "pn_uuid", "number": "+15551234567" },
72+
"customer": { "number": "+15559871234" }
73+
}
74+
}
75+
```
76+
77+
## Step 4: Respond With an Assistant
78+
79+
Your JSON response must follow `AssistantRequestMessageResponse`, which extends the `CallAssistant` schema. That means you can return:
80+
81+
- `assistantId`: use a saved assistant.
82+
- `assistant`: inline assistant definition (same structure as POST `/assistant`).
83+
- `squadId` / `squad` or `workflowId` / `workflow`.
84+
- `assistantOverrides`, `squadOverrides`, or `workflowOverrides`.
85+
- `destination`: transfer immediately to a number or SIP URI.
86+
- `error`: reject the call with a spoken message.
87+
88+
```1679:1750:libs/core/src/types/message.types.ts
89+
export class AssistantRequestMessageResponse extends IntersectionType(
90+
OutboundMessageResponseBase,
91+
CallAssistant,
92+
) {
93+
destination?: TransferDestinationSip | TransferDestinationNumber;
94+
error?: string;
95+
}
96+
```
97+
98+
Example responses:
99+
100+
```json
101+
{ "assistantId": "2ccabf54-ccd8-4dff-ae93-e830159c8004" }
102+
```
103+
104+
```json
105+
{
106+
"assistant": {
107+
"name": "Dynamic Intake",
108+
"model": { "provider": "openai", "model": "gpt-4o-mini" },
109+
"voice": { "provider": "11labs", "voiceId": "..." }
110+
},
111+
"assistantOverrides": {
112+
"variables": { "accountId": "12345" }
113+
}
114+
}
115+
```
116+
117+
```json
118+
{ "destination": { "type": "number", "number": "+15551230000" } }
119+
```
120+
121+
```json
122+
{ "error": "All agents are busy. Please try again later." }
123+
```
124+
125+
## Assistant Request Flow
126+
127+
The diagram below shows the synchronous decision tree: Vapi pauses the call, invokes your Assistant Request Server (ARS), optionally enriches context via CRM, and either transfers the call or resumes it with the returned assistant.
128+
129+
```mermaid
130+
sequenceDiagram
131+
participant Caller
132+
participant Vapi
133+
participant ARS as "Assistant Request Server"
134+
participant CRM as "CRM (optional)"
135+
participant Transfer as "Transfer Destination (optional)"
136+
137+
%% 1. Caller calls Vapi
138+
Caller->>Vapi: Inbound phone call ("Connect me to support")
139+
140+
%% 2. Vapi self-note
141+
Note right of Vapi: Phone number lacks fixed assistant → trigger assistant request
142+
143+
%% 3. Vapi requests assistant
144+
Vapi->>ARS: POST /webhook/vapi<br/>(assistant-request payload with call + customer context)
145+
146+
%% 4. Optional CRM enrichment
147+
opt CRM enrichment (optional)
148+
ARS->>CRM: Lookup caller / apply business rules
149+
CRM-->>ARS: Customer profile / routing decision
150+
end
151+
152+
%% Optional CRM note
153+
Note over CRM: CRM step runs only when extra context is needed.
154+
155+
%% 5. ARS responds
156+
ARS-->>Vapi: assistantId | inline assistant | destination | error
157+
158+
%% 6. Vapi splits: forward vs assistant
159+
alt Response includes destination
160+
Vapi->>Transfer: Forward call to number/SIP destination
161+
Transfer-->>Caller: Connected to destination
162+
else Assistant provided (default)
163+
Vapi->>Caller: Assistant joins call using provided config
164+
end
165+
```
166+
167+
## Testing the Flow
168+
169+
1. Start your webhook server.
170+
2. Ensure its public URL is reachable by Vapi (through your tunnel, reverse proxy, or production host).
171+
3. Place a call to the configured Vapi number.
172+
4. Monitor your server logs to verify the assistant-request payload arrives.
173+
5. Confirm the call proceeds according to the assistant or destination you returned.
174+
175+
## Troubleshooting & Tips
176+
177+
- **Timeouts:** The assistant request times out after ~5 s (`DEFAULT_TIMEOUT_SECOND_ASSISTANT_REQUEST`). Keep logic fast or cache lookups.
178+
- **Missing server URL:** Calls end immediately with `call-start-error-neither-assistant-nor-server-set`.
179+
- **Invalid response:** Vapi records ended reasons such as `assistant-request-returned-error` or `assistant-request-returned-no-assistant`. Check the call log and your server logs.
180+
- **Authentication failures:** Ensure the secret/credential in the phone number matches what you validate in the webhook.
181+
- **Debugging:** Use the ngrok inspector (`http://127.0.0.1:4040`) to inspect payloads, or log the entire request as shown above.
182+
183+
By handling the assistant request webhook, you can dynamically route every inbound interaction—pick the right assistant per customer, run A/B tests, or short-circuit calls to human teams when necessary.

0 commit comments

Comments
 (0)