From ca821fc70aea145799e83b35efa921ad95e2561b Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Aug 2026 04:45:14 +0900 Subject: [PATCH 1/7] docs(agents): system variables ({{system.caller_number}}, channel, timezone, today, language) Document the reserved system.* namespace: the full variable table with values per session type, where they render (prompt, first message, voicemail, webhook tool url / headers / body), the empty-on-web rule for phone facts, the 422 on unknown names, why there is no system.now or system.session_id, plus pointers from the inbound / outbound call, webhook tool, and time & timezone pages. Co-Authored-By: Claude Fable 5 --- agents/build/dynamic-variables.mdx | 39 ++++++++++++++++++++++++++--- agents/build/time-timezone.mdx | 2 ++ agents/build/webhook-tools.mdx | 4 +-- agents/telephony/inbound-calls.mdx | 6 +++++ agents/telephony/outbound-calls.mdx | 2 +- 5 files changed, 47 insertions(+), 6 deletions(-) diff --git a/agents/build/dynamic-variables.mdx b/agents/build/dynamic-variables.mdx index 1fabc34..fd8fc35 100644 --- a/agents/build/dynamic-variables.mdx +++ b/agents/build/dynamic-variables.mdx @@ -11,7 +11,7 @@ You are a support agent for {{company}}. The caller's name is {{customer_name}} and they are on the {{plan}} plan. Greet them by name. ``` -Pass values as a flat object of strings, numbers, or booleans. Variable names must match `[A-Za-z][A-Za-z0-9_]*` (no hyphens or dots), string values are capped at 1,000 characters, and a request can carry at most 50 variables. Violations reject session creation with `422`: +Pass values as a flat object of strings, numbers, or booleans. Variable names must match `[A-Za-z][A-Za-z0-9_]*` (no hyphens or dots; the dotted `system.*` names are reserved for [system variables](#system-variables)), string values are capped at 1,000 characters, and a request can carry at most 50 variables. Violations reject session creation with `422`: ```bash API (curl) @@ -40,7 +40,7 @@ const session = await AgentSession.start({ -Placeholders with no matching variable are **not** removed: the literal `{{variable_name}}` text stays in the prompt, visible to the model. Make sure every placeholder in your configuration has a value at session creation. +Placeholders with no matching variable are **not** removed: the literal `{{variable_name}}` text stays in the prompt, visible to the model. Make sure every placeholder in your configuration has a value at session creation. [System variables](#system-variables) are the exception: the platform always fills them. @@ -48,11 +48,44 @@ Session records never store dynamic variable values. -You don't need a variable for the current date or time. The agent already knows both, in the session's timezone. See [Time & timezone](/agents/build/time-timezone). +You don't need a variable for the current date or time. The agent already knows both, in the session's timezone. See [Time & timezone](/agents/build/time-timezone). When you do want the date in a template, `{{system.today}}` is [filled for you](#system-variables). Variables fill placeholders in the configured text. To replace whole configuration fields for a session (the prompt itself, the opener, voice, language), use [overrides](/agents/deploy/authenticated-sessions#overrides) on the same request; `{{placeholders}}` render inside overridden text too. +## System variables + +The platform fills a handful of `{{system.*}}` placeholders itself, on every session. You cannot supply or override them: the names you pass in `dynamic_variables` cannot contain a dot, so the two namespaces never collide. + +| Variable | Value | Notes | +|---|---|---| +| `system.channel` | `phone_inbound`, `phone_outbound`, or `web_voice` | Web SDK, API, preview, and agent test sessions are all `web_voice`. `web_text` is reserved and never emitted today. | +| `system.timezone` | The session's resolved IANA timezone, for example `Asia/Shanghai` | `UTC` when nothing resolves. Always equals the session's `timezone` field; see [which timezone a session uses](/agents/build/time-timezone#which-timezone-a-session-uses). | +| `system.today` | Today's date in that timezone, ISO 8601 `YYYY-MM-DD` | The calendar date at session creation. There is no `system.now`; see below. | +| `system.language` | The session language id: `en`, `ja`, `zh`, `ko`, `es`, `fr`, `de` | The `language` [override](/agents/deploy/authenticated-sessions#overrides) when the request carries one, otherwise the agent's [speaking language](/agents/build/voice-language#speaking-language). | +| `system.caller_number` | The human party's number, E.164 | Inbound: the caller (empty if withheld or not a valid E.164 number). Outbound: the number you dialed. Empty on non-phone sessions. | +| `system.dialed_number` | Your workspace number, E.164 | Inbound: the number that was called. Outbound: the number the call was placed from. Empty on non-phone sessions. | + +The phone numbers are the session's `caller_number` and `dialed_number` fields as shown in [session history](/agents/telephony/inbound-calls#phone-sessions-in-history). Only a value that is a well-formed E.164 number reaches the agent: anything else the carrier sends as the caller id (for example an `anonymous` marker) renders as an empty string, while the history field keeps the raw value. On every session that is not a phone call (web, text, preview, agent tests) both render as an empty string, never as literal `{{system.caller_number}}` text, so one agent can serve web and phone with the same prompt. + +`system.today` is deliberately a date, not a time. Variables render once, when the session is created, so a `system.now` would be stale from the first reply onward. You don't need it: the agent already knows the current time, refreshed on every turn, in the session's timezone (see [Time & timezone](/agents/build/time-timezone)). Reach for `system.today` and `system.timezone` when you want to template them explicitly, for example into a webhook tool's query string. The date is always ISO formatted, whatever the session language: in the system prompt the model reads it and speaks it in the session's language, while a fixed first message containing `{{system.today}}` is spoken verbatim as `2026-08-21`. A session that runs past midnight keeps its opening date. + +System variables render wherever dynamic variables do (system prompt, first message, the outbound voicemail message, overridden text), and also inside [webhook tool](/agents/build/webhook-tools#argument-templating) requests: the URL, header values, and body template. Dynamic variables you supply do not render into tools. In the URL the value is URL-encoded, like argument values: the leading `+` of an E.164 number travels as `%2B` and the `/` in a timezone name as `%2F`, both decoding back on your server. + +```text System prompt +The caller is calling from {{system.caller_number}}. Confirm the last four digits before discussing the account. +``` + +```text Webhook tool URL +https://crm.example.com/availability?phone={{system.caller_number}}&from={{system.today}}&tz={{system.timezone}} +``` + +A reference to a system variable that does not exist (`{{system.foo}}`) is rejected with `422` when you save the configuration or the tool, and when you send it in session overrides. The error lists the available names. + + +There is no `system.session_id`. The configuration is rendered before the session receives its id, so the id cannot be templated into it. Read it from the `POST /v1/agent/sessions` [response](/agents/deploy/authenticated-sessions), the SDK's `connect` event, or the `session` object in [webhooks](/agents/monitor/webhooks). + + ## Who supplies the values Where variable values come from depends on the session's [access mode](/agents/deploy/overview#who-may-start-sessions): diff --git a/agents/build/time-timezone.mdx b/agents/build/time-timezone.mdx index d37d9f3..fae1fad 100644 --- a/agents/build/time-timezone.mdx +++ b/agents/build/time-timezone.mdx @@ -22,6 +22,8 @@ The time is refreshed on every turn, so it stays accurate through long conversat system prompt's character limit. +Both facts also exist as [system variables](/agents/build/dynamic-variables#system-variables) for the cases where you want to template them yourself, for example into a webhook tool's URL: `{{system.today}}` is the date at session creation as ISO `YYYY-MM-DD`, and `{{system.timezone}}` is the resolved IANA name (`UTC` when nothing resolves). There is no `{{system.now}}`: variables render once, when the session is created, so a templated clock would freeze at the opening. The per-turn time above is the one to rely on. + ## Which timezone a session uses The timezone is resolved once, when the session is created, taking the first that applies: diff --git a/agents/build/webhook-tools.mdx b/agents/build/webhook-tools.mdx index 3e93019..a93aa72 100644 --- a/agents/build/webhook-tools.mdx +++ b/agents/build/webhook-tools.mdx @@ -37,7 +37,7 @@ In the Builder, open **Tools** and choose **Add tool → Webhook**. A tool creat ## Argument templating -Reference any declared argument with `{{name}}`: in the URL, in the body template, or both. The platform substitutes the values the agent supplies before sending the request. +Reference any declared argument with `{{name}}`: in the URL, in header values, or in the body template. The platform substitutes the values the agent supplies before sending the request. [System variables](/agents/build/dynamic-variables#system-variables) such as `{{system.caller_number}}` or `{{system.today}}` are available in the same places; they are filled when the session starts, before the agent supplies any argument. ```text Endpoint URL https://api.example.com/orders/{{order_number}} @@ -114,7 +114,7 @@ A tool can store mock responses: canned payloads, each with a `name`, a `status_ ## Test your tool -The tool editor's **Test** tab fires a real request at your endpoint. Fill in the arguments as JSON (pre-filled with a sample based on your declared arguments) and send. You get back the status code, latency, response headers, and response body. A failing test never blocks saving the tool. +The tool editor's **Test** tab fires a real request at your endpoint. Fill in the arguments as JSON (pre-filled with a sample based on your declared arguments) and send. You get back the status code, latency, response headers, and response body. A failing test never blocks saving the tool. There is no live call behind a test, so `{{system.*}}` placeholders have no value: in header values and the body template they render empty unless you add the values to the arguments JSON as a nested object, for example `{"system": {"caller_number": "+15551234567"}}`. The URL is sent exactly as saved; placeholders in it are not substituted by a test request. Response bodies are captured up to 64 KB; larger bodies are cut off and flagged with `response_truncated`. diff --git a/agents/telephony/inbound-calls.mdx b/agents/telephony/inbound-calls.mdx index 5fa9fe4..bed6c38 100644 --- a/agents/telephony/inbound-calls.mdx +++ b/agents/telephony/inbound-calls.mdx @@ -104,6 +104,12 @@ curl --request GET "https://api.fish.audio/v1/agent/sessions?agent_id=YOUR_AGENT You can combine `caller_number` with the other list filters (`agent_id`, `status`, `created_after`, `created_before`); see [Conversation history](/agents/monitor/conversation-history) for the full parameter list. +The agent can read both numbers too, as `{{system.caller_number}}` and `{{system.dialed_number}}` in its system prompt, first message, and webhook tool requests. No variable needs to be passed; see [System variables](/agents/build/dynamic-variables#system-variables). + +```text System prompt +The caller is calling from {{system.caller_number}}. Confirm the last four digits before discussing the account. +``` + ## Phone sessions behave like any session Nothing about a phone call needs special handling downstream: diff --git a/agents/telephony/outbound-calls.mdx b/agents/telephony/outbound-calls.mdx index 394edbb..fa944dc 100644 --- a/agents/telephony/outbound-calls.mdx +++ b/agents/telephony/outbound-calls.mdx @@ -97,7 +97,7 @@ Two session fields carry the outcome: | `dial_status` | `answered`, `busy`, `no_answer`, or `failed`. `null` while the call is still ringing, and on inbound calls. | | `answered_by` | What answering-machine detection heard: `human`, `voicemail`, or `unknown`. `null` unless answered. | -On outbound sessions the attribution fields are person-centric: `caller_number` is the human you dialed and `dialed_number` is your workspace number. Everything else about the session (transcript, recording, [post-call analysis](/agents/monitor/post-call-analysis), `call.ended` and `call.analyzed` webhooks, hang-up via `POST /v1/agent/sessions/{session_id}/end`) works exactly as for [inbound calls](/agents/telephony/inbound-calls). +On outbound sessions the attribution fields are person-centric: `caller_number` is the human you dialed and `dialed_number` is your workspace number. The same values reach the agent as `{{system.caller_number}}` and `{{system.dialed_number}}`, so a CRM lookup tool can use `https://crm.example.com/contacts?phone={{system.caller_number}}` without a per-call variable; see [System variables](/agents/build/dynamic-variables#system-variables). Everything else about the session (transcript, recording, [post-call analysis](/agents/monitor/post-call-analysis), `call.ended` and `call.analyzed` webhooks, hang-up via `POST /v1/agent/sessions/{session_id}/end`) works exactly as for [inbound calls](/agents/telephony/inbound-calls). ## Allowed destinations From 8bb9ba43bcfa16f6d605b3243413d1d9f68bc0f0 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Aug 2026 14:19:55 +0900 Subject: [PATCH 2/7] docs(agents): drop the web_text remark from the system.channel row Co-Authored-By: Claude Fable 5 --- agents/build/dynamic-variables.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agents/build/dynamic-variables.mdx b/agents/build/dynamic-variables.mdx index fd8fc35..b44b86f 100644 --- a/agents/build/dynamic-variables.mdx +++ b/agents/build/dynamic-variables.mdx @@ -59,7 +59,7 @@ The platform fills a handful of `{{system.*}}` placeholders itself, on every ses | Variable | Value | Notes | |---|---|---| -| `system.channel` | `phone_inbound`, `phone_outbound`, or `web_voice` | Web SDK, API, preview, and agent test sessions are all `web_voice`. `web_text` is reserved and never emitted today. | +| `system.channel` | `phone_inbound`, `phone_outbound`, or `web_voice` | Web SDK, API, preview, and agent test sessions are all `web_voice`. | | `system.timezone` | The session's resolved IANA timezone, for example `Asia/Shanghai` | `UTC` when nothing resolves. Always equals the session's `timezone` field; see [which timezone a session uses](/agents/build/time-timezone#which-timezone-a-session-uses). | | `system.today` | Today's date in that timezone, ISO 8601 `YYYY-MM-DD` | The calendar date at session creation. There is no `system.now`; see below. | | `system.language` | The session language id: `en`, `ja`, `zh`, `ko`, `es`, `fr`, `de` | The `language` [override](/agents/deploy/authenticated-sessions#overrides) when the request carries one, otherwise the agent's [speaking language](/agents/build/voice-language#speaking-language). | From 275df2b38b2a53847a51b7bec74bf3969f75062f Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Aug 2026 14:22:07 +0900 Subject: [PATCH 3/7] docs(agents): split Dynamic Variables into Custom variables and System variables Co-Authored-By: Claude Fable 5 --- agents/build/dynamic-variables.mdx | 97 ++++++++++++++++++------------ 1 file changed, 57 insertions(+), 40 deletions(-) diff --git a/agents/build/dynamic-variables.mdx b/agents/build/dynamic-variables.mdx index b44b86f..116c927 100644 --- a/agents/build/dynamic-variables.mdx +++ b/agents/build/dynamic-variables.mdx @@ -1,10 +1,19 @@ --- title: "Dynamic Variables" -description: "Personalize each session with template variables supplied at creation time" +description: "Personalize each session with template variables: your own values, and facts the platform fills in" icon: "brackets-curly" --- -An agent's published configuration is shared by every caller. Dynamic variables personalize it per session. Write `{{variable_name}}` placeholders in your agent's system prompt or first message and supply values when you create the session. Substitution happens once, when the published configuration is assembled for the session. +An agent's published configuration is shared by every caller. Dynamic variables personalize it per session: write `{{name}}` placeholders in the configuration and they are substituted once, when the published configuration is assembled for the session. There are two kinds: + +- **[Custom variables](#custom-variables)**: values you supply when you create the session, such as the caller's name or plan. +- **[System variables](#system-variables)**: facts about the session itself, such as the caller's phone number or the date, filled in by the platform under the reserved `system.*` names. + +Both render in the system prompt, the first message, the outbound voicemail message, and in [overridden](/agents/deploy/authenticated-sessions#overrides) text. System variables additionally render inside webhook tool requests. + +## Custom variables + +Write `{{variable_name}}` placeholders in your agent's system prompt or first message and supply values when you create the session: ```text System prompt You are a support agent for {{company}}. The caller's name is {{customer_name}} @@ -40,55 +49,22 @@ const session = await AgentSession.start({ -Placeholders with no matching variable are **not** removed: the literal `{{variable_name}}` text stays in the prompt, visible to the model. Make sure every placeholder in your configuration has a value at session creation. [System variables](#system-variables) are the exception: the platform always fills them. +Placeholders with no matching variable are **not** removed: the literal `{{variable_name}}` text stays in the prompt, visible to the model. Make sure every placeholder in your configuration has a value at session creation. -Session records never store dynamic variable values. +Session records never store custom variable values. -You don't need a variable for the current date or time. The agent already knows both, in the session's timezone. See [Time & timezone](/agents/build/time-timezone). When you do want the date in a template, `{{system.today}}` is [filled for you](#system-variables). +You don't need a custom variable for the current date or time. The agent already knows both, in the session's timezone. See [Time & timezone](/agents/build/time-timezone). When you do want the date in a template, `{{system.today}}` is [filled for you](#system-variables). Variables fill placeholders in the configured text. To replace whole configuration fields for a session (the prompt itself, the opener, voice, language), use [overrides](/agents/deploy/authenticated-sessions#overrides) on the same request; `{{placeholders}}` render inside overridden text too. -## System variables - -The platform fills a handful of `{{system.*}}` placeholders itself, on every session. You cannot supply or override them: the names you pass in `dynamic_variables` cannot contain a dot, so the two namespaces never collide. - -| Variable | Value | Notes | -|---|---|---| -| `system.channel` | `phone_inbound`, `phone_outbound`, or `web_voice` | Web SDK, API, preview, and agent test sessions are all `web_voice`. | -| `system.timezone` | The session's resolved IANA timezone, for example `Asia/Shanghai` | `UTC` when nothing resolves. Always equals the session's `timezone` field; see [which timezone a session uses](/agents/build/time-timezone#which-timezone-a-session-uses). | -| `system.today` | Today's date in that timezone, ISO 8601 `YYYY-MM-DD` | The calendar date at session creation. There is no `system.now`; see below. | -| `system.language` | The session language id: `en`, `ja`, `zh`, `ko`, `es`, `fr`, `de` | The `language` [override](/agents/deploy/authenticated-sessions#overrides) when the request carries one, otherwise the agent's [speaking language](/agents/build/voice-language#speaking-language). | -| `system.caller_number` | The human party's number, E.164 | Inbound: the caller (empty if withheld or not a valid E.164 number). Outbound: the number you dialed. Empty on non-phone sessions. | -| `system.dialed_number` | Your workspace number, E.164 | Inbound: the number that was called. Outbound: the number the call was placed from. Empty on non-phone sessions. | - -The phone numbers are the session's `caller_number` and `dialed_number` fields as shown in [session history](/agents/telephony/inbound-calls#phone-sessions-in-history). Only a value that is a well-formed E.164 number reaches the agent: anything else the carrier sends as the caller id (for example an `anonymous` marker) renders as an empty string, while the history field keeps the raw value. On every session that is not a phone call (web, text, preview, agent tests) both render as an empty string, never as literal `{{system.caller_number}}` text, so one agent can serve web and phone with the same prompt. - -`system.today` is deliberately a date, not a time. Variables render once, when the session is created, so a `system.now` would be stale from the first reply onward. You don't need it: the agent already knows the current time, refreshed on every turn, in the session's timezone (see [Time & timezone](/agents/build/time-timezone)). Reach for `system.today` and `system.timezone` when you want to template them explicitly, for example into a webhook tool's query string. The date is always ISO formatted, whatever the session language: in the system prompt the model reads it and speaks it in the session's language, while a fixed first message containing `{{system.today}}` is spoken verbatim as `2026-08-21`. A session that runs past midnight keeps its opening date. - -System variables render wherever dynamic variables do (system prompt, first message, the outbound voicemail message, overridden text), and also inside [webhook tool](/agents/build/webhook-tools#argument-templating) requests: the URL, header values, and body template. Dynamic variables you supply do not render into tools. In the URL the value is URL-encoded, like argument values: the leading `+` of an E.164 number travels as `%2B` and the `/` in a timezone name as `%2F`, both decoding back on your server. - -```text System prompt -The caller is calling from {{system.caller_number}}. Confirm the last four digits before discussing the account. -``` - -```text Webhook tool URL -https://crm.example.com/availability?phone={{system.caller_number}}&from={{system.today}}&tz={{system.timezone}} -``` - -A reference to a system variable that does not exist (`{{system.foo}}`) is rejected with `422` when you save the configuration or the tool, and when you send it in session overrides. The error lists the available names. - - -There is no `system.session_id`. The configuration is rendered before the session receives its id, so the id cannot be templated into it. Read it from the `POST /v1/agent/sessions` [response](/agents/deploy/authenticated-sessions), the SDK's `connect` event, or the `session` object in [webhooks](/agents/monitor/webhooks). - - -## Who supplies the values +### Who supplies the values -Where variable values come from depends on the session's [access mode](/agents/deploy/overview#who-may-start-sessions): +Where custom variable values come from depends on the session's [access mode](/agents/deploy/overview#who-may-start-sessions): | Mode | Who creates the session | Who supplies the values | |---|---|---| @@ -117,6 +93,47 @@ const sessionToken = await response.json(); // pass to AgentSession.start({ sess In `agentId` mode, values arrive from the end user's browser. Treat them as untrusted input, and use `sessionToken` mode when personalization must come from data only your backend knows. +## System variables + +The platform fills a handful of `{{system.*}}` placeholders itself, on every session. You cannot supply or override them: the names you pass in `dynamic_variables` cannot contain a dot, so the two namespaces never collide. + +| Variable | Value | Notes | +|---|---|---| +| `system.channel` | `phone_inbound`, `phone_outbound`, or `web_voice` | Web SDK, API, preview, and agent test sessions are all `web_voice`. | +| `system.timezone` | The session's resolved IANA timezone, for example `Asia/Shanghai` | `UTC` when nothing resolves. Always equals the session's `timezone` field; see [which timezone a session uses](/agents/build/time-timezone#which-timezone-a-session-uses). | +| `system.today` | Today's date in that timezone, ISO 8601 `YYYY-MM-DD` | The calendar date at session creation. There is no `system.now`; see below. | +| `system.language` | The session language id: `en`, `ja`, `zh`, `ko`, `es`, `fr`, `de` | The `language` [override](/agents/deploy/authenticated-sessions#overrides) when the request carries one, otherwise the agent's [speaking language](/agents/build/voice-language#speaking-language). | +| `system.caller_number` | The human party's number, E.164 | Inbound: the caller (empty if withheld or not a valid E.164 number). Outbound: the number you dialed. Empty on non-phone sessions. | +| `system.dialed_number` | Your workspace number, E.164 | Inbound: the number that was called. Outbound: the number the call was placed from. Empty on non-phone sessions. | + +```text System prompt +The caller is calling from {{system.caller_number}}. Confirm the last four digits before discussing the account. +``` + +### Phone numbers + +The phone numbers are the session's `caller_number` and `dialed_number` fields as shown in [session history](/agents/telephony/inbound-calls#phone-sessions-in-history). Only a value that is a well-formed E.164 number reaches the agent: anything else the carrier sends as the caller id (for example an `anonymous` marker) renders as an empty string, while the history field keeps the raw value. On every session that is not a phone call (web, text, preview, agent tests) both render as an empty string, never as literal `{{system.caller_number}}` text, so one agent can serve web and phone with the same prompt. + +### Date and timezone + +`system.today` is deliberately a date, not a time. Variables render once, when the session is created, so a `system.now` would be stale from the first reply onward. You don't need it: the agent already knows the current time, refreshed on every turn, in the session's timezone (see [Time & timezone](/agents/build/time-timezone)). Reach for `system.today` and `system.timezone` when you want to template them explicitly, for example into a webhook tool's query string. The date is always ISO formatted, whatever the session language: in the system prompt the model reads it and speaks it in the session's language, while a fixed first message containing `{{system.today}}` is spoken verbatim as `2026-08-21`. A session that runs past midnight keeps its opening date. + +### In webhook tools + +System variables render wherever custom variables do, and also inside [webhook tool](/agents/build/webhook-tools#argument-templating) requests: the URL, header values, and body template. Custom variables do not render into tools. In the URL the value is URL-encoded, like argument values: the leading `+` of an E.164 number travels as `%2B` and the `/` in a timezone name as `%2F`, both decoding back on your server. + +```text Webhook tool URL +https://crm.example.com/availability?phone={{system.caller_number}}&from={{system.today}}&tz={{system.timezone}} +``` + +### Unknown names + +A reference to a system variable that does not exist (`{{system.foo}}`) is rejected with `422` when you save the configuration or the tool, and when you send it in session overrides. The error lists the available names. + + +There is no `system.session_id`. The configuration is rendered before the session receives its id, so the id cannot be templated into it. Read it from the `POST /v1/agent/sessions` [response](/agents/deploy/authenticated-sessions), the SDK's `connect` event, or the `session` object in [webhooks](/agents/monitor/webhooks). + + ## Going further From d10a288ee622d89a518b68bc689e42a226967848 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Aug 2026 14:44:25 +0900 Subject: [PATCH 4/7] docs(agents): point the configuration page at dynamic variables Co-Authored-By: Claude Fable 5 --- agents/build/configuration.mdx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/agents/build/configuration.mdx b/agents/build/configuration.mdx index 6bf8eb9..9a3ec56 100644 --- a/agents/build/configuration.mdx +++ b/agents/build/configuration.mdx @@ -10,6 +10,8 @@ Configuration is the Builder's home tab: define how your agent behaves, how it o The system prompt sets the personality, goals, and guardrails that steer every reply. It is capped at **4,000 tokens**. The API rejects longer prompts with `422 Unprocessable Entity`. Keeping it under **2,000 tokens** is recommended: shorter prompts cut latency and cost, and hold the model's attention better. It can also be replaced for a single session by sending `overrides.system_prompt` when [creating the session](/agents/deploy/authenticated-sessions#overrides). +To personalize the prompt per session without replacing it, write `{{placeholders}}`: [custom variables](/agents/build/dynamic-variables#custom-variables) such as `{{customer_name}}` take the values you pass when creating the session, and [system variables](/agents/build/dynamic-variables#system-variables) such as `{{system.caller_number}}` or `{{system.today}}` are filled in by the platform. + Keep the prompt focused: @@ -28,7 +30,7 @@ Choose how the agent opens each conversation: | **Fixed message** | The agent opens with the exact text you provide, every time. | `first_message` | | **Prompt** | The agent generates its opening line from instructions you provide. | `first_message_prompt` | -`first_message` and `first_message_prompt` can each hold up to 10,000 characters. The active mode (`first_message_mode`: `off`, `fixed`, or `prompt`) decides what callers hear. Both fields can be replaced for a single session by sending `overrides.first_message` or `overrides.first_message_prompt` when [creating the session](/agents/deploy/authenticated-sessions#overrides). +`first_message` and `first_message_prompt` can each hold up to 10,000 characters. The active mode (`first_message_mode`: `off`, `fixed`, or `prompt`) decides what callers hear. Both fields accept the same `{{placeholders}}` as the system prompt (see [Dynamic variables](/agents/build/dynamic-variables)), and both can be replaced for a single session by sending `overrides.first_message` or `overrides.first_message_prompt` when [creating the session](/agents/deploy/authenticated-sessions#overrides). ## Voice @@ -153,4 +155,7 @@ The response includes the draft's new `config_hash`. Values outside the document Let the agent call your backend mid-conversation. + + Personalize the prompt and first message per session. + From 670c107d6068946fc7e18b43308f9899c7f87fca Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Aug 2026 14:46:46 +0900 Subject: [PATCH 5/7] docs(agents): where dynamic variables render Co-Authored-By: Claude Fable 5 --- agents/build/dynamic-variables.mdx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/agents/build/dynamic-variables.mdx b/agents/build/dynamic-variables.mdx index 116c927..346db98 100644 --- a/agents/build/dynamic-variables.mdx +++ b/agents/build/dynamic-variables.mdx @@ -9,7 +9,19 @@ An agent's published configuration is shared by every caller. Dynamic variables - **[Custom variables](#custom-variables)**: values you supply when you create the session, such as the caller's name or plan. - **[System variables](#system-variables)**: facts about the session itself, such as the caller's phone number or the date, filled in by the platform under the reserved `system.*` names. -Both render in the system prompt, the first message, the outbound voicemail message, and in [overridden](/agents/deploy/authenticated-sessions#overrides) text. System variables additionally render inside webhook tool requests. +## Where variables render + +Placeholders are substituted in the agent's configured text and in webhook tool requests. Everything else (knowledge base content, tool names and descriptions, post-call analysis prompts) is left as written. + +| Where | Custom variables | System variables | +|---|---|---| +| System prompt (`prompt.system_prompt`) | Yes | Yes | +| First message, fixed text or prompt (`prompt.first_message`, `prompt.first_message_prompt`) | Yes | Yes | +| Session [overrides](/agents/deploy/authenticated-sessions#overrides) of those fields (`overrides.system_prompt`, `overrides.first_message`, `overrides.first_message_prompt`) | Yes | Yes | +| Outbound voicemail message (`conversation.outbound.voicemail.message`) | Yes | Yes | +| [Webhook tool](/agents/build/webhook-tools#argument-templating) URL, header values, and body template | No | Yes | + +Custom values travel with the request that starts the session: `dynamic_variables` on [`POST /v1/agent/sessions`](/agents/deploy/authenticated-sessions), `dynamicVariables` in the SDK's `AgentSession.start()` for [public agents](/agents/deploy/public-agents), `dynamic_variables` on [`POST /v1/agent/phone-calls`](/agents/telephony/outbound-calls#request-fields) for outbound calls, and the variables panel of a [preview call](/agents/test/preview-calls) in the Builder. Inbound phone calls have no such request: values come from the inbound call webhook on the agent's Webhooks tab when one is configured; the caller's number and the other session facts are always available as system variables. ## Custom variables From 63683bc838b0226bf27ace2012edebabb032b7b7 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Aug 2026 14:59:05 +0900 Subject: [PATCH 6/7] docs(agents): dynamic variables page gets a World context section Co-Authored-By: Claude Fable 5 --- agents/build/dynamic-variables.mdx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/agents/build/dynamic-variables.mdx b/agents/build/dynamic-variables.mdx index 346db98..61d084e 100644 --- a/agents/build/dynamic-variables.mdx +++ b/agents/build/dynamic-variables.mdx @@ -64,14 +64,6 @@ const session = await AgentSession.start({ Placeholders with no matching variable are **not** removed: the literal `{{variable_name}}` text stays in the prompt, visible to the model. Make sure every placeholder in your configuration has a value at session creation. - -Session records never store custom variable values. - - - -You don't need a custom variable for the current date or time. The agent already knows both, in the session's timezone. See [Time & timezone](/agents/build/time-timezone). When you do want the date in a template, `{{system.today}}` is [filled for you](#system-variables). - - Variables fill placeholders in the configured text. To replace whole configuration fields for a session (the prompt itself, the opener, voice, language), use [overrides](/agents/deploy/authenticated-sessions#overrides) on the same request; `{{placeholders}}` render inside overridden text too. ### Who supplies the values @@ -113,7 +105,7 @@ The platform fills a handful of `{{system.*}}` placeholders itself, on every ses |---|---|---| | `system.channel` | `phone_inbound`, `phone_outbound`, or `web_voice` | Web SDK, API, preview, and agent test sessions are all `web_voice`. | | `system.timezone` | The session's resolved IANA timezone, for example `Asia/Shanghai` | `UTC` when nothing resolves. Always equals the session's `timezone` field; see [which timezone a session uses](/agents/build/time-timezone#which-timezone-a-session-uses). | -| `system.today` | Today's date in that timezone, ISO 8601 `YYYY-MM-DD` | The calendar date at session creation. There is no `system.now`; see below. | +| `system.today` | Today's date in that timezone, ISO 8601 `YYYY-MM-DD` | The calendar date at session creation. There is no `system.now`; see [World context](#world-context). | | `system.language` | The session language id: `en`, `ja`, `zh`, `ko`, `es`, `fr`, `de` | The `language` [override](/agents/deploy/authenticated-sessions#overrides) when the request carries one, otherwise the agent's [speaking language](/agents/build/voice-language#speaking-language). | | `system.caller_number` | The human party's number, E.164 | Inbound: the caller (empty if withheld or not a valid E.164 number). Outbound: the number you dialed. Empty on non-phone sessions. | | `system.dialed_number` | Your workspace number, E.164 | Inbound: the number that was called. Outbound: the number the call was placed from. Empty on non-phone sessions. | @@ -128,7 +120,7 @@ The phone numbers are the session's `caller_number` and `dialed_number` fields a ### Date and timezone -`system.today` is deliberately a date, not a time. Variables render once, when the session is created, so a `system.now` would be stale from the first reply onward. You don't need it: the agent already knows the current time, refreshed on every turn, in the session's timezone (see [Time & timezone](/agents/build/time-timezone)). Reach for `system.today` and `system.timezone` when you want to template them explicitly, for example into a webhook tool's query string. The date is always ISO formatted, whatever the session language: in the system prompt the model reads it and speaks it in the session's language, while a fixed first message containing `{{system.today}}` is spoken verbatim as `2026-08-21`. A session that runs past midnight keeps its opening date. +`system.today` is always ISO formatted, whatever the session language: in the system prompt the model reads it and speaks it in the session's language, while a fixed first message containing `{{system.today}}` is spoken verbatim as `2026-08-21`. A session that runs past midnight keeps its opening date. Template it, and `system.timezone`, when a request needs them explicitly, for example a webhook tool's query string; for the agent's own sense of time no variable is needed, see [World context](#world-context). ### In webhook tools @@ -146,6 +138,12 @@ A reference to a system variable that does not exist (`{{system.foo}}`) is rejec There is no `system.session_id`. The configuration is rendered before the session receives its id, so the id cannot be templated into it. Read it from the `POST /v1/agent/sessions` [response](/agents/deploy/authenticated-sessions), the SDK's `connect` event, or the `session` object in [webhooks](/agents/monitor/webhooks). +## World context + +The agent knows the current date and time without any variable. The platform states the date and the session's timezone at the start of every session and refreshes the time on every turn, so "tomorrow morning" or "next Tuesday" resolve correctly even in a long conversation. You don't need a custom `{{today}}` or `{{now}}`, and there is no `system.now`: variables render once, when the session is created, so a templated time would be stale from the first reply onward. When you want the date or timezone as text in a template, for example in a webhook tool URL, use the [`system.today` and `system.timezone`](#system-variables) system variables. + +See [Time & timezone](/agents/build/time-timezone) for how the timezone is resolved and how to turn the injection off for a session (`world_context: false`). + ## Going further From 61d5232ddf368be80a865275dd6db071ddce5f66 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 21 Aug 2026 15:05:25 +0900 Subject: [PATCH 7/7] docs(agents): phone number example for system variables Co-Authored-By: Claude Fable 5 --- agents/build/dynamic-variables.mdx | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/agents/build/dynamic-variables.mdx b/agents/build/dynamic-variables.mdx index 61d084e..dc4e449 100644 --- a/agents/build/dynamic-variables.mdx +++ b/agents/build/dynamic-variables.mdx @@ -110,13 +110,27 @@ The platform fills a handful of `{{system.*}}` placeholders itself, on every ses | `system.caller_number` | The human party's number, E.164 | Inbound: the caller (empty if withheld or not a valid E.164 number). Outbound: the number you dialed. Empty on non-phone sessions. | | `system.dialed_number` | Your workspace number, E.164 | Inbound: the number that was called. Outbound: the number the call was placed from. Empty on non-phone sessions. | +### Phone numbers + +The phone numbers are the session's `caller_number` and `dialed_number` fields as shown in [session history](/agents/telephony/inbound-calls#phone-sessions-in-history). Only a value that is a well-formed E.164 number reaches the agent: anything else the carrier sends as the caller id (for example an `anonymous` marker) renders as an empty string, while the history field keeps the raw value. On every session that is not a phone call (web, text, preview, agent tests) both render as an empty string, never as literal `{{system.caller_number}}` text, so one agent can serve web and phone with the same prompt. + +Write the prompt so it also reads well when the number is empty: + ```text System prompt -The caller is calling from {{system.caller_number}}. Confirm the last four digits before discussing the account. +You answer the support line {{system.dialed_number}}. The caller's number is +"{{system.caller_number}}". If it is empty, ask for a callback number; otherwise +confirm the last four digits before discussing the account. ``` -### Phone numbers +On an inbound call from +1 415 555 0123 to your number +1 408 555 0199 the model sees: -The phone numbers are the session's `caller_number` and `dialed_number` fields as shown in [session history](/agents/telephony/inbound-calls#phone-sessions-in-history). Only a value that is a well-formed E.164 number reaches the agent: anything else the carrier sends as the caller id (for example an `anonymous` marker) renders as an empty string, while the history field keeps the raw value. On every session that is not a phone call (web, text, preview, agent tests) both render as an empty string, never as literal `{{system.caller_number}}` text, so one agent can serve web and phone with the same prompt. +```text Rendered +You answer the support line +14085550199. The caller's number is +"+14155550123". If it is empty, ask for a callback number; otherwise +confirm the last four digits before discussing the account. +``` + +On a web session the same prompt renders `The caller's number is "".`, and the agent asks for a number. To look the caller up in your own systems, template the number into a [webhook tool](#in-webhook-tools) instead of relying on the model to repeat it. ### Date and timezone