Repository navigation
Dynamic variables: default values and the on_missing setting #236
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -105,16 +105,16 @@ | |
| "branch_id": "b7d4…", | ||
| "source": "phone", | ||
| "status": "completed", | ||
| "end_reason": "user_hangup", | ||
| "conversation_started_at": "2026-07-23T12:01:12Z", | ||
| "conversation_ended_at": "2026-07-23T12:04:16Z", | ||
| "duration_seconds": 184, | ||
| "end_user_id": "customer-42", | ||
| "metadata": { "order_ref": "SO-1042" }, | ||
| "agent_name": "Support agent", | ||
| "config_hash": "sha256:9c41…" | ||
| }, | ||
| "ended_reason": "user_hangup" | ||
| } | ||
| ``` | ||
|
|
||
|
|
@@ -280,14 +280,14 @@ | |
| | Fan-out | Every configured endpoint receives every event | | ||
| | Guarantee | At-least-once, per endpoint | | ||
| | Timeout | 10 seconds per attempt | | ||
| | Retries | 2 after the first attempt (3 attempts total) per endpoint, with backoff of 1s / 5s, then that delivery is dropped | | ||
| | Ordering | `phone_call.dial_finished` (outbound only) before `call.ended` before `call.analyzed` for the same session | | ||
|
|
||
| Endpoints are delivered in parallel and independently: each gets its own attempts, its own retry budget, and its own signature keyed with its own secret. An endpoint that is down and exhausts all three attempts has no effect on the others. | ||
|
|
||
| Respond with a `2xx` status within the timeout; a `500` response or a timed-out request counts as a failed attempt. Acknowledge first and process asynchronously. Slow handlers burn their own retry budget. | ||
|
|
||
| **Idempotency.** At-least-once delivery means the same event can arrive more than once. Retries of one delivery carry an identical body, so dedupe `call.ended` and `phone_call.dial_finished` on (`event`, `session.id`), and `call.analyzed` on (`event`, `session.id`, `analysis.finished_at`). The extra element matters because a skipped or failed analysis can be re-run from the console: the recovered result arrives as a fresh `call.analyzed` with a newer `finished_at`, superseding the earlier one. | ||
|
Check warning on line 290 in agents/monitor/webhooks.mdx
|
||
|
|
||
| <Note> | ||
| [Preview calls](/agents/test/preview-calls) made from the Builder never | ||
|
|
@@ -341,11 +341,11 @@ | |
| } | ||
| ``` | ||
|
|
||
| `caller_number` and `dialed_number` are E.164. `caller_number` is an empty string when the carrier withholds the caller's number, and `twilio_call_sid` is empty when the call did not come through a Twilio number. | ||
|
|
||
| ### Response | ||
|
|
||
| Respond with a `2xx` status and a JSON object whose `dynamic_variables` follow the same rules as on session creation: names match `[A-Za-z][A-Za-z0-9_]*`, values are strings (up to 100,000 characters), numbers, or booleans, and at most 500 entries totaling 1 MiB are read. | ||
|
|
||
| ```json Response | ||
| { | ||
|
|
@@ -357,7 +357,7 @@ | |
| } | ||
| ``` | ||
|
|
||
| An entry that breaks a rule is dropped on its own; the call still connects with the rest. Other fields in the response are ignored, so the contract can grow without breaking deployed endpoints. Placeholders that receive no value stay in the text as written, as described under [dynamic variables](/agents/build/dynamic-variables#custom-variables). | ||
| An entry that breaks a rule is dropped on its own; the call still connects with the rest. Other fields in the response are ignored, so the contract can grow without breaking deployed endpoints. A placeholder that receives no value takes the agent's configured [default value](/agents/build/dynamic-variables#default-values), and follows the agent's `on_missing` setting when there is none. | ||
|
|
||
| ```text System prompt | ||
| You are the support line for Acme. The caller is {{customer_name}} on the {{plan}} plan | ||
|
|
@@ -366,18 +366,18 @@ | |
|
|
||
| ### Failure behavior | ||
|
|
||
| The endpoint can personalize a call but never block one. The caller is waiting while the request runs, so keep the handler fast and the timeout short. | ||
| The endpoint can personalize a call but never block one. The caller is waiting while the request runs, so keep the handler fast and the timeout short. Whenever the call connects without your variables, every placeholder falls back to its configured [default value](/agents/build/dynamic-variables#default-values), so give each one a default that reads well on its own. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win State when webhook failure can prevent connection. Both guides say a failed webhook cannot block a call. They also describe
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
|
|
||
| | Outcome | Effect | | ||
| | ---------------------------------------- | ------------------------------------------------------------- | | ||
| | Response within the timeout | Variables are rendered into the prompt, first message, and webhook tool requests | | ||
| | Timeout | The call connects without variables; the request is not retried | | ||
| | Connection error or non-`2xx` response | One immediate retry, then the call connects without variables | | ||
| | Body is not a JSON object | The call connects without variables | | ||
| | Timeout | The call connects on the defaults; the request is not retried | | ||
| | Connection error or non-`2xx` response | One immediate retry, then the call connects on the defaults | | ||
| | Body is not a JSON object | The call connects on the defaults | | ||
|
|
||
| ## Auto-ticket unresolved calls | ||
|
|
||
| `call.analyzed` closes the loop on conversations the agent couldn't: judge every call with a success criterion, and open a ticket in your helpdesk whenever the verdict isn't `success`. | ||
|
|
||
| First give the agent's [analysis configuration](/agents/monitor/post-call-analysis) a criterion that captures resolution: | ||
|
|
||
|
|
@@ -498,11 +498,11 @@ | |
|
|
||
| </CodeGroup> | ||
|
|
||
| `verifyWebhook` is the function from [Verify the signature](#verify-the-signature); `openTicket` stands in for your helpdesk's API. Escalating on anything but `success` includes `unknown` verdicts: the model couldn't judge the call, which usually deserves human eyes too. Tighten the check to `failure` only if unknowns prove noisy. | ||
|
|
||
| Edges worth handling: | ||
|
|
||
| - Calls with nothing to analyze arrive with `analysis.status: "skipped"`. The handler above tickets them as unjudged, so every call reaches the helpdesk without also watching `call.ended`. Drop that branch if silent calls don't belong in your queue. | ||
|
Check warning on line 505 in agents/monitor/webhooks.mdx
|
||
| - Payloads carry no transcript. To include one in the ticket, fetch `GET /v1/agent/sessions/{session_id}` from your handler. See [conversation history](/agents/monitor/conversation-history). | ||
| - Set `end_user_id` and `metadata` when creating sessions so tickets attach to the right customer record without a lookup. | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: fishaudio/docs
Length of output: 41582
Resolve the
variables.defaultsPATCH semantics before publishing.agents/build/configuration.mdx:133says PATCH deep-merges partial bodies and replaces only list fields as a whole.variables.defaultsis an object, butagents/build/dynamic-variables.mdx:74says the map is replaced as a whole. A partial defaults update can therefore retain old keys, contrary to the documented behavior. Confirm the endpoint contract and align both pages. If defaults are deep-merged, document how to remove keys; if they replace the map, document that exception in the config API section.🤖 Prompt for AI Agents