From ff1279f6e6a528f8e7bce703a8bb657324083af2 Mon Sep 17 00:00:00 2001 From: Devon-White Date: Tue, 28 Jul 2026 13:10:43 -0400 Subject: [PATCH 1/3] SWML: fix shipped doc defects and document messaging transfer/execute dest forms Contains two groups of changes. One is correct against shipped behavior today; the other describes behavior that has not shipped yet. See the gating note below. ## Shipped-behavior fixes (correct today) Voice reference: - transfer.mdx: delete the array-of-objects example. It fails schema validation and rejects the entire document, so copying it broke the customer's script. - transfer.mdx: remove the step placed after a transfer. transfer is a tail call; that step never ran. Tail-call behavior is now stated outright. - transfer.mdx + TypeSpec: drop the inline-SWML-document claim. Voice transfer accepts a URL or section name only and never accepted inline documents. - execute.mdx + TypeSpec: correct an inverted rule. An array result block runs as cond and an object runs as switch, not the reverse. Document the previously omitted inline YAML and bare-instruction-array forms. - goto.mdx + TypeSpec: document the shared jump budget and per-label max counting. - Document the bare-string shorthand for transfer, execute, and goto. Messaging reference: - overview.mdx and variables.mdx: remove the vars.* variable scope, which does not exist in messaging. Runtime variables are read without a prefix; a vars.-prefixed path silently expanded to an empty string in the customer's outgoing message. vars remains documented as what it actually is: a field of the outbound webhook payload, not an interpolation scope. - overview.mdx: runtime variables are delivered in the webhook payload but the transferred document's context is rebuilt, so drop the propagation claim. - overview.mdx and request.mdx: request_response_code is a string, not an integer. - overview.mdx: replace the unenforced content-type requirement with the actual parse behavior. - errors.mdx: correct two wrong error-code strings, add missing codes, fix message drift, and group entries by document, method, and runtime level. enter_queue and connect: rename transfer_after_bridge to execute_after_queue, renamed on the backend on 2026-06-01. connect validates against a closed parameter whitelist, so the worked example rejected the whole document. On enter_queue the old name was silently ignored and the post-call SWML never ran. Also drop the required flag, since the same change made it optional and defaults to hangup, and remove the parameter from the parallel, serial, and serial_parallel forms, which have no working placement for it. Regenerates fern/apis/signalwire-rest/openapi.yaml, which is emitted from these SWML models. Changes are confined to SWML.Calling.* and SWML.Messaging.* schemas. ## docs#510 - BLOCKED, do not merge yet Documents messaging transfer and execute accepting a section name, a URL, or an inline SWML document, plus the swml_execute_fetch_failed and swml_execute_document_invalid error codes. This behavior exists only in signalwire/prime-rails#9574, which is OPEN and UNMERGED (upstream signalwire/cloud-product#18950). Hold this PR as a draft until that merges and deploys, then re-verify against the merge commit, since the PR can still change in review. Note that issue #510's premise that these are "all three forms that Voice SWML already supports" is not accurate. Voice transfer never accepted inline documents, so after #9574 messaging transfer is a superset of voice on destination forms while being narrower on guardrails. The docs avoid claiming parity in either direction. Verified: yarn build:schema compiles both SWML projects; yarn fern-md-check reports all 2810 MDX files valid. --- fern/apis/signalwire-rest/openapi.yaml | 455 +++++++++++------- .../products/swml/pages/get-started/index.mdx | 4 +- fern/products/swml/pages/reference/errors.mdx | 74 ++- .../methods/calling/connect/index.mdx | 48 +- .../reference/methods/calling/enter_queue.mdx | 16 +- .../reference/methods/calling/execute.mdx | 8 +- .../pages/reference/methods/calling/goto.mdx | 11 +- .../reference/methods/calling/transfer.mdx | 69 +-- .../reference/methods/messaging/execute.mdx | 189 +++++++- .../reference/methods/messaging/goto.mdx | 8 +- .../reference/methods/messaging/overview.mdx | 106 ++-- .../reference/methods/messaging/request.mdx | 8 +- .../reference/methods/messaging/return.mdx | 14 +- .../reference/methods/messaging/transfer.mdx | 200 +++++++- .../swml/pages/reference/variables.mdx | 11 +- specs/swml/calling/Methods/connect/main.tsp | 6 +- .../swml/calling/Methods/enter_queue/main.tsp | 7 +- specs/swml/calling/Methods/execute/main.tsp | 71 +-- specs/swml/calling/Methods/goto/main.tsp | 34 +- specs/swml/calling/Methods/transfer/main.tsp | 75 +-- .../@typespec/json-schema/SWMLObject.json | 362 +++++++------- specs/swml/messaging/Methods/execute/main.tsp | 54 ++- .../swml/messaging/Methods/transfer/main.tsp | 85 ++-- .../@typespec/json-schema/SWMLObject.json | 181 ++++--- 24 files changed, 1340 insertions(+), 756 deletions(-) diff --git a/fern/apis/signalwire-rest/openapi.yaml b/fern/apis/signalwire-rest/openapi.yaml index 189dc95f62..72053e8305 100644 --- a/fern/apis/signalwire-rest/openapi.yaml +++ b/fern/apis/signalwire-rest/openapi.yaml @@ -35579,7 +35579,7 @@ components: description: Webhook URL to send call status change notifications to. Authentication can also be set in the URL in the format of `username:password@url`. examples: - https://example.com/call-status - transfer_after_bridge: + execute_after_queue: type: string description: |- SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends. @@ -35587,7 +35587,9 @@ components: - A URL (http or https) that returns a SWML document - An inline SWML document (as a JSON string) - **Note:** This parameter is REQUIRED when connecting to a queue (when `to` starts with "queue:") + Applies only when connecting to a single queue destination — that is, when `to` starts with + `queue:`. It has no effect for any other destination type, or in the `serial`, `parallel`, and + `serial_parallel` forms. If omitted, the call hangs up once the bridge ends. examples: - https://example.com/after-bridge.swml call_state_events: @@ -35749,7 +35751,7 @@ components: description: Webhook URL to send call status change notifications to. Authentication can also be set in the URL in the format of `username:password@url`. examples: - https://example.com/call-status - transfer_after_bridge: + execute_after_queue: type: string description: |- SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends. @@ -35757,7 +35759,9 @@ components: - A URL (http or https) that returns a SWML document - An inline SWML document (as a JSON string) - **Note:** This parameter is REQUIRED when connecting to a queue (when `to` starts with "queue:") + Applies only when connecting to a single queue destination — that is, when `to` starts with + `queue:`. It has no effect for any other destination type, or in the `serial`, `parallel`, and + `serial_parallel` forms. If omitted, the call hangs up once the bridge ends. examples: - https://example.com/after-bridge.swml call_state_events: @@ -35919,7 +35923,7 @@ components: description: Webhook URL to send call status change notifications to. Authentication can also be set in the URL in the format of `username:password@url`. examples: - https://example.com/call-status - transfer_after_bridge: + execute_after_queue: type: string description: |- SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends. @@ -35927,7 +35931,9 @@ components: - A URL (http or https) that returns a SWML document - An inline SWML document (as a JSON string) - **Note:** This parameter is REQUIRED when connecting to a queue (when `to` starts with "queue:") + Applies only when connecting to a single queue destination — that is, when `to` starts with + `queue:`. It has no effect for any other destination type, or in the `serial`, `parallel`, and + `serial_parallel` forms. If omitted, the call hangs up once the bridge ends. examples: - https://example.com/after-bridge.swml call_state_events: @@ -36095,7 +36101,7 @@ components: description: Webhook URL to send call status change notifications to. Authentication can also be set in the URL in the format of `username:password@url`. examples: - https://example.com/call-status - transfer_after_bridge: + execute_after_queue: type: string description: |- SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends. @@ -36103,7 +36109,9 @@ components: - A URL (http or https) that returns a SWML document - An inline SWML document (as a JSON string) - **Note:** This parameter is REQUIRED when connecting to a queue (when `to` starts with "queue:") + Applies only when connecting to a single queue destination — that is, when `to` starts with + `queue:`. It has no effect for any other destination type, or in the `serial`, `parallel`, and + `serial_parallel` forms. If omitted, the call hangs up once the bridge ends. examples: - https://example.com/after-bridge.swml call_state_events: @@ -36780,7 +36788,8 @@ components: Place the current call in a named queue where it will wait to be connected to an available agent or resource. While waiting, callers will hear music or custom audio. When an agent connects to the queue (using the connect method), the caller and agent are bridged together. - After the bridge completes, execution continues with the SWML script specified in transfer_after_bridge. + After the bridge completes, execution continues with the SWML specified in `execute_after_queue`, + or the call hangs up if that parameter is not set. title: enter_queue unevaluatedProperties: not: {} @@ -36789,20 +36798,21 @@ components: type: object required: - queue_name - - transfer_after_bridge properties: queue_name: type: string description: Name of the queue to enter. If a queue with this name does not exist, it will be automatically created. examples: - support-queue - transfer_after_bridge: + execute_after_queue: type: string description: |- SWML to execute after the bridge completes. This defines what should happen after the call is connected to an agent and the bridge ends. Can be either: - A URL (http or https) that returns a SWML document - An inline SWML document (as a JSON string) + + If omitted, the call hangs up when the queue interaction ends. examples: - https://example.com/post-call-survey status_url: @@ -36833,54 +36843,14 @@ components: - execute properties: execute: - type: object - properties: - dest: - type: string - description: |- - Specifies what to execute. The value can be one of: - - `` - section in the current document to execute - - A URL (http or https) that returns a SWML document - Sends HTTP POST - - An inline SWML document (as a JSON string) - examples: - - https://example.com/swml-handler - params: - type: object - unevaluatedProperties: {} - description: Named parameters to send to section or URL - examples: - - caller_id: '+15551234567' - language: en-US - meta: - type: object - unevaluatedProperties: {} - description: User-defined metadata, ignored by SignalWire - examples: - - request_id: req_abc123 - source: ivr - on_return: - description: The list of SWML instructions to be executed when the executed section or URL returns - type: array - items: - $ref: '#/components/schemas/SWML.Calling.SWMLMethod' - result: - anyOf: - - $ref: '#/components/schemas/SWML.Calling.ExecuteSwitch' - - type: array - items: - $ref: '#/components/schemas/SWML.Calling.CondParams' - description: Execute a sequence of instructions depending on the value of a JavaScript condition. - title: cond - description: |- - Action to take based on the result of the call. This will run once the peer leg of the call has ended. - Will use the switch method when the return_value is an object, and will use the cond method when the return_value is an array. - required: - - dest - unevaluatedProperties: - not: {} + anyOf: + - type: string + - $ref: '#/components/schemas/SWML.Calling.ExecuteTarget' description: |- - Execute a specified section or URL as a subroutine, and upon completion, return to the current document. + Execute a specified section, URL, or inline document as a subroutine, and upon completion, return to the current document. Use the return statement to pass any return values or objects back to the current document. + + Accepts either an object, or a string used directly as `dest`. unevaluatedProperties: not: {} title: execute Method @@ -36911,6 +36881,55 @@ components: unevaluatedProperties: not: {} title: ExecuteSwitch object + SWML.Calling.ExecuteTarget: + type: object + required: + - dest + properties: + dest: + type: string + description: |- + Specifies what to execute. The value can be one of: + - `` - section in the current document to execute + - A URL (http or https) that returns a SWML document - Sends HTTP POST + - An inline SWML document, as a JSON or YAML string. It may be a full document with + `version` and `sections`, or just an array of instructions. + examples: + - https://example.com/swml-handler + params: + type: object + unevaluatedProperties: {} + description: Named parameters to send to section or URL + examples: + - caller_id: '+15551234567' + language: en-US + meta: + type: object + unevaluatedProperties: {} + description: User-defined metadata, ignored by SignalWire + examples: + - request_id: req_abc123 + source: ivr + on_return: + description: The list of SWML instructions to be executed when the executed section or URL returns + type: array + items: + $ref: '#/components/schemas/SWML.Calling.SWMLMethod' + result: + anyOf: + - $ref: '#/components/schemas/SWML.Calling.ExecuteSwitch' + - type: array + items: + $ref: '#/components/schemas/SWML.Calling.CondParams' + description: Execute a sequence of instructions depending on the value of a JavaScript condition. + title: cond + description: |- + Action to take based on the result of the call. This will run once the peer leg of the call has ended. + The form of this block selects how it runs: an array of instructions runs as a `cond`, and an + object runs as a `switch` matched against `return_value`. + unevaluatedProperties: + not: {} + title: ExecuteTarget object SWML.Calling.Expression: type: object required: @@ -38413,36 +38432,47 @@ components: - goto properties: goto: - type: object - properties: - label: - type: string - description: Mark any point of the SWML section with a label so that goto can jump to it. - examples: - - greeting - when: - type: string - description: A JavaScript condition that determines whether to perform the jump. If the condition evaluates to true, the jump is executed. If omitted, the jump is unconditional. - examples: - - vars.retry_count < 3 - max: - type: integer - minimum: 1 - maximum: 100 - description: The maximum number of times to perform the jump. Must be a number between 1 and 100. Default `100`. - examples: - - 3 - default: 100 - required: - - label - unevaluatedProperties: - not: {} + anyOf: + - type: string + - $ref: '#/components/schemas/SWML.Calling.GotoTarget' description: |- Jump to a label within the current section, optionally based on a condition. The goto method will only navigate to a label within the same section. + + Jumps count against the same per-call budget as `transfer` — 32 combined, or 8 without + caller interaction. Exceeding it ends the call with + [`relay_script_too_many_transfers`](/docs/swml/reference/errors). + + Accepts either an object, or a string used directly as `label`. unevaluatedProperties: not: {} title: goto Method + SWML.Calling.GotoTarget: + type: object + required: + - label + properties: + label: + type: string + description: Mark any point of the SWML section with a label so that goto can jump to it. + examples: + - greeting + when: + type: string + description: A JavaScript condition that determines whether to perform the jump. If the condition evaluates to true, the jump is executed. If omitted, the jump is unconditional. + examples: + - vars.retry_count < 3 + max: + type: integer + minimum: 1 + maximum: 100 + description: The maximum number of times to perform the jump. Must be a number between 1 and 100. Default `100`. + examples: + - 3 + default: 100 + unevaluatedProperties: + not: {} + title: GotoTarget object SWML.Calling.HangUpHookSWAIGFunction: type: object required: @@ -42037,49 +42067,59 @@ components: - transfer properties: transfer: - type: object - properties: - dest: - type: string - description: |- - Specifies where to transfer to. The value can be one of: - - - section in the SWML document to jump to - - A URL (http or https) - URL to fetch next document from. Sends HTTP POST. - Authentication can also be set in the URL in the format of username:password@url. - - An inline SWML document (as a JSON string) - examples: - - https://example.com/transfer-handler - params: - type: object - unevaluatedProperties: {} - description: |- - Named parameters to send to transfer destination. - Accepts an object mapping variable names to values. - Default is not set. - examples: - - department: sales - priority: high - meta: - type: object - unevaluatedProperties: {} - description: |- - User data, ignored by SignalWire. - Accepts an object mapping variable names to values. - Default is not set. - examples: - - transfer_reason: escalation - original_agent: agent_001 - required: - - dest - unevaluatedProperties: - not: {} + anyOf: + - type: string + - $ref: '#/components/schemas/SWML.Calling.TransferTarget' description: |- - Transfer the execution of the script to a different SWML section, URL, or Relay application. - Once the transfer is complete, the script will continue executing SWML from the new location. + Transfer the execution of the script to a different SWML section or URL. + Execution continues in the new location and does not return — instructions after + `transfer` in the current section are skipped. Use `execute` to run a section or + document as a subroutine and come back. + + Accepts either an object, or a string used directly as `dest`. title: transfer unevaluatedProperties: not: {} title: transfer Method + SWML.Calling.TransferTarget: + type: object + required: + - dest + properties: + dest: + type: string + description: |- + Specifies where to transfer to. The value can be one of: + - - section in the SWML document to jump to + - A URL (http or https) - URL to fetch next document from. Sends HTTP POST. + Authentication can also be set in the URL in the format of username:password@url. + + Unlike `execute`, `transfer` does not accept an inline SWML document. + examples: + - https://example.com/transfer-handler + params: + type: object + unevaluatedProperties: {} + description: |- + Named parameters to send to transfer destination. + Accepts an object mapping variable names to values. + Default is not set. + examples: + - department: sales + priority: high + meta: + type: object + unevaluatedProperties: {} + description: |- + User data, ignored by SignalWire. + Accepts an object mapping variable names to values. + Default is not set. + examples: + - transfer_reason: escalation + original_agent: agent_001 + unevaluatedProperties: + not: {} + title: TransferTarget object SWML.Calling.TranslateAction: anyOf: - $ref: '#/components/schemas/SWML.Calling.StartAction' @@ -42470,33 +42510,55 @@ components: - execute properties: execute: - type: object - properties: - dest: - type: string - description: Name of the section to execute. Must reference a section in the current document. - examples: - - greet - params: - type: object - unevaluatedProperties: {} - description: Parameters accessible as `params.*` in the called section. Replaces (does not merge with) any outer `params` from the caller. - examples: - - name: '%{message.from}' - required: - - dest - unevaluatedProperties: - not: {} + anyOf: + - type: string + - $ref: '#/components/schemas/SWML.Messaging.ExecuteTarget' description: |- - Call a named section as a subroutine. Execution continues in the called section, - then returns to the caller when the section completes — either via `return` or by - reaching the end of the section. The destination must be the name of a section - defined in the current document; URLs and inline documents are not accepted in the - messaging context. + Call a section, a URL, or an inline document as a subroutine. Execution continues in the + destination, then returns to the caller when it completes — either via `return` or by + reaching the end. `return_value` and the propagated `reply_*` and `request_*` variables + come back with it. + + A section destination runs against the caller's own variables, with `params` replacing any + inherited `params`. A URL or inline destination is a separate document: it sees only the + `message` variables and the `params` you pass, and it cannot reach the caller's sections. + + Accepts either an object, or a string used directly as `dest`. `%{...}` placeholders are + not expanded in the string form — use the object form for a computed destination. title: execute unevaluatedProperties: not: {} title: execute Method + SWML.Messaging.ExecuteTarget: + type: object + required: + - dest + properties: + dest: + type: string + description: |- + What to execute. The value can be one of: + - A section name — a section defined in the current document. + - A URL (`http` or `https`) that returns a SWML document. SignalWire always sends an HTTP + POST; there is no `method` parameter on `execute`. + - An inline SWML document, supplied as a JSON string. It may be a full document with + `sections`, or a bare `reply` or `receive` document. + + A value that is not an `http`/`https` URL and does not parse as a JSON object is treated + as a section name. A malformed inline document — a syntax error, a JSON array, or a value + written as a native object rather than a string — therefore fails with + `swml_section_not_found`, not `swml_execute_document_invalid`. + examples: + - greet + params: + type: object + unevaluatedProperties: {} + description: Parameters accessible as `params.*` in the destination. Replaces (does not merge with) any outer `params` from the caller. + examples: + - name: '%{message.from}' + unevaluatedProperties: + not: {} + title: ExecuteTarget object SWML.Messaging.Goto: type: object required: @@ -42933,53 +42995,80 @@ components: - transfer properties: transfer: - type: object - properties: - dest: - type: string - format: uri - description: |- - URL (`http` or `https`) to fetch the new SWML document from. Authentication can - be embedded in the URL as `username:password@url`. - examples: - - https://example.com/handler - method: - type: string - enum: - - GET - - POST - - PUT - - PATCH - - DELETE - description: HTTP method for the fetch request. - examples: - - POST - default: POST - params: - type: object - unevaluatedProperties: {} - description: Parameters to include in the request body of the fetch. Available as `params.*` in the transferred document. - examples: - - reason: escalation - required: - - dest - unevaluatedProperties: - not: {} + anyOf: + - type: string + - $ref: '#/components/schemas/SWML.Messaging.TransferTarget' description: |- - Fetch and execute a new SWML document from a URL. SignalWire POSTs the - [inbound message webhook payload](/docs/apis/rest/swml-webhook/webhooks/inbound-message-webhook) - to `dest`: `message` describes the original inbound message, `params` carries - the values supplied here, and `vars` carries the propagated runtime variables - (`request_result`, `reply_result`, etc.) accumulated by the current document. - - This is a tail call — it replaces the current document and does not return. - Steps after `transfer` are skipped, including steps in calling sections. In the - messaging context, `transfer.dest` must be a URL — section names and inline - documents are not accepted. + Hand execution to another SWML destination: a section in the current document, a URL that + returns a SWML document, or an inline SWML document. + + This is a tail call — it replaces the current document and does not return. Steps after + `transfer` are skipped, including steps in calling sections. A transfer that cannot be + resolved ends the run rather than falling through to the next step. + + A section destination keeps the runtime variables accumulated so far. A URL or inline + destination is a separate document: it sees only the `message` variables and the `params` + you pass. + + Accepts either an object, or a string used directly as `dest`. `%{...}` placeholders are + not expanded in the string form — use the object form for a computed destination. title: transfer unevaluatedProperties: not: {} title: transfer Method + SWML.Messaging.TransferTarget: + type: object + required: + - dest + properties: + dest: + type: string + description: |- + Where to transfer to. The value can be one of: + - A section name — a section defined in the current document. + - A URL (`http` or `https`) that returns a SWML document. SignalWire sends the + [inbound message webhook payload](/docs/apis/rest/swml-webhook/webhooks/inbound-message-webhook) + as the request body: `message` describes the original inbound message, `params` carries + the values supplied here, and `vars` carries the runtime variables (`request_result`, + `reply_result`, etc.) accumulated by the current document. Credentials embedded in the + URL as `username:password@url` are sent as HTTP basic authentication; this relies on how + the HTTP client handles credentials in a URL, so re-check it after a major platform + update if you depend on it. + - An inline SWML document, supplied as a JSON string. It may be a full document with + `sections`, or a bare `reply` or `receive` document. + + A value that is not an `http`/`https` URL and does not parse as a JSON object is treated + as a section name. A malformed inline document — a syntax error, a JSON array, or a value + written as a native object rather than a string — therefore fails with + `swml_section_not_found`, not a fetch or parse error. + examples: + - https://example.com/handler + method: + type: string + enum: + - GET + - POST + - PUT + - PATCH + - DELETE + description: |- + HTTP method for the fetch request. Applies only when `dest` is a URL; it is ignored for + section and inline-document destinations. One of `GET`, `POST`, `PUT`, `PATCH`, or + `DELETE` — any other value causes the fetch to fail with `swml_transfer_fetch_failed`. + examples: + - POST + default: POST + params: + type: object + unevaluatedProperties: {} + description: |- + Parameters made available as `params.*` in the destination. Replaces (does not merge with) + the caller's `params`. For a URL destination they are also sent in the request body. + examples: + - reason: escalation + unevaluatedProperties: + not: {} + title: TransferTarget object SWMLScriptAddressListResponse: type: object required: diff --git a/fern/products/swml/pages/get-started/index.mdx b/fern/products/swml/pages/get-started/index.mdx index b111699f9f..b4af4fc750 100644 --- a/fern/products/swml/pages/get-started/index.mdx +++ b/fern/products/swml/pages/get-started/index.mdx @@ -150,8 +150,8 @@ paths in each context. When SignalWire fetches a SWML script from an external URL, it sends a POST request whose payload shape depends on the document type: -- **Calling** — see [Calling SWML → Document-fetching webhook](/docs/swml/reference/calling#document-fetching-webhook). -- **Messaging** — see [Messaging SWML → Document-fetching webhook](/docs/swml/reference/messaging#document-fetching-webhook). +- **Calling** — see [Calling SWML → Webhook and variable payload](/docs/swml/reference/calling#webhook-payload). +- **Messaging** — see [Messaging SWML → Webhook and variable payload](/docs/swml/reference/messaging#webhook-payload). Your server must respond with a valid SWML document using one of these content types: `application/json`, `application/yaml`, or `text/x-yaml`. For server setup instructions, see the diff --git a/fern/products/swml/pages/reference/errors.mdx b/fern/products/swml/pages/reference/errors.mdx index 3feb2278e5..f03bf498ac 100644 --- a/fern/products/swml/pages/reference/errors.mdx +++ b/fern/products/swml/pages/reference/errors.mdx @@ -24,14 +24,16 @@ message describing the offending method, parameter, or condition. You can read t `script_error` variable on the call session or from the payload of the `calling.script.warning` event. +### Document level + - The fetched document could not be parsed as valid JSON or YAML, or the `dest` URL of a `transfer` - was malformed. + The fetched document could not be parsed as valid JSON or YAML, or the `dest` of a `transfer` or + `execute` was malformed. - An internal parsing error occurred while reading the document. Verify the document is valid - YAML or JSON and retry. + The YAML parser failed internally while reading the document. A document that fails to parse as + JSON reports `relay_script_parse_error` instead. @@ -51,7 +53,7 @@ event. - An `execute` step references a section that does not exist in the document. + A section defined in object form contains a key other than `code` or `meta`. @@ -72,6 +74,8 @@ event. A document element that may appear at most once was provided multiple times. +### Method level + A step in a section does not have a method name (the step entry is malformed). @@ -108,20 +112,26 @@ event. cause. +### Runtime + A JavaScript expression in a `cond`, `switch`, `eval`, or `when` clause raised an error. - A document or one of its sections is nested beyond the maximum depth. + A section or a nested method block exceeds the maximum nesting depth of 32. A chain of documents + nested too deeply reports `relay_script_internal_error` instead. - The chain of `transfer` and `goto` steps exceeded the platform maximum (32 transfers per call). + The chain of `transfer` and `goto` steps exceeded a platform maximum: 32 in total, or 8 without + caller interaction. The call is disconnected. - An internal server error occurred during script execution. + An internal server error occurred during script execution. This also covers a chain of documents + reached through `execute` or `transfer` that is nested beyond the platform limit, in which case + the call is disconnected. ## Messaging errors @@ -132,18 +142,29 @@ inbound message is marked `failed` with the listed `error_code` and a correspond ### Document fetch / route - - Unrouteable message received — no SWML handler is assigned to the inbound number. +These codes cover accepting the inbound message and fetching the handler assigned to its number. +The granular fetch codes below, along with `json_or_yaml_required`, are reachable only from that +initial fetch. A document fetch that fails partway through a run, from a `transfer` or an `execute` +step, always reports that method's own fetch code regardless of the underlying cause. + + + No inbound route exists for the destination number, so the message could not be accepted. - - Insufficient account balance to receive the message. + + Insufficient account balance to receive the message. The message is marked `failed` and the SWML + document is never fetched. Message filtered (e.g. spam detection or carrier-level filtering). + + The handler assigned to the inbound number is not a SWML script — for example, a LaML bin + assigned to a SWML handler slot. + + The attempt to retrieve the SWML document timed out or failed. @@ -189,7 +210,7 @@ inbound message is marked `failed` with the listed `error_code` and a correspond - The reply `from` number is not a valid PhoneRoute or ShortCode in this project. + The reply `from` number is not a number assigned to this project. @@ -222,7 +243,18 @@ inbound message is marked `failed` with the listed `error_code` and a correspond ### `execute` method - The section named by `execute.dest` is not defined in the current document. + The section named by `execute.dest` or `transfer.dest` is not defined in the document currently + in scope. A `dest` that is neither a URL nor a parseable inline document is read as a section + name, so a malformed destination reports this code rather than a fetch or parse error. + + + + Failed to fetch the executed SWML document from the destination URL. Covers a non-2xx response, a + timeout, an unparseable URL, or a body that is neither JSON nor YAML. + + + + The executed SWML document is not a valid messaging document. ### `transfer` method @@ -232,13 +264,18 @@ inbound message is marked `failed` with the listed `error_code` and a correspond - Failed to fetch the transferred SWML document from the destination URL. + Failed to fetch the transferred SWML document from the destination URL. Covers a non-2xx + response, a timeout, an unparseable URL, an unsupported HTTP method, or a body that is neither + JSON nor YAML. The transferred SWML document is not a valid messaging document. +`transfer` to a section name that is not defined also reports +[`swml_section_not_found`](#swml_section_not_found). + ### `goto` method @@ -250,3 +287,10 @@ inbound message is marked `failed` with the listed `error_code` and a correspond `request.url` is required and was missing or empty. + +### General + + + An unexpected error occurred while executing the document. Contact support with the message ID if + this repeats. + diff --git a/fern/products/swml/pages/reference/methods/calling/connect/index.mdx b/fern/products/swml/pages/reference/methods/calling/connect/index.mdx index 831b002a15..a20eefe2d2 100644 --- a/fern/products/swml/pages/reference/methods/calling/connect/index.mdx +++ b/fern/products/swml/pages/reference/methods/calling/connect/index.mdx @@ -63,6 +63,14 @@ Dial a single destination directly using the `to` property. The encryption method to use for the call. Possible values: `mandatory`, `optional`, `forbidden`. + + SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends. Applies only when connecting to a queue (when `to` starts with `queue:`); without it, the call hangs up when the queue interaction ends. + + Can be either: + - A URL (http or https) that returns a SWML document + - An inline SWML document (as a JSON string) + + Caller ID number. Optional. @@ -122,16 +130,6 @@ Dial a single destination directly using the `to` property. Maximum time, in seconds, to wait for an answer. - - SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends. - - Can be either: - - A URL (http or https) that returns a SWML document - - An inline SWML document (as a JSON string) - - Required when connecting to a queue (when `to` starts with `queue:`). - - SIP authentication username (`sip_auth_username`) for the outbound leg. **Only applies to [SIP URI](/docs/platform/voice/sip) targets** — ignored for phone, Resource Address, queue, and stream destinations. @@ -353,14 +351,6 @@ Dial multiple destinations simultaneously — the first to answer is bridged and Maximum time, in seconds, to wait for an answer. - - SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends. - - Can be either: - - A URL (http or https) that returns a SWML document - - An inline SWML document (as a JSON string) - - If true, WebRTC media is offered to the SIP endpoint. Has no effect on calls to phone numbers. @@ -563,14 +553,6 @@ Dial destinations in sequence — each destination is tried one at a time, movin Maximum time, in seconds, to wait for an answer. - - SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends. - - Can be either: - - A URL (http or https) that returns a SWML document - - An inline SWML document (as a JSON string) - - If true, WebRTC media is offered to the SIP endpoint. Has no effect on calls to phone numbers. @@ -775,14 +757,6 @@ Combine both strategies. The outer array is the **serial** dimension — groups Maximum time, in seconds, to wait for an answer. - - SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends. - - Can be either: - - A URL (http or https) that returns a SWML document - - An inline SWML document (as a JSON string) - - If true, WebRTC media is offered to the SIP endpoint. Has no effect on calls to phone numbers. @@ -1422,7 +1396,7 @@ sections: -### Connect to a queue with transfer after bridge +### Connect to a queue with SWML to run afterward @@ -1432,7 +1406,7 @@ sections: main: - connect: to: "queue:support" - transfer_after_bridge: "https://example.com/post-call-swml" + execute_after_queue: "https://example.com/post-call-swml" ``` @@ -1444,7 +1418,7 @@ sections: { "connect": { "to": "queue:support", - "transfer_after_bridge": "https://example.com/post-call-swml" + "execute_after_queue": "https://example.com/post-call-swml" } } ] diff --git a/fern/products/swml/pages/reference/methods/calling/enter_queue.mdx b/fern/products/swml/pages/reference/methods/calling/enter_queue.mdx index 715f47b3da..f61ed4db05 100644 --- a/fern/products/swml/pages/reference/methods/calling/enter_queue.mdx +++ b/fern/products/swml/pages/reference/methods/calling/enter_queue.mdx @@ -9,7 +9,7 @@ max-toc-depth: 3 Place the current call in a named queue where it will wait to be connected to an available agent or resource. While waiting, callers will hear music or custom audio. When an agent connects to the queue (using the [`connect`](/docs/swml/reference/calling/connect) method), the caller and agent are bridged together. -After the bridge completes (when the agent or caller hangs up), execution continues with the SWML script specified in `transfer_after_bridge`. +After the bridge completes (when the agent or caller hangs up), execution continues with the SWML script specified in `execute_after_queue`. Without that property, the call hangs up instead. ## **Properties** @@ -24,9 +24,9 @@ After the bridge completes (when the agent or caller hangs up), execution contin - - SWML to execute after the bridge completes (when the agent or caller hangs up). This defines what should happen after the call is connected to an agent and the bridge ends. - + + SWML to execute after the bridge completes (when the agent or caller hangs up). This defines what should happen after the call is connected to an agent and the bridge ends. Without this property, the call hangs up when the queue interaction ends. + Can be either: - A URL (http or https) that returns a SWML document - An inline SWML document (as a JSON string) @@ -280,7 +280,7 @@ sections: main: - enter_queue: queue_name: "my_queue" - transfer_after_bridge: "https://example.com/post-call-swml" + execute_after_queue: "https://example.com/post-call-swml" ``` @@ -292,7 +292,7 @@ sections: { "enter_queue": { "queue_name": "my_queue", - "transfer_after_bridge": "https://example.com/post-call-swml" + "execute_after_queue": "https://example.com/post-call-swml" } } ] @@ -314,7 +314,7 @@ sections: queue_name: "sales_queue" status_url: "https://example.com/queue-status" wait_time: 1800 - transfer_after_bridge: "https://example.com/post-call-swml" + execute_after_queue: "https://example.com/post-call-swml" ``` @@ -328,7 +328,7 @@ sections: "queue_name": "sales_queue", "status_url": "https://example.com/queue-status", "wait_time": 1800, - "transfer_after_bridge": "https://example.com/post-call-swml" + "execute_after_queue": "https://example.com/post-call-swml" } } ] diff --git a/fern/products/swml/pages/reference/methods/calling/execute.mdx b/fern/products/swml/pages/reference/methods/calling/execute.mdx index ae54eebf56..c097ea2cb8 100644 --- a/fern/products/swml/pages/reference/methods/calling/execute.mdx +++ b/fern/products/swml/pages/reference/methods/calling/execute.mdx @@ -9,6 +9,8 @@ max-toc-depth: 3 Execute a specified section or URL as a subroutine, and upon completion, return to the current document. Use the [return](/docs/swml/reference/calling/return) statement to pass any return values or objects back to the current document. +As a shorthand, `execute` accepts a bare string used as `dest`. Use the object form to set any other property. + ## **Properties** @@ -41,15 +43,15 @@ Use the [return](/docs/swml/reference/calling/return) statement to pass any retu Action to take based on the result of the call. This will run once the peer leg of the call has ended. -Will use the [`switch`](/docs/swml/reference/calling/switch) method when the `return_value` is a object, and will use the [`cond`](/docs/swml/reference/calling/cond) method when the `return_value` is an array. +The `result` block runs only when the subroutine set a `return_value`. An array runs as a [`cond`](/docs/swml/reference/calling/cond); an object runs as a [`switch`](/docs/swml/reference/calling/switch) on `return_value`. ### Valid Destinations The destination string can be one of: - `"section_name"` - section in the current document to execute. (For example: `main`) -- A URL (http or https) - URL pointing to the document to execute. An HTTP POST request will be sent to the URL. The `params` object is passed, along with the variables and the [`call`](/docs/swml/reference/calling#call) object. Authentication can also be set in the url in the format of `username:password@url`. -- An inline SWML document (as a JSON string) - SWML document provided directly as a JSON string. +- A URL (http or https) - URL pointing to the document to execute. An HTTP POST request will be sent to the URL. The `params` object is passed, along with the variables and the [`call`](/docs/swml/reference/calling#webhook-payload) object. Authentication can also be set in the url in the format of `username:password@url`. +- An inline SWML document, as a JSON or YAML string. It may be a full document with `version` and `sections`, or just an array of instructions. diff --git a/fern/products/swml/pages/reference/methods/calling/goto.mdx b/fern/products/swml/pages/reference/methods/calling/goto.mdx index 29bc24a5d6..95dea3fd31 100644 --- a/fern/products/swml/pages/reference/methods/calling/goto.mdx +++ b/fern/products/swml/pages/reference/methods/calling/goto.mdx @@ -9,6 +9,8 @@ max-toc-depth: 3 Jump to a `label` within the current section, optionally based on a condition. The `goto` method will only navigate to a label within the same section. +As a shorthand, `goto` accepts a bare string used as `label`. Use the object form to set `when` or `max`. + ## **Properties** @@ -18,7 +20,7 @@ The `goto` method will only navigate to a label within the same section. - The label in section to jump to. + The label in section to jump to. A label that is not defined in the section is caught when the document is validated, and the whole document is rejected. When the label comes from a variable, it is resolved at runtime instead, and execution stops with `relay_script_method_parameter_invalid_value` if no matching label exists. @@ -28,11 +30,16 @@ The `goto` method will only navigate to a label within the same section. - The maximum number of times to jump, from the minimum of `1` to max `100` jumps. + The maximum number of times to jump, from the minimum of `1` to max `100` jumps. The count is kept per label and shared by every `goto` step that targets it. Once the limit is reached, the jump is skipped and execution continues with the next instruction. + +`goto` jumps count against the same per-call budget as [`transfer`](/docs/swml/reference/calling/transfer): 32 jumps and transfers combined, or 8 without caller interaction. +A loop that stays under its own `max` can still exceed the budget, which ends the call with [`relay_script_too_many_transfers`](/docs/swml/reference/errors). + + ## **Examples** ### Loop with max diff --git a/fern/products/swml/pages/reference/methods/calling/transfer.mdx b/fern/products/swml/pages/reference/methods/calling/transfer.mdx index d85a4ae9bb..919a88a026 100644 --- a/fern/products/swml/pages/reference/methods/calling/transfer.mdx +++ b/fern/products/swml/pages/reference/methods/calling/transfer.mdx @@ -2,12 +2,18 @@ id: 0f948677-5818-4ce4-9727-40516a8c1278 title: transfer slug: /reference/calling/transfer -description: Transfer the execution of the script to a different `SWML section`, `URL`, or `RELAY application`. +description: Transfer the execution of the script to a different SWML section or URL. max-toc-depth: 3 --- -Transfer the execution of the script to a different `SWML section`, `URL`, or `RELAY application`. Once -the transfer is complete, the script will continue executing SWML from the new location. +Transfer the execution of the script to a different SWML section or URL. Execution continues in the new location. + +As a shorthand, `transfer` accepts a bare string used as `dest`. Use the object form to set any other property. + + +`transfer` is a tail call: any instructions after it in the current section are skipped, and execution never comes back. +To run a section or document as a subroutine and return, use [`execute`](/docs/swml/reference/calling/execute). + ## **Properties** @@ -19,14 +25,15 @@ the transfer is complete, the script will continue executing SWML from the new l Specifies where to transfer to. The value can be one of: - - `""` - section in the SWML document to jump to - - A URL (http or https) - URL to fetch next document from. Sends HTTP POST. Authentication can also be set in the url in the format of `username:password@url`. - - An inline SWML document (as a JSON string) - SWML document provided directly as a JSON string + - `""` - section in the current SWML document to jump to + - A URL (http or https) - URL to fetch the next document from. Sends HTTP POST. Authentication can also be set in the url in the format of `username:password@url`. + + Unlike [`execute`](/docs/swml/reference/calling/execute), `transfer` does not accept an inline SWML document. - Named parameters to send to a section, URL, or application. + Named parameters to send to the section or URL. When `dest` is a URL, `params` is sent with the request, along with the SWML variables and the [`call`](/docs/swml/reference/calling#webhook-payload) object. @@ -36,19 +43,6 @@ the transfer is complete, the script will continue executing SWML from the new l -## **Valid Destination Values** - -The destination string can be one of: - -- `"section_name"` - section in the current document to execute. -(For example: `execute: main`) -- `"relay:"` - relay application to notify (currently not implemented) -- `"https://example.com/sub-swml.yaml"` - URL pointing to the document to execute. -An HTTP POST request will be sent to the URL. Authentication can also be set in the url in the format of `username:password@url`. -The `params` object is passed, along with the variables and the [`call`](/docs/swml/reference/calling#call) object. - ---- - ## **Examples** ### Basic transfer to a URL @@ -83,6 +77,8 @@ sections: ### Basic transfer to a section +Execution continues in `subsection` and does not return to `main`. + ```yaml @@ -93,8 +89,6 @@ sections: url: 'say:Transferring you to another section' - transfer: dest: subsection - - play: - url: 'say:Back!' subsection: - play: url: 'say:inside a subsection' @@ -115,11 +109,6 @@ sections: "transfer": { "dest": "subsection" } - }, - { - "play": { - "url": "say:Back!" - } } ], "subsection": [ @@ -135,7 +124,9 @@ sections: -### Named parameter with sub-parameters +### Transfer using the shorthand form + +A bare string is used as `dest`, so this is equivalent to the first example. @@ -143,10 +134,7 @@ sections: version: 1.0.0 sections: main: - - transfer: - - dest: "https://example.com/next" - - params: - - foo: "bar" + - transfer: "https://example.com/next" ``` @@ -155,20 +143,7 @@ sections: "version": "1.0.0", "sections": { "main": [ - { - "transfer": [ - { - "dest": "https://example.com/next" - }, - { - "params": [ - { - "foo": "bar" - } - ] - } - ] - } + { "transfer": "https://example.com/next" } ] } } @@ -345,6 +320,6 @@ The server will be sent the payload in the following format: } ``` -The `call` object is described in detail in the [introduction](/docs/swml/reference/calling/transfer). +The `call` object is described in detail in the [Calling overview](/docs/swml/reference/calling#webhook-payload). All variables created within the SWML document are passed inside `vars`, and the `params` object contains the parameters defined in the `params` parameter of `transfer`. diff --git a/fern/products/swml/pages/reference/methods/messaging/execute.mdx b/fern/products/swml/pages/reference/methods/messaging/execute.mdx index d32882b1de..8f14737f80 100644 --- a/fern/products/swml/pages/reference/methods/messaging/execute.mdx +++ b/fern/products/swml/pages/reference/methods/messaging/execute.mdx @@ -2,16 +2,17 @@ id: 2e40525b-df0a-4ce3-8c7b-6139df1c6d66 title: execute slug: /reference/messaging/execute -description: Call a named section as a subroutine and return when it completes. +description: Call a section, a URL, or an inline SWML document as a subroutine. max-toc-depth: 3 --- -Call a named section as a subroutine. Execution continues in the called section, then returns to the -caller when the section completes (via [`return`](/docs/swml/reference/messaging/return) or by -reaching the end of the section). +Call another SWML destination as a subroutine: a section of the current document, a URL that +returns a SWML document, or an inline SWML document. Execution continues in the destination and +comes back to the caller when it finishes, either through +[`return`](/docs/swml/reference/messaging/return) or by reaching the end. -The destination must be the **name of a section** defined in the current document — `execute` does not -accept URLs or inline documents in the messaging context. +To hand the message off without coming back, use +[`transfer`](/docs/swml/reference/messaging/transfer). ## **Properties** @@ -22,31 +23,105 @@ accept URLs or inline documents in the messaging context. - Name of the section to execute. Must reference a section defined in the current document. + What to execute. One of: + + - the name of a section in the current document + - a URL (`http` or `https`) that returns a SWML document + - an inline SWML document, supplied as a JSON string + + Anything that is not an `http` or `https` URL and does not parse as a JSON object is treated as + a section name. A URL destination is always fetched with a `POST`: unlike + [`transfer`](/docs/swml/reference/messaging/transfer), `execute` has no `method` property. - Parameters accessible as `params.*` in the called section. Replaces (does not merge with) any outer - `params` from the caller. + Parameters accessible as `params.*` in the destination. Replaces, and never merges with, any + outer `params` from the caller. When `dest` is a URL, they are also sent in the request body. +`execute` also accepts a bare string in place of the object, so `- execute: greet` is shorthand for +an `execute` whose only property is `dest`. The shorthand does not expand `%{...}` placeholders, so +a destination built from a variable needs the object form. + +## **Destination forms** + +SignalWire infers the form from the value of `dest`; there is no separate type property. A value +beginning with `http://` or `https://` is a URL, a value that parses as a JSON object is an inline +document, and anything else is a section name. + +What the destination can read depends on which form it is: + +| `dest` form | What the destination can read | +|---|---| +| Section in the current document | Everything the caller can read, including the runtime variables set so far, with `params` replaced by this step's `params` | +| URL | `message` and this step's `params` | +| Inline document | `message` and this step's `params` | + +A URL or inline destination runs as a separate document, including the inline form where nothing is +fetched. It does not inherit the caller's runtime variables, so `params` is the only way to pass a +computed value into one. What comes back afterwards is the same for all three forms, and is covered +under [Variables](#variables). + +When `dest` is a URL, SignalWire sends the +[inbound message webhook payload](/docs/apis/rest/swml-webhook/webhooks/inbound-message-webhook) to +it with a `POST`: the inbound `message`, this step's `params`, and the runtime variables the +document has accumulated so far as `vars`. Your server can read `vars` and fold those values into +the document it returns. SignalWire waits up to 5 seconds for the response and does not retry. See +the [messaging webhook payload](/docs/swml/reference/messaging#webhook-payload) for the full field +reference. + +A URL or inline destination that defines its own `sections` uses those sections for the duration of +the `execute` step. It cannot reach a section that exists only in the caller, and the caller's +sections are back in place once it returns. + +### Inline documents + +An inline document must be a JSON **string**, not an object or mapping written directly in your +SWML. Its content has to parse as a JSON object holding exactly one of `reply`, `sections`, or +`receive`. A full `sections.main` is not required, so `{"reply": "Thanks!"}` is a valid inline +document. A bare array of steps is not accepted, and inline documents are JSON only, not YAML. + +### When a destination is malformed + +Because a section name is the fallback, a malformed `dest` never reports a fetch or parse error. A +scheme typo such as `htp://example.com`, a JSON syntax error, a JSON array, an object passed where +a string was expected, and a document nested more deeply than the JSON parser accepts all fall +through to a section lookup and fail with `swml_section_not_found` rather than +`swml_execute_document_invalid`. An empty `dest` fails the same way. + ## **Variables** -After the called section completes, `execute` exposes the following variable in the caller's -context. Variables that the subroutine itself set via [`reply`](/docs/swml/reference/messaging/reply) +After the destination completes, `execute` exposes the following variable in the caller's +context. Variables that the destination itself set via [`reply`](/docs/swml/reference/messaging/reply) or [`request`](/docs/swml/reference/messaging/request) — `reply_result`, `reply_message_id`, `request_result`, `request_response`, `request_response_code`, `request_response_body` — are -also propagated back to the caller automatically; see those methods for details. +also propagated back to the caller automatically; see those methods for details. Nothing else +crosses back: any other variable the destination set stays with it. - The value supplied to [`return`](/docs/swml/reference/messaging/return) inside the called - section. Set only when the subroutine called `return` with an argument. Absent when the - subroutine ran to completion without `return`. + The value supplied to [`return`](/docs/swml/reference/messaging/return) inside the destination. + A bare `return`, or `return: null`, still sets it, to an empty value. It is left untouched only + when the destination ran to completion without any `return`, in which case a value from an + earlier `execute` remains visible. +## **Errors** + +A failed `execute` marks the inbound message `failed` with one of these codes. See +[Errors](/docs/swml/reference/errors) for the full list. + +- `swml_section_not_found` — `dest` was read as a section name and no such section exists in the + document that is currently in scope. +- `swml_execute_fetch_failed` — the URL could not be fetched, whether from a non-2xx response, a + timeout, an unparseable URL, or a body that is neither JSON nor YAML. +- `swml_execute_document_invalid` — the fetched or inline document is not a valid messaging + document. +- `swml_goto_label_not_found` — the destination left a [`goto`](/docs/swml/reference/messaging/goto) + whose label it could not find. The caller does not resume. + ## **Examples** ### Calling a subroutine @@ -98,6 +173,89 @@ sections: +### Execute a document fetched from your server + +The document your server returns runs as a subroutine, then `main` picks up at the next step. +Because the fetched document cannot read the caller's runtime variables, pass anything it needs in +`params`. + + + +```yaml +version: 1.0.0 +sections: + main: + - execute: + dest: "https://example.com/account-lookup" + params: + caller: "%{message.from}" + - reply: + body: "%{return_value}" +``` + + +```json +{ + "version": "1.0.0", + "sections": { + "main": [ + { + "execute": { + "dest": "https://example.com/account-lookup", + "params": { + "caller": "%{message.from}" + } + } + }, + { + "reply": { + "body": "%{return_value}" + } + } + ] + } +} +``` + + + +### Execute an inline document + +The inline document is a JSON string, so in YAML it goes in single quotes and in JSON its own +quotes are escaped. + + + +```yaml +version: 1.0.0 +sections: + main: + - execute: '{"sections":{"main":[{"return":"Thanks for reaching out!"}]}}' + - reply: + body: "%{return_value}" +``` + + +```json +{ + "version": "1.0.0", + "sections": { + "main": [ + { + "execute": "{\"sections\":{\"main\":[{\"return\":\"Thanks for reaching out!\"}]}}" + }, + { + "reply": { + "body": "%{return_value}" + } + } + ] + } +} +``` + + + ### Branch on the subroutine return value @@ -208,4 +366,3 @@ sections: ``` - diff --git a/fern/products/swml/pages/reference/methods/messaging/goto.mdx b/fern/products/swml/pages/reference/methods/messaging/goto.mdx index 6fb542c70e..5e8c073022 100644 --- a/fern/products/swml/pages/reference/methods/messaging/goto.mdx +++ b/fern/products/swml/pages/reference/methods/messaging/goto.mdx @@ -10,7 +10,8 @@ Jump to a [`label`](/docs/swml/reference/messaging/label) within the current sec loops and conditional repetition. `goto` does **not** cross subroutine boundaries — it can only jump within the section that contains -the matching `label`. +the matching `label`. If the label cannot be found in the current section, the inbound message +fails with [`swml_goto_label_not_found`](/docs/swml/reference/errors). ## **Properties** @@ -26,8 +27,9 @@ the matching `label`. - Maximum number of times this `goto` can jump to its label. Once the limit is reached, the - section ends without running any further steps. + Maximum number of jumps allowed to this label. The count is tracked per label for the whole + document run, so several `goto` steps aimed at the same label share one budget. Once the limit + is reached, the section ends without running any further steps. diff --git a/fern/products/swml/pages/reference/methods/messaging/overview.mdx b/fern/products/swml/pages/reference/methods/messaging/overview.mdx index c9370ae311..c2a7b9c00d 100644 --- a/fern/products/swml/pages/reference/methods/messaging/overview.mdx +++ b/fern/products/swml/pages/reference/methods/messaging/overview.mdx @@ -19,22 +19,34 @@ For handling voice calls, see the [Calling SWML overview](/docs/swml/reference/c A Messaging SWML document follows the standard [SWML document structure](/docs/swml#document-structure) — a top-level `sections` map with `sections.main` as the entry point. Each section contains an array of -[messaging methods](#methods) that run sequentially. A document can execute up to **100 method -steps** per inbound message; once that ceiling is hit, execution stops. +[messaging methods](#methods) that run sequentially. + +An inbound message can execute up to **100 method steps** in total. That budget covers everything +the message runs, including sections reached through +[`execute`](/docs/swml/reference/messaging/execute) and documents reached through +[`transfer`](/docs/swml/reference/messaging/transfer); there is no separate cap on how many times a +document can execute or transfer. When the budget runs out, execution stops where it is. No error +code is reported and the message is not marked failed, so a conversation that ends early is the +only symptom. ## Webhook and variable payload [#webhook-payload] -When SignalWire fetches a Messaging SWML document from an external URL, it POSTs the +When SignalWire fetches a Messaging SWML document from an external URL, it sends the [inbound message webhook payload](/docs/apis/rest/swml-webhook/webhooks/inbound-message-webhook) -to your server — on the initial inbound-message fetch and on every fetch triggered by a -[`transfer`](/docs/swml/reference/messaging/transfer) step. Your server must respond with a valid -SWML document using one of these content types: `application/json`, `application/yaml`, or -`text/x-yaml`. +to your server. That happens on the initial inbound-message fetch, and again on any +[`transfer`](/docs/swml/reference/messaging/transfer) or +[`execute`](/docs/swml/reference/messaging/execute) step whose destination is a URL. + +Respond with a valid SWML document as JSON or YAML. The body is parsed as JSON first and then as +YAML, so the content type does not have to match exactly. An XML document, such as a LaML +document, is rejected with `json_or_yaml_required`. Inside the executing document, the same `message` and `params` fields are available for `%{...}` -variable expansion (for example, `%{message.from}`). As the script runs, methods also populate -the **`vars.*` runtime scope** — those values are not in the initial inbound payload, but they -are propagated across `transfer` boundaries and delivered on transfer-driven fetches. +variable expansion (for example, `%{message.from}`). As the script runs, methods also set +**top-level runtime variables** such as `request_result` and `reply_result`. Reference those +without a prefix: `%{request_result}`, not `%{vars.request_result}`. They are not part of the +initial inbound payload, and they are delivered to your server as a top-level `vars` object on +document fetches made once the script is already running. @@ -113,47 +125,55 @@ are propagated across `transfer` boundaries and delivered on transfer-driven fet - Runtime variable scope, populated by methods as the script executes. **Not part of the - initial inbound payload** — values appear here only after a method that sets them has run. - The full `vars` object is propagated across `transfer` boundaries and delivered as a top-level - `vars` field on the transfer-driven webhook payload. + The runtime variables the document has accumulated so far, sent as a top-level payload field on + document fetches made once the script is already running. Absent on the initial inbound fetch, + because no method has run yet. - The standard runtime entries are listed below. Methods may also write additional keys; see - each method's reference page for what it sets. + This is a payload field, not a variable scope. Inside a document the same values are read + without a `vars.` prefix, as [Runtime variables](#runtime-variables) below describes. - +### Runtime variables [#runtime-variables] - - Result of the most recent [`reply`](/docs/swml/reference/messaging/reply) step. Set after - the step completes. Possible values: `queued`, `failed`. - +Methods write these to the top level of the document's variable context as the script runs, so a +later step reads them directly: `%{reply_result}`, not `%{vars.reply_result}`. Methods may also +write additional keys; see each method's reference page for what it sets. - - Message segment ID of the most recent successful `reply`. Set after the step completes. - +A destination that is a section of the current document keeps whatever the caller has accumulated. +A URL or inline-document destination does not: it starts with a fresh context holding `message` and +the `params` the step passed, so these values reach it only through the `vars` payload field or +through `params`. - - Result of the most recent [`request`](/docs/swml/reference/messaging/request) step. Set - after the step completes. Possible values: `success`, `failed`, `timeout`, `limit_exceeded`. - + + Result of the most recent [`reply`](/docs/swml/reference/messaging/reply) step. Set after + the step completes. Possible values: `queued`, `failed`. + - - Parsed JSON response from the most recent `request`. Set after the step completes when - `save_variables: true` was set on the `request` step. Access nested fields with dot - notation, e.g., `%{vars.request_response.field_name}`. - + + Message segment ID of the most recent successful `reply`. Set after the step completes. + - - HTTP response code from the most recent `request`. Set after the step completes. - + + Result of the most recent [`request`](/docs/swml/reference/messaging/request) step. Set + after the step completes. Possible values: `success`, `failed`, `timeout`, `limit_exceeded`. + - - Raw response body from the most recent `request`. Set after the step completes. Truncated - to 64 KB. - + + Parsed JSON response from the most recent `request`. Set after the step completes when + `save_variables: true` was set on the `request` step. Access nested fields with dot + notation, e.g., `%{request_response.field_name}`. + - + + HTTP response code from the most recent `request`, as a string such as `"200"`. Set after the + step completes. When you match it in a [`switch`](/docs/swml/reference/messaging/switch), quote + the `case` key so it compares as a string. + + + + Raw response body from the most recent `request`. Set after the step completes. Truncated + to 64 KB. + See [Variables](/docs/swml/reference/variables) for variable-expansion syntax, deployment-mode differences, and tips on accessing nested fields and array elements. @@ -184,13 +204,13 @@ branches, or hand off the message to another document entirely. - Run another section of the current document, then resume when it finishes. Use to reuse a sequence of steps from more than one place. + Run a section, a URL, or an inline document as a subroutine, then resume when it finishes. Use to reuse a sequence of steps from more than one place. Hand control back to the section that called the current one, optionally passing a value the caller can read. - Hand off the rest of the conversation to a different SWML document hosted on an external server. The current document stops; the new one takes over. + Hand the rest of the conversation to a section, a URL, or an inline document. The current document stops and the destination takes over without returning. Restart from a named point earlier or later in the current section. Use for retry loops that repeat a step until it succeeds. diff --git a/fern/products/swml/pages/reference/methods/messaging/request.mdx b/fern/products/swml/pages/reference/methods/messaging/request.mdx index 728b0fff25..a8f6293df9 100644 --- a/fern/products/swml/pages/reference/methods/messaging/request.mdx +++ b/fern/products/swml/pages/reference/methods/messaging/request.mdx @@ -64,9 +64,11 @@ skipped). Read them with the `%{variable}` syntax in subsequent steps. 10 `request` calls). - - HTTP status code from the response. Set when the request was actually sent. Absent when - `request_result` is `limit_exceeded`. + + HTTP status code from the response, as a string such as `"200"`. Set when the request was + actually sent. Absent when `request_result` is `limit_exceeded`. When you match it in a + [`switch`](/docs/swml/reference/messaging/switch), quote the `case` key so it compares as a + string. diff --git a/fern/products/swml/pages/reference/methods/messaging/return.mdx b/fern/products/swml/pages/reference/methods/messaging/return.mdx index f87d8d3887..ff45d4d668 100644 --- a/fern/products/swml/pages/reference/methods/messaging/return.mdx +++ b/fern/products/swml/pages/reference/methods/messaging/return.mdx @@ -15,8 +15,9 @@ To return without a value, use `return: null`. ## **Properties** - - The value to return. Accepts any type. Use `null` to return no value. + + The value to return. Accepts any type. Use `null` to return no value; a bare `- return` step with + no value at all behaves the same way. ## **Variables** @@ -28,7 +29,8 @@ called from `main`, `return` stops execution immediately and no variable is set The value supplied to `return`. Available in the caller's context after the `execute` step that invoked this section completes. String values are expanded for `%{...}` placeholders - before storage; objects and arrays are stored as-is. + before storage. Objects and arrays are stored as-is, which means `%{...}` placeholders inside + them are **not** expanded. ## **Examples** @@ -77,7 +79,11 @@ sections: -### Return with no value (exit a section without setting `return_value`) +### Return with no value + +`return: null` hands control back to the caller with an empty `return_value`. That is not the same +as leaving it unset: a section that simply runs to its end sets nothing, so a value left by an +earlier `execute` is still visible to the caller. diff --git a/fern/products/swml/pages/reference/methods/messaging/transfer.mdx b/fern/products/swml/pages/reference/methods/messaging/transfer.mdx index c4bef92c41..0b4cb9f44b 100644 --- a/fern/products/swml/pages/reference/methods/messaging/transfer.mdx +++ b/fern/products/swml/pages/reference/methods/messaging/transfer.mdx @@ -2,16 +2,23 @@ id: a86b1e82-0cdd-4c54-8b35-932839347bcb title: transfer slug: /reference/messaging/transfer -description: Tail-call to a new SWML document fetched from a URL. +description: Tail-call to a section, a URL, or an inline SWML document. max-toc-depth: 3 --- -Fetch and execute a new SWML document from a URL. This is a **tail call** — it replaces the current -document and does not return. Steps after `transfer`, including steps in calling sections, are +Hand off the rest of the inbound message to another SWML destination: a section of the current +document, a URL that returns a SWML document, or an inline SWML document. This is a **tail call**, +so execution never comes back. Steps after `transfer`, including steps in calling sections, are skipped. -In the messaging context, `transfer.dest` must be a URL — it does not accept a section name or an -inline document. +To call a destination and then pick up where you left off, use +[`execute`](/docs/swml/reference/messaging/execute) instead. + + +A failed `transfer` is still a tail call. When the destination cannot be resolved, because the +section does not exist, the fetch failed, or the document is invalid, execution stops and the +inbound message is marked `failed`. The steps after `transfer` do not run as a fallback. + ## **Properties** @@ -22,37 +29,102 @@ inline document. - URL (`http` or `https`) to fetch the new SWML document from. Authentication can be set in the URL in - the format `username:password@url`. + Where to transfer to. One of: + + - the name of a section in the current document + - a URL (`http` or `https`) that returns a SWML document + - an inline SWML document, supplied as a JSON string + + Anything that is not an `http` or `https` URL and does not parse as a JSON object is treated as + a section name. For a URL, authentication can be set in the URL in the format + `username:password@url`. - HTTP method for the fetch request. One of `GET`, `POST`, `PUT`, `PATCH`, or `DELETE`. + HTTP method for the fetch request. Applies only when `dest` is a URL, and is ignored for section + and inline-document destinations. One of `GET`, `POST`, `PUT`, `PATCH`, or `DELETE`. Other values + are not rejected up front, but a method the platform cannot send fails the transfer with + `swml_transfer_fetch_failed`. - Parameters to include in the request body of the fetch. Available as `params.*` in the transferred - document. + Values made available as `params.*` in the destination. Replaces, and never merges with, the + caller's `params`. When `dest` is a URL, they are also sent in the request body. -## **Webhook payload sent to `dest`** +`transfer` also accepts a bare string in place of the object, so `- transfer: support` is shorthand +for a `transfer` whose only property is `dest`. The shorthand does not expand `%{...}` placeholders, +so a destination built from a variable needs the object form. + +## **Destination forms** + +SignalWire infers the form from the value of `dest`; there is no separate type property. A value +beginning with `http://` or `https://` is a URL, a value that parses as a JSON object is an inline +document, and anything else is a section name. + +What the destination can read depends on which form it is: + +| `dest` form | What the destination can read | +|---|---| +| Section in the current document | Everything the caller can read, including the runtime variables set so far, with `params` replaced by this step's `params` | +| URL | `message` and this step's `params` | +| Inline document | `message` and this step's `params` | + +A URL or inline destination runs as a separate document, including the inline form where nothing is +fetched. It does not inherit the caller's runtime variables, so `params` is the only way to pass a +computed value into one. For a URL destination those variables are still sent outward in the +request body as `vars`, which your server can read and fold into the document it returns. + +Nothing comes back to the caller from any form of `transfer`. Use `execute` when you need a value +returned. + +### Inline documents + +An inline document must be a JSON **string**, not an object or mapping written directly in your +SWML. Its content has to parse as a JSON object holding exactly one of `reply`, `sections`, or +`receive`. A full `sections.main` is not required, so `{"reply": "Thanks!"}` is a valid inline +document. A bare array of steps is not accepted, and inline documents are JSON only, not YAML. + +### When a destination is malformed -When `transfer` fetches an external document, SignalWire `POST`s the +Because a section name is the fallback, a malformed `dest` never reports a fetch or parse error. A +scheme typo such as `htp://example.com`, a JSON syntax error, a JSON array, an object passed where +a string was expected, and a document nested more deeply than the JSON parser accepts all fall +through to a section lookup and fail with `swml_section_not_found`. + +## **Webhook payload sent to a URL destination** + +When `dest` is a URL, SignalWire sends the [inbound message webhook payload](/docs/apis/rest/swml-webhook/webhooks/inbound-message-webhook) -to `dest`: +to it using `method`: - `message` — the original inbound message that triggered this SWML document. - `params` — the values supplied to this `transfer` step. -- `vars` — runtime variables propagated from the current document (`request_result`, +- `vars` — the runtime variables the current document has accumulated (`request_result`, `reply_result`, `request_response`, `request_response_code`, `request_response_body`, `reply_message_id`). Present on transfer-driven fetches; absent on the initial inbound fetch. +SignalWire waits up to 5 seconds for the response and does not retry. + +## **Errors** + +A failed `transfer` marks the inbound message `failed` with one of these codes. See +[Errors](/docs/swml/reference/errors) for the full list. + +- `swml_transfer_failed` — `dest` was missing or empty. +- `swml_section_not_found` — `dest` was read as a section name and no such section exists in the + current document. +- `swml_transfer_fetch_failed` — the URL could not be fetched, whether from a non-2xx response, a + timeout, an unparseable URL, an unsupported HTTP method, or a body that is neither JSON nor YAML. +- `swml_transfer_document_invalid` — the fetched or inline document is not a valid messaging + document. + ## **Examples** ### Transfer to an external SWML handler @@ -90,6 +162,104 @@ sections: +### Transfer to a section of the same document + + + +```yaml +version: 1.0.0 +sections: + main: + - switch: + variable: message.body + transform: lowercase_trim + case: + stop: + - transfer: + dest: opt_out + default: + - reply: + body: "Thanks for your message!" + opt_out: + - reply: + body: "You've been unsubscribed. Reply START to opt back in." +``` + + +```json +{ + "version": "1.0.0", + "sections": { + "main": [ + { + "switch": { + "variable": "message.body", + "transform": "lowercase_trim", + "case": { + "stop": [ + { + "transfer": { + "dest": "opt_out" + } + } + ] + }, + "default": [ + { + "reply": { + "body": "Thanks for your message!" + } + } + ] + } + } + ], + "opt_out": [ + { + "reply": { + "body": "You've been unsubscribed. Reply START to opt back in." + } + } + ] + } +} +``` + + + +### Transfer to an inline document + +The inline document is a JSON string, so in YAML it goes in single quotes and in JSON its own +quotes are escaped. + + + +```yaml +version: 1.0.0 +sections: + main: + - transfer: + dest: '{"reply":{"body":"Our office is closed. We will text you back at 9am."}}' +``` + + +```json +{ + "version": "1.0.0", + "sections": { + "main": [ + { + "transfer": { + "dest": "{\"reply\":{\"body\":\"Our office is closed. We will text you back at 9am.\"}}" + } + } + ] + } +} +``` + + + ### Route to different handlers based on keyword @@ -164,5 +334,3 @@ sections: ``` - - diff --git a/fern/products/swml/pages/reference/variables.mdx b/fern/products/swml/pages/reference/variables.mdx index bc5eea8771..9adf30349d 100644 --- a/fern/products/swml/pages/reference/variables.mdx +++ b/fern/products/swml/pages/reference/variables.mdx @@ -70,9 +70,11 @@ sections: A Messaging document uses `%{...}` only. -**Reference a value.** Use a plain path like `%{message.from}` or `%{vars.reply_message_id}`. -Works for any field on `message`, `params`, or `vars`. If the path is valid but the value -isn't set, the placeholder resolves to an empty string. +**Reference a value.** Use a plain path like `%{message.from}` or `%{reply_message_id}`. Works for +any field on `message` or `params`, and for the +[runtime variables](/docs/swml/reference/messaging#runtime-variables) a document sets as it runs, +which are read without a `vars.` prefix. If the path is valid but the value isn't set, the +placeholder resolves to an empty string. @@ -105,7 +107,8 @@ for each SWML flavor. See the [Calling webhook and variable payload](/docs/swml/reference/calling#webhook-payload) and the [Messaging webhook and variable payload](/docs/swml/reference/messaging#webhook-payload) for the authoritative list of scopes (`call` / `message`, `params`, `vars`, `envs`) and every field -inside them. +inside them. In a Messaging document, `vars` is a field of the webhook payload rather than an +interpolation scope, so its values are read without the `vars.` prefix. ## Accessing nested data diff --git a/specs/swml/calling/Methods/connect/main.tsp b/specs/swml/calling/Methods/connect/main.tsp index 6038d7f527..d57bdb9f28 100644 --- a/specs/swml/calling/Methods/connect/main.tsp +++ b/specs/swml/calling/Methods/connect/main.tsp @@ -155,10 +155,12 @@ model ConnectDeviceBase { - A URL (http or https) that returns a SWML document - An inline SWML document (as a JSON string) - **Note:** This parameter is REQUIRED when connecting to a queue (when `to` starts with "queue:") + Applies only when connecting to a single queue destination — that is, when `to` starts with + `queue:`. It has no effect for any other destination type, or in the `serial`, `parallel`, and + `serial_parallel` forms. If omitted, the call hangs up once the bridge ends. """) @example("https://example.com/after-bridge.swml") - transfer_after_bridge?: string | SWMLVar; + execute_after_queue?: string | SWMLVar; @doc(""" An array of call state event names to be notified about. diff --git a/specs/swml/calling/Methods/enter_queue/main.tsp b/specs/swml/calling/Methods/enter_queue/main.tsp index ebbd9d38d5..a4abc3811e 100644 --- a/specs/swml/calling/Methods/enter_queue/main.tsp +++ b/specs/swml/calling/Methods/enter_queue/main.tsp @@ -17,9 +17,11 @@ model EnterQueueObject { Can be either: - A URL (http or https) that returns a SWML document - An inline SWML document (as a JSON string) + + If omitted, the call hangs up when the queue interaction ends. """) @example("https://example.com/post-call-survey") - transfer_after_bridge: string | SWMLVar; + execute_after_queue?: string | SWMLVar; @doc("HTTP or HTTPS URL to deliver queue status events. Default not set") @example("https://example.com/queue-status") @@ -41,7 +43,8 @@ model EnterQueue { Place the current call in a named queue where it will wait to be connected to an available agent or resource. While waiting, callers will hear music or custom audio. When an agent connects to the queue (using the connect method), the caller and agent are bridged together. - After the bridge completes, execution continues with the SWML script specified in transfer_after_bridge. + After the bridge completes, execution continues with the SWML specified in `execute_after_queue`, + or the call hangs up if that parameter is not set. """) @summary("enter_queue") enter_queue: EnterQueueObject; diff --git a/specs/swml/calling/Methods/execute/main.tsp b/specs/swml/calling/Methods/execute/main.tsp index 9226bdc44d..f1cca3896c 100644 --- a/specs/swml/calling/Methods/execute/main.tsp +++ b/specs/swml/calling/Methods/execute/main.tsp @@ -20,41 +20,48 @@ model ExecuteSwitch { default?: SWMLMethod[]; } +@summary("ExecuteTarget object") +model ExecuteTarget { + @doc(""" + Specifies what to execute. The value can be one of: + - `` - section in the current document to execute + - A URL (http or https) that returns a SWML document - Sends HTTP POST + - An inline SWML document, as a JSON or YAML string. It may be a full document with + `version` and `sections`, or just an array of instructions. + """) + @example("https://example.com/swml-handler") + dest: string; + + @doc("Named parameters to send to section or URL") + @example(#{ caller_id: "+15551234567", language: "en-US" }) + params?: { + ...TypeSpec.Record; + }; + + @doc("User-defined metadata, ignored by SignalWire") + @example(#{ request_id: "req_abc123", source: "ivr" }) + meta?: { + ...TypeSpec.Record; + }; + + @doc("The list of SWML instructions to be executed when the executed section or URL returns") + on_return?: SWMLMethod[]; + + @doc(""" + Action to take based on the result of the call. This will run once the peer leg of the call has ended. + The form of this block selects how it runs: an array of instructions runs as a `cond`, and an + object runs as a `switch` matched against `return_value`. + """) + result?: ExecuteSwitch | Cond.cond; +} + @summary("execute Method") model Execute { @doc(""" - Execute a specified section or URL as a subroutine, and upon completion, return to the current document. + Execute a specified section, URL, or inline document as a subroutine, and upon completion, return to the current document. Use the return statement to pass any return values or objects back to the current document. + + Accepts either an object, or a string used directly as `dest`. """) - execute: { - @doc(""" - Specifies what to execute. The value can be one of: - - `` - section in the current document to execute - - A URL (http or https) that returns a SWML document - Sends HTTP POST - - An inline SWML document (as a JSON string) - """) - @example("https://example.com/swml-handler") - dest: string; - - @doc("Named parameters to send to section or URL") - @example(#{ caller_id: "+15551234567", language: "en-US" }) - params?: { - ...TypeSpec.Record; - }; - - @doc("User-defined metadata, ignored by SignalWire") - @example(#{ request_id: "req_abc123", source: "ivr" }) - meta?: { - ...TypeSpec.Record; - }; - - @doc("The list of SWML instructions to be executed when the executed section or URL returns") - on_return?: SWMLMethod[]; - - @doc(""" - Action to take based on the result of the call. This will run once the peer leg of the call has ended. - Will use the switch method when the return_value is an object, and will use the cond method when the return_value is an array. - """) - result?: ExecuteSwitch | Cond.cond; - }; + execute: string | ExecuteTarget; } diff --git a/specs/swml/calling/Methods/goto/main.tsp b/specs/swml/calling/Methods/goto/main.tsp index b028ba7665..1d8dfe6837 100644 --- a/specs/swml/calling/Methods/goto/main.tsp +++ b/specs/swml/calling/Methods/goto/main.tsp @@ -2,23 +2,33 @@ import "../label"; import "../../../Shared/Types/main.tsp"; namespace SWML.Calling; + +@summary("GotoTarget object") +model GotoTarget { + label: Label.label; + + @doc("A JavaScript condition that determines whether to perform the jump. If the condition evaluates to true, the jump is executed. If omitted, the jump is unconditional.") + @example("vars.retry_count < 3") + when?: string; + + @doc("The maximum number of times to perform the jump. Must be a number between 1 and 100. Default `100`.") + @minValue(1) + @maxValue(100) + @example(3) + max?: integer | SWMLVar = 100; +} + @summary("goto Method") model Goto { @doc(""" Jump to a label within the current section, optionally based on a condition. The goto method will only navigate to a label within the same section. - """) - goto: { - label: Label.label; - @doc("A JavaScript condition that determines whether to perform the jump. If the condition evaluates to true, the jump is executed. If omitted, the jump is unconditional.") - @example("vars.retry_count < 3") - when?: string; + Jumps count against the same per-call budget as `transfer` — 32 combined, or 8 without + caller interaction. Exceeding it ends the call with + [`relay_script_too_many_transfers`](/docs/swml/reference/errors). - @doc("The maximum number of times to perform the jump. Must be a number between 1 and 100. Default `100`.") - @minValue(1) - @maxValue(100) - @example(3) - max?: integer | SWMLVar = 100; - }; + Accepts either an object, or a string used directly as `label`. + """) + goto: string | GotoTarget; } diff --git a/specs/swml/calling/Methods/transfer/main.tsp b/specs/swml/calling/Methods/transfer/main.tsp index 51c7414836..d0f662bbe2 100644 --- a/specs/swml/calling/Methods/transfer/main.tsp +++ b/specs/swml/calling/Methods/transfer/main.tsp @@ -1,41 +1,50 @@ namespace SWML.Calling; -@summary("transfer Method") -model Transfer { - @summary("transfer") + +@summary("TransferTarget object") +model TransferTarget { @doc(""" - Transfer the execution of the script to a different SWML section, URL, or Relay application. - Once the transfer is complete, the script will continue executing SWML from the new location. + Specifies where to transfer to. The value can be one of: + - - section in the SWML document to jump to + - A URL (http or https) - URL to fetch next document from. Sends HTTP POST. + Authentication can also be set in the URL in the format of username:password@url. + + Unlike `execute`, `transfer` does not accept an inline SWML document. """) - transfer: { - @doc(""" - Specifies where to transfer to. The value can be one of: - - - section in the SWML document to jump to - - A URL (http or https) - URL to fetch next document from. Sends HTTP POST. - Authentication can also be set in the URL in the format of username:password@url. - - An inline SWML document (as a JSON string) - """) - @example("https://example.com/transfer-handler") - dest: string; + @example("https://example.com/transfer-handler") + dest: string; - @doc(""" - Named parameters to send to transfer destination. - Accepts an object mapping variable names to values. - Default is not set. - """) - @example(#{ department: "sales", priority: "high" }) - params?: { - ...TypeSpec.Record; - }; + @doc(""" + Named parameters to send to transfer destination. + Accepts an object mapping variable names to values. + Default is not set. + """) + @example(#{ department: "sales", priority: "high" }) + params?: { + ...TypeSpec.Record; + }; - @doc(""" - User data, ignored by SignalWire. - Accepts an object mapping variable names to values. - Default is not set. - """) - @example(#{ transfer_reason: "escalation", original_agent: "agent_001" }) - meta?: { - ...TypeSpec.Record; - }; + @doc(""" + User data, ignored by SignalWire. + Accepts an object mapping variable names to values. + Default is not set. + """) + @example(#{ transfer_reason: "escalation", original_agent: "agent_001" }) + meta?: { + ...TypeSpec.Record; }; } + +@summary("transfer Method") +model Transfer { + @summary("transfer") + @doc(""" + Transfer the execution of the script to a different SWML section or URL. + Execution continues in the new location and does not return — instructions after + `transfer` in the current section are skipped. Use `execute` to run a section or + document as a subroutine and come back. + + Accepts either an object, or a string used directly as `dest`. + """) + transfer: string | TransferTarget; +} diff --git a/specs/swml/calling/tsp-output/@typespec/json-schema/SWMLObject.json b/specs/swml/calling/tsp-output/@typespec/json-schema/SWMLObject.json index 0b64e36a34..0454ca135a 100644 --- a/specs/swml/calling/tsp-output/@typespec/json-schema/SWMLObject.json +++ b/specs/swml/calling/tsp-output/@typespec/json-schema/SWMLObject.json @@ -360,7 +360,7 @@ "properties": { "enter_queue": { "$ref": "#/$defs/EnterQueueObject", - "description": "Place the current call in a named queue where it will wait to be connected to an available agent or resource.\nWhile waiting, callers will hear music or custom audio.\nWhen an agent connects to the queue (using the connect method), the caller and agent are bridged together.\nAfter the bridge completes, execution continues with the SWML script specified in transfer_after_bridge.", + "description": "Place the current call in a named queue where it will wait to be connected to an available agent or resource.\nWhile waiting, callers will hear music or custom audio.\nWhen an agent connects to the queue (using the connect method), the caller and agent are bridged together.\nAfter the bridge completes, execution continues with the SWML specified in `execute_after_queue`,\nor the call hangs up if that parameter is not set.", "title": "enter_queue" } }, @@ -376,70 +376,15 @@ "type": "object", "properties": { "execute": { - "type": "object", - "properties": { - "dest": { - "type": "string", - "examples": [ - "https://example.com/swml-handler" - ], - "description": "Specifies what to execute. The value can be one of:\n- `` - section in the current document to execute\n- A URL (http or https) that returns a SWML document - Sends HTTP POST\n- An inline SWML document (as a JSON string)" - }, - "params": { - "type": "object", - "properties": {}, - "unevaluatedProperties": {}, - "examples": [ - { - "caller_id": "+15551234567", - "language": "en-US" - } - ], - "description": "Named parameters to send to section or URL" - }, - "meta": { - "type": "object", - "properties": {}, - "unevaluatedProperties": {}, - "examples": [ - { - "request_id": "req_abc123", - "source": "ivr" - } - ], - "description": "User-defined metadata, ignored by SignalWire" - }, - "on_return": { - "type": "array", - "items": { - "$ref": "#/$defs/SWMLMethod" - }, - "description": "The list of SWML instructions to be executed when the executed section or URL returns" + "anyOf": [ + { + "type": "string" }, - "result": { - "anyOf": [ - { - "$ref": "#/$defs/ExecuteSwitch" - }, - { - "type": "array", - "items": { - "$ref": "#/$defs/CondParams" - }, - "description": "Execute a sequence of instructions depending on the value of a JavaScript condition.", - "title": "cond" - } - ], - "description": "Action to take based on the result of the call. This will run once the peer leg of the call has ended.\nWill use the switch method when the return_value is an object, and will use the cond method when the return_value is an array." + { + "$ref": "#/$defs/ExecuteTarget" } - }, - "required": [ - "dest" ], - "unevaluatedProperties": { - "not": {} - }, - "description": "Execute a specified section or URL as a subroutine, and upon completion, return to the current document.\nUse the return statement to pass any return values or objects back to the current document." + "description": "Execute a specified section, URL, or inline document as a subroutine, and upon completion, return to the current document.\nUse the return statement to pass any return values or objects back to the current document.\n\nAccepts either an object, or a string used directly as `dest`." } }, "required": [ @@ -454,47 +399,15 @@ "type": "object", "properties": { "goto": { - "type": "object", - "properties": { - "label": { - "type": "string", - "examples": [ - "greeting" - ], - "description": "Mark any point of the SWML section with a label so that goto can jump to it." - }, - "when": { - "type": "string", - "examples": [ - "vars.retry_count < 3" - ], - "description": "A JavaScript condition that determines whether to perform the jump. If the condition evaluates to true, the jump is executed. If omitted, the jump is unconditional." + "anyOf": [ + { + "type": "string" }, - "max": { - "anyOf": [ - { - "type": "integer" - }, - { - "$ref": "#/$defs/SWMLVar" - } - ], - "default": 100, - "examples": [ - 3 - ], - "minimum": 1, - "maximum": 100, - "description": "The maximum number of times to perform the jump. Must be a number between 1 and 100. Default `100`." + { + "$ref": "#/$defs/GotoTarget" } - }, - "required": [ - "label" ], - "unevaluatedProperties": { - "not": {} - }, - "description": "Jump to a label within the current section, optionally based on a condition.\nThe goto method will only navigate to a label within the same section." + "description": "Jump to a label within the current section, optionally based on a condition.\nThe goto method will only navigate to a label within the same section.\n\nJumps count against the same per-call budget as `transfer` — 32 combined, or 8 without\ncaller interaction. Exceeding it ends the call with\n[`relay_script_too_many_transfers`](/docs/swml/reference/errors).\n\nAccepts either an object, or a string used directly as `label`." } }, "required": [ @@ -2105,47 +2018,15 @@ "type": "object", "properties": { "transfer": { - "type": "object", - "properties": { - "dest": { - "type": "string", - "examples": [ - "https://example.com/transfer-handler" - ], - "description": "Specifies where to transfer to. The value can be one of:\n- - section in the SWML document to jump to\n- A URL (http or https) - URL to fetch next document from. Sends HTTP POST.\n Authentication can also be set in the URL in the format of username:password@url.\n- An inline SWML document (as a JSON string)" - }, - "params": { - "type": "object", - "properties": {}, - "unevaluatedProperties": {}, - "examples": [ - { - "department": "sales", - "priority": "high" - } - ], - "description": "Named parameters to send to transfer destination.\nAccepts an object mapping variable names to values.\nDefault is not set." + "anyOf": [ + { + "type": "string" }, - "meta": { - "type": "object", - "properties": {}, - "unevaluatedProperties": {}, - "examples": [ - { - "transfer_reason": "escalation", - "original_agent": "agent_001" - } - ], - "description": "User data, ignored by SignalWire.\nAccepts an object mapping variable names to values.\nDefault is not set." + { + "$ref": "#/$defs/TransferTarget" } - }, - "required": [ - "dest" ], - "unevaluatedProperties": { - "not": {} - }, - "description": "Transfer the execution of the script to a different SWML section, URL, or Relay application.\nOnce the transfer is complete, the script will continue executing SWML from the new location.", + "description": "Transfer the execution of the script to a different SWML section or URL.\nExecution continues in the new location and does not return — instructions after\n`transfer` in the current section are skipped. Use `execute` to run a section or\ndocument as a subroutine and come back.\n\nAccepts either an object, or a string used directly as `dest`.", "title": "transfer" } }, @@ -3079,7 +2960,7 @@ ], "description": "Webhook URL to send call status change notifications to. Authentication can also be set in the URL in the format of `username:password@url`." }, - "transfer_after_bridge": { + "execute_after_queue": { "anyOf": [ { "type": "string" @@ -3091,7 +2972,7 @@ "examples": [ "https://example.com/after-bridge.swml" ], - "description": "SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends.\nCan be either:\n- A URL (http or https) that returns a SWML document\n- An inline SWML document (as a JSON string)\n\n**Note:** This parameter is REQUIRED when connecting to a queue (when `to` starts with \"queue:\")" + "description": "SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends.\nCan be either:\n- A URL (http or https) that returns a SWML document\n- An inline SWML document (as a JSON string)\n\nApplies only when connecting to a single queue destination — that is, when `to` starts with\n`queue:`. It has no effect for any other destination type, or in the `serial`, `parallel`, and\n`serial_parallel` forms. If omitted, the call hangs up once the bridge ends." }, "call_state_events": { "type": "array", @@ -3394,7 +3275,7 @@ ], "description": "Webhook URL to send call status change notifications to. Authentication can also be set in the URL in the format of `username:password@url`." }, - "transfer_after_bridge": { + "execute_after_queue": { "anyOf": [ { "type": "string" @@ -3406,7 +3287,7 @@ "examples": [ "https://example.com/after-bridge.swml" ], - "description": "SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends.\nCan be either:\n- A URL (http or https) that returns a SWML document\n- An inline SWML document (as a JSON string)\n\n**Note:** This parameter is REQUIRED when connecting to a queue (when `to` starts with \"queue:\")" + "description": "SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends.\nCan be either:\n- A URL (http or https) that returns a SWML document\n- An inline SWML document (as a JSON string)\n\nApplies only when connecting to a single queue destination — that is, when `to` starts with\n`queue:`. It has no effect for any other destination type, or in the `serial`, `parallel`, and\n`serial_parallel` forms. If omitted, the call hangs up once the bridge ends." }, "call_state_events": { "type": "array", @@ -3638,7 +3519,7 @@ ], "description": "Webhook URL to send call status change notifications to. Authentication can also be set in the URL in the format of `username:password@url`." }, - "transfer_after_bridge": { + "execute_after_queue": { "anyOf": [ { "type": "string" @@ -3650,7 +3531,7 @@ "examples": [ "https://example.com/after-bridge.swml" ], - "description": "SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends.\nCan be either:\n- A URL (http or https) that returns a SWML document\n- An inline SWML document (as a JSON string)\n\n**Note:** This parameter is REQUIRED when connecting to a queue (when `to` starts with \"queue:\")" + "description": "SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends.\nCan be either:\n- A URL (http or https) that returns a SWML document\n- An inline SWML document (as a JSON string)\n\nApplies only when connecting to a single queue destination — that is, when `to` starts with\n`queue:`. It has no effect for any other destination type, or in the `serial`, `parallel`, and\n`serial_parallel` forms. If omitted, the call hangs up once the bridge ends." }, "call_state_events": { "type": "array", @@ -3882,7 +3763,7 @@ ], "description": "Webhook URL to send call status change notifications to. Authentication can also be set in the URL in the format of `username:password@url`." }, - "transfer_after_bridge": { + "execute_after_queue": { "anyOf": [ { "type": "string" @@ -3894,7 +3775,7 @@ "examples": [ "https://example.com/after-bridge.swml" ], - "description": "SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends.\nCan be either:\n- A URL (http or https) that returns a SWML document\n- An inline SWML document (as a JSON string)\n\n**Note:** This parameter is REQUIRED when connecting to a queue (when `to` starts with \"queue:\")" + "description": "SWML to execute after the bridge completes. This defines what should happen after the call is connected and the bridge ends.\nCan be either:\n- A URL (http or https) that returns a SWML document\n- An inline SWML document (as a JSON string)\n\nApplies only when connecting to a single queue destination — that is, when `to` starts with\n`queue:`. It has no effect for any other destination type, or in the `serial`, `parallel`, and\n`serial_parallel` forms. If omitted, the call hangs up once the bridge ends." }, "call_state_events": { "type": "array", @@ -3944,7 +3825,7 @@ ], "description": "Name of the queue to enter. If a queue with this name does not exist, it will be automatically created." }, - "transfer_after_bridge": { + "execute_after_queue": { "anyOf": [ { "type": "string" @@ -3956,7 +3837,7 @@ "examples": [ "https://example.com/post-call-survey" ], - "description": "SWML to execute after the bridge completes. This defines what should happen after the call is connected to an agent and the bridge ends.\nCan be either:\n- A URL (http or https) that returns a SWML document\n- An inline SWML document (as a JSON string)" + "description": "SWML to execute after the bridge completes. This defines what should happen after the call is connected to an agent and the bridge ends.\nCan be either:\n- A URL (http or https) that returns a SWML document\n- An inline SWML document (as a JSON string)\n\nIf omitted, the call hangs up when the queue interaction ends." }, "status_url": { "type": "string", @@ -3999,50 +3880,121 @@ } }, "required": [ - "queue_name", - "transfer_after_bridge" + "queue_name" ], "unevaluatedProperties": { "not": {} }, "title": "EnterQueueObject object" }, - "ExecuteSwitch": { + "ExecuteTarget": { "type": "object", "properties": { - "variable": { + "dest": { "type": "string", "examples": [ - "return_value" + "https://example.com/swml-handler" ], - "description": "Name of the variable whose value needs to be compared. If not provided, it will check the `return_value` variable.\nCan be one of the listed set of variables, or a string to represent a custom variable." + "description": "Specifies what to execute. The value can be one of:\n- `` - section in the current document to execute\n- A URL (http or https) that returns a SWML document - Sends HTTP POST\n- An inline SWML document, as a JSON or YAML string. It may be a full document with\n `version` and `sections`, or just an array of instructions." }, - "case": { + "params": { "type": "object", "properties": {}, - "unevaluatedProperties": { - "type": "array", - "items": { - "$ref": "#/$defs/SWMLMethod" + "unevaluatedProperties": {}, + "examples": [ + { + "caller_id": "+15551234567", + "language": "en-US" } - }, - "description": "Object of values mapped to array of instructions to execute" + ], + "description": "Named parameters to send to section or URL" }, - "default": { + "meta": { + "type": "object", + "properties": {}, + "unevaluatedProperties": {}, + "examples": [ + { + "request_id": "req_abc123", + "source": "ivr" + } + ], + "description": "User-defined metadata, ignored by SignalWire" + }, + "on_return": { "type": "array", "items": { "$ref": "#/$defs/SWMLMethod" }, - "description": "Array of instructions to execute if no cases match" + "description": "The list of SWML instructions to be executed when the executed section or URL returns" + }, + "result": { + "anyOf": [ + { + "$ref": "#/$defs/ExecuteSwitch" + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/CondParams" + }, + "description": "Execute a sequence of instructions depending on the value of a JavaScript condition.", + "title": "cond" + } + ], + "description": "Action to take based on the result of the call. This will run once the peer leg of the call has ended.\nThe form of this block selects how it runs: an array of instructions runs as a `cond`, and an\nobject runs as a `switch` matched against `return_value`." } }, "required": [ - "case" + "dest" ], "unevaluatedProperties": { "not": {} }, - "title": "ExecuteSwitch object" + "title": "ExecuteTarget object" + }, + "GotoTarget": { + "type": "object", + "properties": { + "label": { + "type": "string", + "examples": [ + "greeting" + ], + "description": "Mark any point of the SWML section with a label so that goto can jump to it." + }, + "when": { + "type": "string", + "examples": [ + "vars.retry_count < 3" + ], + "description": "A JavaScript condition that determines whether to perform the jump. If the condition evaluates to true, the jump is executed. If omitted, the jump is unconditional." + }, + "max": { + "anyOf": [ + { + "type": "integer" + }, + { + "$ref": "#/$defs/SWMLVar" + } + ], + "default": 100, + "examples": [ + 3 + ], + "minimum": 1, + "maximum": 100, + "description": "The maximum number of times to perform the jump. Must be a number between 1 and 100. Default `100`." + } + }, + "required": [ + "label" + ], + "unevaluatedProperties": { + "not": {} + }, + "title": "GotoTarget object" }, "TranscribeAction": { "oneOf": [ @@ -4763,6 +4715,49 @@ "type": "string" } }, + "TransferTarget": { + "type": "object", + "properties": { + "dest": { + "type": "string", + "examples": [ + "https://example.com/transfer-handler" + ], + "description": "Specifies where to transfer to. The value can be one of:\n- - section in the SWML document to jump to\n- A URL (http or https) - URL to fetch next document from. Sends HTTP POST.\n Authentication can also be set in the URL in the format of username:password@url.\n\nUnlike `execute`, `transfer` does not accept an inline SWML document." + }, + "params": { + "type": "object", + "properties": {}, + "unevaluatedProperties": {}, + "examples": [ + { + "department": "sales", + "priority": "high" + } + ], + "description": "Named parameters to send to transfer destination.\nAccepts an object mapping variable names to values.\nDefault is not set." + }, + "meta": { + "type": "object", + "properties": {}, + "unevaluatedProperties": {}, + "examples": [ + { + "transfer_reason": "escalation", + "original_agent": "agent_001" + } + ], + "description": "User data, ignored by SignalWire.\nAccepts an object mapping variable names to values.\nDefault is not set." + } + }, + "required": [ + "dest" + ], + "unevaluatedProperties": { + "not": {} + }, + "title": "TransferTarget object" + }, "PayParameters": { "type": "object", "properties": { @@ -7393,6 +7388,43 @@ "description": "Per-destination model used inside `serial`, `parallel`, and `serial_parallel` arrays.\n\nContains only the properties that apply to an individual destination:\naddressing, caller-ID overrides, SIP auth, per-leg timeouts/confirmations,\nand stream-specific settings.", "title": "ConnectDestination object" }, + "ExecuteSwitch": { + "type": "object", + "properties": { + "variable": { + "type": "string", + "examples": [ + "return_value" + ], + "description": "Name of the variable whose value needs to be compared. If not provided, it will check the `return_value` variable.\nCan be one of the listed set of variables, or a string to represent a custom variable." + }, + "case": { + "type": "object", + "properties": {}, + "unevaluatedProperties": { + "type": "array", + "items": { + "$ref": "#/$defs/SWMLMethod" + } + }, + "description": "Object of values mapped to array of instructions to execute" + }, + "default": { + "type": "array", + "items": { + "$ref": "#/$defs/SWMLMethod" + }, + "description": "Array of instructions to execute if no cases match" + } + }, + "required": [ + "case" + ], + "unevaluatedProperties": { + "not": {} + }, + "title": "ExecuteSwitch object" + }, "TranscribeStartAction": { "type": "object", "properties": { diff --git a/specs/swml/messaging/Methods/execute/main.tsp b/specs/swml/messaging/Methods/execute/main.tsp index 3fe9694253..9661bd0c21 100644 --- a/specs/swml/messaging/Methods/execute/main.tsp +++ b/specs/swml/messaging/Methods/execute/main.tsp @@ -1,24 +1,46 @@ namespace SWML.Messaging; + +@summary("ExecuteTarget object") +model ExecuteTarget { + @doc(""" + What to execute. The value can be one of: + - A section name — a section defined in the current document. + - A URL (`http` or `https`) that returns a SWML document. SignalWire always sends an HTTP + POST; there is no `method` parameter on `execute`. + - An inline SWML document, supplied as a JSON string. It may be a full document with + `sections`, or a bare `reply` or `receive` document. + + A value that is not an `http`/`https` URL and does not parse as a JSON object is treated + as a section name. A malformed inline document — a syntax error, a JSON array, or a value + written as a native object rather than a string — therefore fails with + `swml_section_not_found`, not `swml_execute_document_invalid`. + """) + @example("greet") + dest: string; + + @doc("Parameters accessible as `params.*` in the destination. Replaces (does not merge with) any outer `params` from the caller.") + @example(#{ name: "%{message.from}" }) + params?: { + ...TypeSpec.Record; + }; +} + @summary("execute Method") model Execute { @summary("execute") @doc(""" - Call a named section as a subroutine. Execution continues in the called section, - then returns to the caller when the section completes — either via `return` or by - reaching the end of the section. The destination must be the name of a section - defined in the current document; URLs and inline documents are not accepted in the - messaging context. - """) - execute: { - @doc("Name of the section to execute. Must reference a section in the current document.") - @example("greet") - dest: string; + Call a section, a URL, or an inline document as a subroutine. Execution continues in the + destination, then returns to the caller when it completes — either via `return` or by + reaching the end. `return_value` and the propagated `reply_*` and `request_*` variables + come back with it. - @doc("Parameters accessible as `params.*` in the called section. Replaces (does not merge with) any outer `params` from the caller.") - @example(#{ name: "%{message.from}" }) - params?: { - ...TypeSpec.Record; - }; - }; + A section destination runs against the caller's own variables, with `params` replacing any + inherited `params`. A URL or inline destination is a separate document: it sees only the + `message` variables and the `params` you pass, and it cannot reach the caller's sections. + + Accepts either an object, or a string used directly as `dest`. `%{...}` placeholders are + not expanded in the string form — use the object form for a computed destination. + """) + execute: string | ExecuteTarget; } diff --git a/specs/swml/messaging/Methods/transfer/main.tsp b/specs/swml/messaging/Methods/transfer/main.tsp index ad106901fe..7af129c261 100644 --- a/specs/swml/messaging/Methods/transfer/main.tsp +++ b/specs/swml/messaging/Methods/transfer/main.tsp @@ -1,36 +1,65 @@ namespace SWML.Messaging; + +@summary("TransferTarget object") +model TransferTarget { + @doc(""" + Where to transfer to. The value can be one of: + - A section name — a section defined in the current document. + - A URL (`http` or `https`) that returns a SWML document. SignalWire sends the + [inbound message webhook payload](/docs/apis/rest/swml-webhook/webhooks/inbound-message-webhook) + as the request body: `message` describes the original inbound message, `params` carries + the values supplied here, and `vars` carries the runtime variables (`request_result`, + `reply_result`, etc.) accumulated by the current document. Credentials embedded in the + URL as `username:password@url` are sent as HTTP basic authentication; this relies on how + the HTTP client handles credentials in a URL, so re-check it after a major platform + update if you depend on it. + - An inline SWML document, supplied as a JSON string. It may be a full document with + `sections`, or a bare `reply` or `receive` document. + + A value that is not an `http`/`https` URL and does not parse as a JSON object is treated + as a section name. A malformed inline document — a syntax error, a JSON array, or a value + written as a native object rather than a string — therefore fails with + `swml_section_not_found`, not a fetch or parse error. + """) + @example("https://example.com/handler") + dest: string; + + @doc(""" + HTTP method for the fetch request. Applies only when `dest` is a URL; it is ignored for + section and inline-document destinations. One of `GET`, `POST`, `PUT`, `PATCH`, or + `DELETE` — any other value causes the fetch to fail with `swml_transfer_fetch_failed`. + """) + @example("POST") + method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" = "POST"; + + @doc(""" + Parameters made available as `params.*` in the destination. Replaces (does not merge with) + the caller's `params`. For a URL destination they are also sent in the request body. + """) + @example(#{ reason: "escalation" }) + params?: { + ...TypeSpec.Record; + }; +} + @summary("transfer Method") model Transfer { @summary("transfer") @doc(""" - Fetch and execute a new SWML document from a URL. SignalWire POSTs the - [inbound message webhook payload](/docs/apis/rest/swml-webhook/webhooks/inbound-message-webhook) - to `dest`: `message` describes the original inbound message, `params` carries - the values supplied here, and `vars` carries the propagated runtime variables - (`request_result`, `reply_result`, etc.) accumulated by the current document. - - This is a tail call — it replaces the current document and does not return. - Steps after `transfer` are skipped, including steps in calling sections. In the - messaging context, `transfer.dest` must be a URL — section names and inline - documents are not accepted. + Hand execution to another SWML destination: a section in the current document, a URL that + returns a SWML document, or an inline SWML document. + + This is a tail call — it replaces the current document and does not return. Steps after + `transfer` are skipped, including steps in calling sections. A transfer that cannot be + resolved ends the run rather than falling through to the next step. + + A section destination keeps the runtime variables accumulated so far. A URL or inline + destination is a separate document: it sees only the `message` variables and the `params` + you pass. + + Accepts either an object, or a string used directly as `dest`. `%{...}` placeholders are + not expanded in the string form — use the object form for a computed destination. """) - transfer: { - @doc(""" - URL (`http` or `https`) to fetch the new SWML document from. Authentication can - be embedded in the URL as `username:password@url`. - """) - @example("https://example.com/handler") - dest: url; - - @doc("HTTP method for the fetch request.") - @example("POST") - method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" = "POST"; - - @doc("Parameters to include in the request body of the fetch. Available as `params.*` in the transferred document.") - @example(#{ reason: "escalation" }) - params?: { - ...TypeSpec.Record; - }; - }; + transfer: string | TransferTarget; } diff --git a/specs/swml/messaging/tsp-output/@typespec/json-schema/SWMLObject.json b/specs/swml/messaging/tsp-output/@typespec/json-schema/SWMLObject.json index 0b32eff984..eed74e04d0 100644 --- a/specs/swml/messaging/tsp-output/@typespec/json-schema/SWMLObject.json +++ b/specs/swml/messaging/tsp-output/@typespec/json-schema/SWMLObject.json @@ -242,34 +242,15 @@ "type": "object", "properties": { "execute": { - "type": "object", - "properties": { - "dest": { - "type": "string", - "examples": [ - "greet" - ], - "description": "Name of the section to execute. Must reference a section in the current document." + "anyOf": [ + { + "type": "string" }, - "params": { - "type": "object", - "properties": {}, - "unevaluatedProperties": {}, - "examples": [ - { - "name": "%{message.from}" - } - ], - "description": "Parameters accessible as `params.*` in the called section. Replaces (does not merge with) any outer `params` from the caller." + { + "$ref": "#/$defs/ExecuteTarget" } - }, - "required": [ - "dest" ], - "unevaluatedProperties": { - "not": {} - }, - "description": "Call a named section as a subroutine. Execution continues in the called section,\nthen returns to the caller when the section completes — either via `return` or by\nreaching the end of the section. The destination must be the name of a section\ndefined in the current document; URLs and inline documents are not accepted in the\nmessaging context.", + "description": "Call a section, a URL, or an inline document as a subroutine. Execution continues in the\ndestination, then returns to the caller when it completes — either via `return` or by\nreaching the end. `return_value` and the propagated `reply_*` and `request_*` variables\ncome back with it.\n\nA section destination runs against the caller's own variables, with `params` replacing any\ninherited `params`. A URL or inline destination is a separate document: it sees only the\n`message` variables and the `params` you pass, and it cannot reach the caller's sections.\n\nAccepts either an object, or a string used directly as `dest`. `%{...}` placeholders are\nnot expanded in the string form — use the object form for a computed destination.", "title": "execute" } }, @@ -285,64 +266,15 @@ "type": "object", "properties": { "transfer": { - "type": "object", - "properties": { - "dest": { - "type": "string", - "format": "uri", - "examples": [ - "https://example.com/handler" - ], - "description": "URL (`http` or `https`) to fetch the new SWML document from. Authentication can\nbe embedded in the URL as `username:password@url`." - }, - "method": { - "anyOf": [ - { - "type": "string", - "const": "GET" - }, - { - "type": "string", - "const": "POST" - }, - { - "type": "string", - "const": "PUT" - }, - { - "type": "string", - "const": "PATCH" - }, - { - "type": "string", - "const": "DELETE" - } - ], - "default": "POST", - "examples": [ - "POST" - ], - "description": "HTTP method for the fetch request." + "anyOf": [ + { + "type": "string" }, - "params": { - "type": "object", - "properties": {}, - "unevaluatedProperties": {}, - "examples": [ - { - "reason": "escalation" - } - ], - "description": "Parameters to include in the request body of the fetch. Available as `params.*` in the transferred document." + { + "$ref": "#/$defs/TransferTarget" } - }, - "required": [ - "dest" ], - "unevaluatedProperties": { - "not": {} - }, - "description": "Fetch and execute a new SWML document from a URL. SignalWire POSTs the\n[inbound message webhook payload](/docs/apis/rest/swml-webhook/webhooks/inbound-message-webhook)\nto `dest`: `message` describes the original inbound message, `params` carries\nthe values supplied here, and `vars` carries the propagated runtime variables\n(`request_result`, `reply_result`, etc.) accumulated by the current document.\n\nThis is a tail call — it replaces the current document and does not return.\nSteps after `transfer` are skipped, including steps in calling sections. In the\nmessaging context, `transfer.dest` must be a URL — section names and inline\ndocuments are not accepted.", + "description": "Hand execution to another SWML destination: a section in the current document, a URL that\nreturns a SWML document, or an inline SWML document.\n\nThis is a tail call — it replaces the current document and does not return. Steps after\n`transfer` are skipped, including steps in calling sections. A transfer that cannot be\nresolved ends the run rather than falling through to the next step.\n\nA section destination keeps the runtime variables accumulated so far. A URL or inline\ndestination is a separate document: it sees only the `message` variables and the `params`\nyou pass.\n\nAccepts either an object, or a string used directly as `dest`. `%{...}` placeholders are\nnot expanded in the string form — use the object form for a computed destination.", "title": "transfer" } }, @@ -673,6 +605,95 @@ "description": "Branch the reply on a variable value. The matched `case` (or `default`) supplies the\nreply contents in one of two forms:\n\n- **String** — sent back to the original sender as the SMS body. The simplest form,\n good for choosing between canned text responses.\n- **Reply object** — a full `{ body, media, to, from, status_url }` block, so the\n matched branch can attach media, override the destination, or set a per-branch\n status callback. Any `%{…}` placeholders inside the object are expanded before\n the reply is sent.\n\nWhen you use an inline `switch`, do not set `body`, `media`, `to`, `from`, or\n`status_url` directly on the same `reply` — put them inside each `case` value\n(or `default`) instead.", "title": "Reply with inline switch" }, + "ExecuteTarget": { + "type": "object", + "properties": { + "dest": { + "type": "string", + "examples": [ + "greet" + ], + "description": "What to execute. The value can be one of:\n- A section name — a section defined in the current document.\n- A URL (`http` or `https`) that returns a SWML document. SignalWire always sends an HTTP\n POST; there is no `method` parameter on `execute`.\n- An inline SWML document, supplied as a JSON string. It may be a full document with\n `sections`, or a bare `reply` or `receive` document.\n\nA value that is not an `http`/`https` URL and does not parse as a JSON object is treated\nas a section name. A malformed inline document — a syntax error, a JSON array, or a value\nwritten as a native object rather than a string — therefore fails with\n`swml_section_not_found`, not `swml_execute_document_invalid`." + }, + "params": { + "type": "object", + "properties": {}, + "unevaluatedProperties": {}, + "examples": [ + { + "name": "%{message.from}" + } + ], + "description": "Parameters accessible as `params.*` in the destination. Replaces (does not merge with) any outer `params` from the caller." + } + }, + "required": [ + "dest" + ], + "unevaluatedProperties": { + "not": {} + }, + "title": "ExecuteTarget object" + }, + "TransferTarget": { + "type": "object", + "properties": { + "dest": { + "type": "string", + "examples": [ + "https://example.com/handler" + ], + "description": "Where to transfer to. The value can be one of:\n- A section name — a section defined in the current document.\n- A URL (`http` or `https`) that returns a SWML document. SignalWire sends the\n [inbound message webhook payload](/docs/apis/rest/swml-webhook/webhooks/inbound-message-webhook)\n as the request body: `message` describes the original inbound message, `params` carries\n the values supplied here, and `vars` carries the runtime variables (`request_result`,\n `reply_result`, etc.) accumulated by the current document. Credentials embedded in the\n URL as `username:password@url` are sent as HTTP basic authentication; this relies on how\n the HTTP client handles credentials in a URL, so re-check it after a major platform\n update if you depend on it.\n- An inline SWML document, supplied as a JSON string. It may be a full document with\n `sections`, or a bare `reply` or `receive` document.\n\nA value that is not an `http`/`https` URL and does not parse as a JSON object is treated\nas a section name. A malformed inline document — a syntax error, a JSON array, or a value\nwritten as a native object rather than a string — therefore fails with\n`swml_section_not_found`, not a fetch or parse error." + }, + "method": { + "anyOf": [ + { + "type": "string", + "const": "GET" + }, + { + "type": "string", + "const": "POST" + }, + { + "type": "string", + "const": "PUT" + }, + { + "type": "string", + "const": "PATCH" + }, + { + "type": "string", + "const": "DELETE" + } + ], + "default": "POST", + "examples": [ + "POST" + ], + "description": "HTTP method for the fetch request. Applies only when `dest` is a URL; it is ignored for\nsection and inline-document destinations. One of `GET`, `POST`, `PUT`, `PATCH`, or\n`DELETE` — any other value causes the fetch to fail with `swml_transfer_fetch_failed`." + }, + "params": { + "type": "object", + "properties": {}, + "unevaluatedProperties": {}, + "examples": [ + { + "reason": "escalation" + } + ], + "description": "Parameters made available as `params.*` in the destination. Replaces (does not merge with)\nthe caller's `params`. For a URL destination they are also sent in the request body." + } + }, + "required": [ + "dest" + ], + "unevaluatedProperties": { + "not": {} + }, + "title": "TransferTarget object" + }, "SwitchTransform": { "type": "string", "enum": [ From d7d7367f490262000606111884bc0a1d410e44b4 Mon Sep 17 00:00:00 2001 From: Devon-White Date: Tue, 28 Jul 2026 13:52:30 -0400 Subject: [PATCH 2/3] Correct claims that adversarial review found wrong Four independent reviewers checked the previous commit against source. The riskiest edits held up: removing inline support from voice transfer, and removing execute_after_queue from three connect forms, were both confirmed. These are the claims that did not. Messaging destination context (the largest error, in five places). Both method pages, their TypeSpec, and the messaging overview claimed a URL or inline destination starts from a fresh context of message plus params, and that it does not inherit the caller's runtime variables. That asymmetry does not exist: in the ordinary case all three destination forms read the same variables. Where a real difference occurs it runs the other way, with a URL or inline destination seeing a subset. The per-form table is replaced with prose stating what a destination does read, keeping the one genuine exception: a step nested inside a destination called by execute or transfer does not see variables set inside that call. Voice execute inline forms. "A JSON or YAML string ... or just an array of instructions" implied four combinations, one of which does not exist. A bare instruction array is JSON only; a YAML string must be a full document. Error descriptions. swml_invalid_message_handler cited a LaML bin, which is the one case that provably cannot produce it; the real trigger is a Call Flow or AI Agent handler. url_failed_to_parse also fires on any non-2xx response, which is its most common cause. http_retrieval_error covers connection and timeout failures only. message_filtered does not mark the message failed and is not carrier filtering, and the section intro no longer over-claims for it. relay_script_element_invalid_value and relay_script_section_code_missing had stated triggers that did not match their only raise sites. Content types are not enforced on either flavor. Both determine the format from the response body, so the three types are now good practice rather than a requirement. The calling overview also listed join_conference.wait_url and enter_queue.wait_url as SWML document fetches; the first is a LaML playlist and the second an audio file, so both are removed from that list. Also: the vars webhook field carries return_value once an earlier execute sets one, and variables.mdx now states the scopes per flavor instead of merging two different sets into one list. Verified: yarn build:schema compiles both SWML projects; yarn fern-md-check reports all 2810 MDX files valid. --- .../products/swml/pages/get-started/index.mdx | 8 +++-- fern/products/swml/pages/reference/errors.mdx | 36 ++++++++++++------- .../reference/methods/calling/execute.mdx | 2 +- .../reference/methods/calling/overview.mdx | 14 ++++---- .../reference/methods/messaging/execute.mdx | 21 +++++------ .../reference/methods/messaging/overview.mdx | 10 +++--- .../reference/methods/messaging/transfer.mdx | 25 +++++++------ .../swml/pages/reference/variables.mdx | 21 +++++++---- specs/swml/calling/Methods/execute/main.tsp | 5 +-- .../@typespec/json-schema/SWMLObject.json | 2 +- specs/swml/messaging/Methods/execute/main.tsp | 9 +++-- .../swml/messaging/Methods/transfer/main.tsp | 10 ++++-- .../@typespec/json-schema/SWMLObject.json | 4 +-- 13 files changed, 97 insertions(+), 70 deletions(-) diff --git a/fern/products/swml/pages/get-started/index.mdx b/fern/products/swml/pages/get-started/index.mdx index b4af4fc750..fbb8ec6b38 100644 --- a/fern/products/swml/pages/get-started/index.mdx +++ b/fern/products/swml/pages/get-started/index.mdx @@ -153,9 +153,11 @@ shape depends on the document type: - **Calling** — see [Calling SWML → Webhook and variable payload](/docs/swml/reference/calling#webhook-payload). - **Messaging** — see [Messaging SWML → Webhook and variable payload](/docs/swml/reference/messaging#webhook-payload). -Your server must respond with a valid SWML document using one of these content types: -`application/json`, `application/yaml`, or `text/x-yaml`. For server setup instructions, see the -[Deployment guide](/docs/swml/guides/deployment). +Your server must respond with a valid SWML document as JSON or YAML. Both document types +determine the format from the response body rather than from the `Content-Type` header, so a +missing or mismatched content type does not on its own cause a failure. Sending an accurate one +(`application/json`, `application/yaml`, or `text/x-yaml`) is still good practice. For server +setup instructions, see the [Deployment guide](/docs/swml/guides/deployment). ## Next steps diff --git a/fern/products/swml/pages/reference/errors.mdx b/fern/products/swml/pages/reference/errors.mdx index f03bf498ac..bfc2172340 100644 --- a/fern/products/swml/pages/reference/errors.mdx +++ b/fern/products/swml/pages/reference/errors.mdx @@ -49,7 +49,8 @@ event. - A section is defined but contains no executable steps. + A section defined in object form has no `code` key. A section written directly as an array of + steps does not need one. @@ -66,8 +67,9 @@ event. - A document element has an invalid value (for example, an enum-valued field set to an unrecognized - string). + A step's `label` is present but its value is an empty string. A `label` that is not a string + reports `relay_script_element_wrong_type`, and an out-of-range value on a method parameter + reports `relay_script_method_parameter_invalid_value`. @@ -136,13 +138,16 @@ event. ## Messaging errors -Errors emitted when an inbound message's SWML document fails to fetch, parse, or execute. Each -inbound message is marked `failed` with the listed `error_code` and a corresponding -`error_message`, visible in the [Messaging logs](/docs/apis/rest/message-logs/list-message-logs). +Errors emitted when an inbound message's SWML document fails to fetch, parse, or execute. The +`error_code` and a corresponding `error_message` are recorded on the inbound message and are +visible in the [Messaging logs](/docs/apis/rest/message-logs/list-message-logs). Every code below +marks the message `failed`, with the exception of `message_filtered`, which flags the message and +lets processing continue. ### Document fetch / route -These codes cover accepting the inbound message and fetching the handler assigned to its number. +These codes cover accepting the inbound message, screening it, and fetching the handler assigned +to its number. The granular fetch codes below, along with `json_or_yaml_required`, are reachable only from that initial fetch. A document fetch that fails partway through a run, from a `transfer` or an `execute` step, always reports that method's own fetch code regardless of the underlying cause. @@ -157,20 +162,27 @@ step, always reports that method's own fetch code regardless of the underlying c - Message filtered (e.g. spam detection or carrier-level filtering). + The inbound message was flagged as spam. The message is not marked `failed` and your SWML + document still runs, but the message body is withheld from the message logs and from API + responses. - The handler assigned to the inbound number is not a SWML script — for example, a LaML bin - assigned to a SWML handler slot. + The handler assigned to the inbound number is a Call Flow or an AI Agent rather than a SWML + script. - The attempt to retrieve the SWML document timed out or failed. + The request for the SWML document never completed. The connection failed, or the request timed + out before a response arrived. An endpoint that answers with a non-2xx status reports + `url_failed_to_parse` instead. - The configured SWML URL could not be parsed. + The endpoint answered with a non-2xx status, the configured URL is not well-formed, or the + response was sent with a JSON content type but a body that is not valid JSON. A non-2xx status + is the most common cause in practice, so check what your endpoint returns before auditing the + URL itself. ### Document parse diff --git a/fern/products/swml/pages/reference/methods/calling/execute.mdx b/fern/products/swml/pages/reference/methods/calling/execute.mdx index c097ea2cb8..a1ef25ca02 100644 --- a/fern/products/swml/pages/reference/methods/calling/execute.mdx +++ b/fern/products/swml/pages/reference/methods/calling/execute.mdx @@ -51,7 +51,7 @@ The destination string can be one of: - `"section_name"` - section in the current document to execute. (For example: `main`) - A URL (http or https) - URL pointing to the document to execute. An HTTP POST request will be sent to the URL. The `params` object is passed, along with the variables and the [`call`](/docs/swml/reference/calling#webhook-payload) object. Authentication can also be set in the url in the format of `username:password@url`. -- An inline SWML document, as a JSON or YAML string. It may be a full document with `version` and `sections`, or just an array of instructions. +- An inline SWML document, as a JSON or YAML string. A JSON string may be either a full document with `version` and `sections` or a bare array of instructions; a YAML string must be a full document. diff --git a/fern/products/swml/pages/reference/methods/calling/overview.mdx b/fern/products/swml/pages/reference/methods/calling/overview.mdx index 53cc4d6879..7a1471ce97 100644 --- a/fern/products/swml/pages/reference/methods/calling/overview.mdx +++ b/fern/products/swml/pages/reference/methods/calling/overview.mdx @@ -53,14 +53,14 @@ sections: ## Webhook and variable payload [#webhook-payload] When SignalWire fetches a Calling SWML document from an external URL, it POSTs this payload to -your server — on the initial inbound fetch and on every fetch triggered by a method that hits -an external URL (`execute` with a remote URL, -[`transfer`](/docs/swml/reference/calling/transfer), -[`join_conference.wait_url`](/docs/swml/reference/calling/join-conference#properties), -[`enter_queue.wait_url`](/docs/swml/reference/calling/enter-queue#properties), and +your server — on the initial inbound fetch and on every method that fetches a further SWML +document from a URL (`execute` with a remote URL, +[`transfer`](/docs/swml/reference/calling/transfer), and [`connect.confirm`](/docs/swml/reference/calling/connect#properties)). Your server must respond -with a valid SWML document using one of these content types: `application/json`, -`application/yaml`, or `text/x-yaml`. +with a valid SWML document as JSON or YAML. Calling SWML determines the format from the response +body rather than from the `Content-Type` header, so a missing or mismatched content type does not +on its own cause a failure. Sending an accurate one (`application/json`, `application/yaml`, or +`text/x-yaml`) is still good practice. Inside the executing document, the `call`, `params`, and `envs` fields are available for variable expansion via `${...}` (JavaScript expressions) and `%{...}` (path substitution). As diff --git a/fern/products/swml/pages/reference/methods/messaging/execute.mdx b/fern/products/swml/pages/reference/methods/messaging/execute.mdx index 8f14737f80..8afb4e96c4 100644 --- a/fern/products/swml/pages/reference/methods/messaging/execute.mdx +++ b/fern/products/swml/pages/reference/methods/messaging/execute.mdx @@ -52,18 +52,15 @@ SignalWire infers the form from the value of `dest`; there is no separate type p beginning with `http://` or `https://` is a URL, a value that parses as a JSON object is an inline document, and anything else is a section name. -What the destination can read depends on which form it is: - -| `dest` form | What the destination can read | -|---|---| -| Section in the current document | Everything the caller can read, including the runtime variables set so far, with `params` replaced by this step's `params` | -| URL | `message` and this step's `params` | -| Inline document | `message` and this step's `params` | +Whichever form you use, the destination reads the inbound `message`, the runtime variables the +document has accumulated so far, and this step's `params`, which replace the caller's `params` +rather than merging with them. When the step itself runs inside a destination called by `execute` +or `transfer`, a URL or inline destination sees the variables accumulated before that call but not +the ones set inside it, so pass those in `params`. A URL or inline destination runs as a separate document, including the inline form where nothing is -fetched. It does not inherit the caller's runtime variables, so `params` is the only way to pass a -computed value into one. What comes back afterwards is the same for all three forms, and is covered -under [Variables](#variables). +fetched. What comes back afterwards is the same for all three forms, and is covered under +[Variables](#variables). When `dest` is a URL, SignalWire sends the [inbound message webhook payload](/docs/apis/rest/swml-webhook/webhooks/inbound-message-webhook) to @@ -176,8 +173,8 @@ sections: ### Execute a document fetched from your server The document your server returns runs as a subroutine, then `main` picks up at the next step. -Because the fetched document cannot read the caller's runtime variables, pass anything it needs in -`params`. +`params` is expanded before the request goes out, so it can carry a computed value into the fetched +document. diff --git a/fern/products/swml/pages/reference/methods/messaging/overview.mdx b/fern/products/swml/pages/reference/methods/messaging/overview.mdx index c2a7b9c00d..9ac8813b91 100644 --- a/fern/products/swml/pages/reference/methods/messaging/overview.mdx +++ b/fern/products/swml/pages/reference/methods/messaging/overview.mdx @@ -139,10 +139,12 @@ Methods write these to the top level of the document's variable context as the s later step reads them directly: `%{reply_result}`, not `%{vars.reply_result}`. Methods may also write additional keys; see each method's reference page for what it sets. -A destination that is a section of the current document keeps whatever the caller has accumulated. -A URL or inline-document destination does not: it starts with a fresh context holding `message` and -the `params` the step passed, so these values reach it only through the `vars` payload field or -through `params`. +A destination reached through [`execute`](/docs/swml/reference/messaging/execute) or +[`transfer`](/docs/swml/reference/messaging/transfer) reads the variables the document has +accumulated so far, whether that destination is a section, a URL, or an inline document. When the +step itself runs inside a destination called by `execute` or `transfer`, a URL or inline-document +destination sees the variables accumulated before that call but not the ones set inside it, so pass +those in `params`. Result of the most recent [`reply`](/docs/swml/reference/messaging/reply) step. Set after diff --git a/fern/products/swml/pages/reference/methods/messaging/transfer.mdx b/fern/products/swml/pages/reference/methods/messaging/transfer.mdx index 0b4cb9f44b..83288e0f6e 100644 --- a/fern/products/swml/pages/reference/methods/messaging/transfer.mdx +++ b/fern/products/swml/pages/reference/methods/messaging/transfer.mdx @@ -66,18 +66,16 @@ SignalWire infers the form from the value of `dest`; there is no separate type p beginning with `http://` or `https://` is a URL, a value that parses as a JSON object is an inline document, and anything else is a section name. -What the destination can read depends on which form it is: - -| `dest` form | What the destination can read | -|---|---| -| Section in the current document | Everything the caller can read, including the runtime variables set so far, with `params` replaced by this step's `params` | -| URL | `message` and this step's `params` | -| Inline document | `message` and this step's `params` | +Whichever form you use, the destination reads the inbound `message`, the runtime variables the +document has accumulated so far, and this step's `params`, which replace the caller's `params` +rather than merging with them. When the step itself runs inside a destination called by `execute` +or `transfer`, a URL or inline destination sees the variables accumulated before that call but not +the ones set inside it, so pass those in `params`. A URL or inline destination runs as a separate document, including the inline form where nothing is -fetched. It does not inherit the caller's runtime variables, so `params` is the only way to pass a -computed value into one. For a URL destination those variables are still sent outward in the -request body as `vars`, which your server can read and fold into the document it returns. +fetched, and a document that defines its own `sections` uses those from then on. For a URL +destination the accumulated variables are also sent outward in the request body as `vars`, which +your server can read and fold into the document it returns. Nothing comes back to the caller from any form of `transfer`. Use `execute` when you need a value returned. @@ -104,9 +102,10 @@ to it using `method`: - `message` — the original inbound message that triggered this SWML document. - `params` — the values supplied to this `transfer` step. -- `vars` — the runtime variables the current document has accumulated (`request_result`, - `reply_result`, `request_response`, `request_response_code`, `request_response_body`, - `reply_message_id`). Present on transfer-driven fetches; absent on the initial inbound fetch. +- `vars` — the runtime variables the current document has accumulated: `request_result`, + `request_response`, `request_response_code`, `request_response_body`, `reply_result`, + `reply_message_id`, and `return_value` once an earlier `execute` has set one. Present on + transfer-driven fetches; absent on the initial inbound fetch. SignalWire waits up to 5 seconds for the response and does not retry. diff --git a/fern/products/swml/pages/reference/variables.mdx b/fern/products/swml/pages/reference/variables.mdx index 9adf30349d..8bbb7168a7 100644 --- a/fern/products/swml/pages/reference/variables.mdx +++ b/fern/products/swml/pages/reference/variables.mdx @@ -102,13 +102,20 @@ sections: ## Variable scopes -The variables available at runtime are the same fields delivered on the inbound webhook payload -for each SWML flavor. See the -[Calling webhook and variable payload](/docs/swml/reference/calling#webhook-payload) and the -[Messaging webhook and variable payload](/docs/swml/reference/messaging#webhook-payload) for the -authoritative list of scopes (`call` / `message`, `params`, `vars`, `envs`) and every field -inside them. In a Messaging document, `vars` is a field of the webhook payload rather than an -interpolation scope, so its values are read without the `vars.` prefix. +The variables available at runtime are the fields delivered on the inbound webhook payload for +each SWML flavor, plus whatever the document sets as it runs. The two flavors differ in how you +address them. + +A Calling document has four scopes — `call`, `params`, `envs`, and `vars` — each addressed +through its own prefix: `${call.from}`, `%{params.department}`, `${envs.api_host}`, +`%{vars.user_choice}`. + +A Messaging document has two — `message` and `params`: `%{message.from}`, `%{params.campaign}`. +Variables that a Messaging document sets while it runs sit outside both scopes and are +referenced by name with no prefix (`%{reply_result}`); they reach your server as a top-level +`vars` object on document fetches made once the script is already running. + +The per-flavor overviews linked above list every field inside each scope. ## Accessing nested data diff --git a/specs/swml/calling/Methods/execute/main.tsp b/specs/swml/calling/Methods/execute/main.tsp index f1cca3896c..beaa96cc05 100644 --- a/specs/swml/calling/Methods/execute/main.tsp +++ b/specs/swml/calling/Methods/execute/main.tsp @@ -26,8 +26,9 @@ model ExecuteTarget { Specifies what to execute. The value can be one of: - `` - section in the current document to execute - A URL (http or https) that returns a SWML document - Sends HTTP POST - - An inline SWML document, as a JSON or YAML string. It may be a full document with - `version` and `sections`, or just an array of instructions. + - An inline SWML document, as a JSON or YAML string. A JSON string may be either a full + document with `version` and `sections` or a bare array of instructions; a YAML string + must be a full document. """) @example("https://example.com/swml-handler") dest: string; diff --git a/specs/swml/calling/tsp-output/@typespec/json-schema/SWMLObject.json b/specs/swml/calling/tsp-output/@typespec/json-schema/SWMLObject.json index 0454ca135a..c3afdeed50 100644 --- a/specs/swml/calling/tsp-output/@typespec/json-schema/SWMLObject.json +++ b/specs/swml/calling/tsp-output/@typespec/json-schema/SWMLObject.json @@ -3895,7 +3895,7 @@ "examples": [ "https://example.com/swml-handler" ], - "description": "Specifies what to execute. The value can be one of:\n- `` - section in the current document to execute\n- A URL (http or https) that returns a SWML document - Sends HTTP POST\n- An inline SWML document, as a JSON or YAML string. It may be a full document with\n `version` and `sections`, or just an array of instructions." + "description": "Specifies what to execute. The value can be one of:\n- `` - section in the current document to execute\n- A URL (http or https) that returns a SWML document - Sends HTTP POST\n- An inline SWML document, as a JSON or YAML string. A JSON string may be either a full\n document with `version` and `sections` or a bare array of instructions; a YAML string\n must be a full document." }, "params": { "type": "object", diff --git a/specs/swml/messaging/Methods/execute/main.tsp b/specs/swml/messaging/Methods/execute/main.tsp index 9661bd0c21..7f313492b1 100644 --- a/specs/swml/messaging/Methods/execute/main.tsp +++ b/specs/swml/messaging/Methods/execute/main.tsp @@ -35,9 +35,12 @@ model Execute { reaching the end. `return_value` and the propagated `reply_*` and `request_*` variables come back with it. - A section destination runs against the caller's own variables, with `params` replacing any - inherited `params`. A URL or inline destination is a separate document: it sees only the - `message` variables and the `params` you pass, and it cannot reach the caller's sections. + Whichever form you use, the destination reads the inbound `message`, the runtime variables + the document has accumulated so far, and this step's `params`, which replace the caller's + `params` rather than merging with them. When the step itself runs inside a destination called + by `execute` or `transfer`, a URL or inline destination sees the variables accumulated before + that call but not the ones set inside it, so pass those in `params`. A URL or inline + destination is a separate document and cannot reach the caller's sections. Accepts either an object, or a string used directly as `dest`. `%{...}` placeholders are not expanded in the string form — use the object form for a computed destination. diff --git a/specs/swml/messaging/Methods/transfer/main.tsp b/specs/swml/messaging/Methods/transfer/main.tsp index 7af129c261..192c893ce7 100644 --- a/specs/swml/messaging/Methods/transfer/main.tsp +++ b/specs/swml/messaging/Methods/transfer/main.tsp @@ -54,9 +54,13 @@ model Transfer { `transfer` are skipped, including steps in calling sections. A transfer that cannot be resolved ends the run rather than falling through to the next step. - A section destination keeps the runtime variables accumulated so far. A URL or inline - destination is a separate document: it sees only the `message` variables and the `params` - you pass. + Whichever form you use, the destination reads the inbound `message`, the runtime variables + the document has accumulated so far, and this step's `params`, which replace the caller's + `params` rather than merging with them. When the step itself runs inside a destination called + by `execute` or `transfer`, a URL or inline destination sees the variables accumulated before + that call but not the ones set inside it, so pass those in `params`. A URL or inline + destination is a separate document, and one that defines its own `sections` uses those from + then on. Accepts either an object, or a string used directly as `dest`. `%{...}` placeholders are not expanded in the string form — use the object form for a computed destination. diff --git a/specs/swml/messaging/tsp-output/@typespec/json-schema/SWMLObject.json b/specs/swml/messaging/tsp-output/@typespec/json-schema/SWMLObject.json index eed74e04d0..2ff4a7c39d 100644 --- a/specs/swml/messaging/tsp-output/@typespec/json-schema/SWMLObject.json +++ b/specs/swml/messaging/tsp-output/@typespec/json-schema/SWMLObject.json @@ -250,7 +250,7 @@ "$ref": "#/$defs/ExecuteTarget" } ], - "description": "Call a section, a URL, or an inline document as a subroutine. Execution continues in the\ndestination, then returns to the caller when it completes — either via `return` or by\nreaching the end. `return_value` and the propagated `reply_*` and `request_*` variables\ncome back with it.\n\nA section destination runs against the caller's own variables, with `params` replacing any\ninherited `params`. A URL or inline destination is a separate document: it sees only the\n`message` variables and the `params` you pass, and it cannot reach the caller's sections.\n\nAccepts either an object, or a string used directly as `dest`. `%{...}` placeholders are\nnot expanded in the string form — use the object form for a computed destination.", + "description": "Call a section, a URL, or an inline document as a subroutine. Execution continues in the\ndestination, then returns to the caller when it completes — either via `return` or by\nreaching the end. `return_value` and the propagated `reply_*` and `request_*` variables\ncome back with it.\n\nWhichever form you use, the destination reads the inbound `message`, the runtime variables\nthe document has accumulated so far, and this step's `params`, which replace the caller's\n`params` rather than merging with them. When the step itself runs inside a destination called\nby `execute` or `transfer`, a URL or inline destination sees the variables accumulated before\nthat call but not the ones set inside it, so pass those in `params`. A URL or inline\ndestination is a separate document and cannot reach the caller's sections.\n\nAccepts either an object, or a string used directly as `dest`. `%{...}` placeholders are\nnot expanded in the string form — use the object form for a computed destination.", "title": "execute" } }, @@ -274,7 +274,7 @@ "$ref": "#/$defs/TransferTarget" } ], - "description": "Hand execution to another SWML destination: a section in the current document, a URL that\nreturns a SWML document, or an inline SWML document.\n\nThis is a tail call — it replaces the current document and does not return. Steps after\n`transfer` are skipped, including steps in calling sections. A transfer that cannot be\nresolved ends the run rather than falling through to the next step.\n\nA section destination keeps the runtime variables accumulated so far. A URL or inline\ndestination is a separate document: it sees only the `message` variables and the `params`\nyou pass.\n\nAccepts either an object, or a string used directly as `dest`. `%{...}` placeholders are\nnot expanded in the string form — use the object form for a computed destination.", + "description": "Hand execution to another SWML destination: a section in the current document, a URL that\nreturns a SWML document, or an inline SWML document.\n\nThis is a tail call — it replaces the current document and does not return. Steps after\n`transfer` are skipped, including steps in calling sections. A transfer that cannot be\nresolved ends the run rather than falling through to the next step.\n\nWhichever form you use, the destination reads the inbound `message`, the runtime variables\nthe document has accumulated so far, and this step's `params`, which replace the caller's\n`params` rather than merging with them. When the step itself runs inside a destination called\nby `execute` or `transfer`, a URL or inline destination sees the variables accumulated before\nthat call but not the ones set inside it, so pass those in `params`. A URL or inline\ndestination is a separate document, and one that defines its own `sections` uses those from\nthen on.\n\nAccepts either an object, or a string used directly as `dest`. `%{...}` placeholders are\nnot expanded in the string form — use the object form for a computed destination.", "title": "transfer" } }, From 391bcb2d47fefd3f003213dec6a4b995fc441ea6 Mon Sep 17 00:00:00 2001 From: Devon-White Date: Tue, 28 Jul 2026 14:05:48 -0400 Subject: [PATCH 3/3] Make the nested-destination rule exact at any depth Re-verification of the previous commit found the corrected sentence was itself an approximation. It said a nested step's URL or inline destination sees the variables accumulated before the enclosing call. That is right only one level deep. A URL or inline destination reads the runtime variables of the top-level flow at any depth, because variables set inside a destination that execute or transfer entered are not visible until control returns to the top level. Nesting two levels deep is reachable in ordinary documents, so the sentence over-credited the destination with variables it cannot read. The wording now separates the two cases rather than stating one rule with an exception: a section destination reads everything the caller can, while a URL or inline destination reads the top-level flow's variables, which are the same thing whenever the step sits in main. Applied to both method pages, the messaging overview, and both TypeSpec doc strings, with the emitted schema regenerated. Also on the messaging overview: add return_value to the runtime variables, since it is readable as %{return_value} and is named in the transfer webhook payload list, and drop the note saying methods may write additional keys. The variable set is closed at seven names, verified across every write site, so the note contradicted the enumeration. The error-reference corrections from the previous commit were re-verified independently against their raise sites and need no further change. Verified: yarn build:schema compiles both SWML projects; yarn fern-md-check reports all 2810 MDX files valid. --- .../reference/methods/messaging/execute.mdx | 19 +++++++----- .../reference/methods/messaging/overview.mdx | 31 ++++++++++++------- .../reference/methods/messaging/transfer.mdx | 17 +++++----- specs/swml/messaging/Methods/execute/main.tsp | 16 ++++++---- .../swml/messaging/Methods/transfer/main.tsp | 18 ++++++----- .../@typespec/json-schema/SWMLObject.json | 4 +-- 6 files changed, 64 insertions(+), 41 deletions(-) diff --git a/fern/products/swml/pages/reference/methods/messaging/execute.mdx b/fern/products/swml/pages/reference/methods/messaging/execute.mdx index 8afb4e96c4..316b281e22 100644 --- a/fern/products/swml/pages/reference/methods/messaging/execute.mdx +++ b/fern/products/swml/pages/reference/methods/messaging/execute.mdx @@ -52,11 +52,13 @@ SignalWire infers the form from the value of `dest`; there is no separate type p beginning with `http://` or `https://` is a URL, a value that parses as a JSON object is an inline document, and anything else is a section name. -Whichever form you use, the destination reads the inbound `message`, the runtime variables the -document has accumulated so far, and this step's `params`, which replace the caller's `params` -rather than merging with them. When the step itself runs inside a destination called by `execute` -or `transfer`, a URL or inline destination sees the variables accumulated before that call but not -the ones set inside it, so pass those in `params`. +Every destination reads the inbound `message` and this step's `params`, which replace the caller's +`params` rather than merging with them. A section destination also reads everything the caller can, +including every runtime variable set so far. A URL or inline destination reads the runtime +variables of the top-level flow, the ones your `main` steps set, which is the same thing whenever +the step itself sits in `main`. Once the step is nested the two differ: variables set inside a +section or document that `execute` or `transfer` entered do not reach a URL or inline destination +until control returns to the top level, so pass those in `params`. A URL or inline destination runs as a separate document, including the inline form where nothing is fetched. What comes back afterwards is the same for all three forms, and is covered under @@ -64,9 +66,10 @@ fetched. What comes back afterwards is the same for all three forms, and is cove When `dest` is a URL, SignalWire sends the [inbound message webhook payload](/docs/apis/rest/swml-webhook/webhooks/inbound-message-webhook) to -it with a `POST`: the inbound `message`, this step's `params`, and the runtime variables the -document has accumulated so far as `vars`. Your server can read `vars` and fold those values into -the document it returns. SignalWire waits up to 5 seconds for the response and does not retry. See +it with a `POST`: the inbound `message`, this step's `params`, and the caller's runtime variables as +`vars`, including any the fetched document cannot read itself. Your server can read `vars` and fold +those values into the document it returns. SignalWire waits up to 5 seconds for the response and +does not retry. See the [messaging webhook payload](/docs/swml/reference/messaging#webhook-payload) for the full field reference. diff --git a/fern/products/swml/pages/reference/methods/messaging/overview.mdx b/fern/products/swml/pages/reference/methods/messaging/overview.mdx index 9ac8813b91..6903d3e9ef 100644 --- a/fern/products/swml/pages/reference/methods/messaging/overview.mdx +++ b/fern/products/swml/pages/reference/methods/messaging/overview.mdx @@ -125,9 +125,10 @@ document fetches made once the script is already running. - The runtime variables the document has accumulated so far, sent as a top-level payload field on - document fetches made once the script is already running. Absent on the initial inbound fetch, - because no method has run yet. + The caller's runtime variables, sent as a top-level payload field on document fetches made once + the script is already running. Absent on the initial inbound fetch, because no method has run + yet. This carries the variables of the step that made the fetch, so it can include values the + fetched document cannot read itself. This is a payload field, not a variable scope. Inside a document the same values are read without a `vars.` prefix, as [Runtime variables](#runtime-variables) below describes. @@ -136,15 +137,16 @@ document fetches made once the script is already running. ### Runtime variables [#runtime-variables] Methods write these to the top level of the document's variable context as the script runs, so a -later step reads them directly: `%{reply_result}`, not `%{vars.reply_result}`. Methods may also -write additional keys; see each method's reference page for what it sets. +later step reads them directly: `%{reply_result}`, not `%{vars.reply_result}`. These are the only +runtime variables a document can set; each method's reference page says which of them it writes. -A destination reached through [`execute`](/docs/swml/reference/messaging/execute) or -[`transfer`](/docs/swml/reference/messaging/transfer) reads the variables the document has -accumulated so far, whether that destination is a section, a URL, or an inline document. When the -step itself runs inside a destination called by `execute` or `transfer`, a URL or inline-document -destination sees the variables accumulated before that call but not the ones set inside it, so pass -those in `params`. +A section destination reads everything the calling section can, including every runtime variable +set so far. A URL or inline-document destination reads the runtime variables of the top-level flow, +the ones your `main` steps set, which is the same thing whenever the step itself sits in `main`. +Once the step is nested the two differ: variables set inside a section or document that +[`execute`](/docs/swml/reference/messaging/execute) or +[`transfer`](/docs/swml/reference/messaging/transfer) entered do not reach a URL or inline-document +destination until control returns to the top level, so pass those in `params`. Result of the most recent [`reply`](/docs/swml/reference/messaging/reply) step. Set after @@ -177,6 +179,13 @@ those in `params`. to 64 KB. + + The value passed to [`return`](/docs/swml/reference/messaging/return) by the most recent + [`execute`](/docs/swml/reference/messaging/execute) destination. A bare `return`, or + `return: null`, still sets it, to an empty value. Left untouched when the destination ran to + completion without any `return`. + + See [Variables](/docs/swml/reference/variables) for variable-expansion syntax, deployment-mode differences, and tips on accessing nested fields and array elements. diff --git a/fern/products/swml/pages/reference/methods/messaging/transfer.mdx b/fern/products/swml/pages/reference/methods/messaging/transfer.mdx index 83288e0f6e..7f881dad08 100644 --- a/fern/products/swml/pages/reference/methods/messaging/transfer.mdx +++ b/fern/products/swml/pages/reference/methods/messaging/transfer.mdx @@ -66,16 +66,19 @@ SignalWire infers the form from the value of `dest`; there is no separate type p beginning with `http://` or `https://` is a URL, a value that parses as a JSON object is an inline document, and anything else is a section name. -Whichever form you use, the destination reads the inbound `message`, the runtime variables the -document has accumulated so far, and this step's `params`, which replace the caller's `params` -rather than merging with them. When the step itself runs inside a destination called by `execute` -or `transfer`, a URL or inline destination sees the variables accumulated before that call but not -the ones set inside it, so pass those in `params`. +Every destination reads the inbound `message` and this step's `params`, which replace the caller's +`params` rather than merging with them. A section destination also reads everything the caller can, +including every runtime variable set so far. A URL or inline destination reads the runtime +variables of the top-level flow, the ones your `main` steps set, which is the same thing whenever +the step itself sits in `main`. Once the step is nested the two differ: variables set inside a +section or document that `execute` or `transfer` entered do not reach a URL or inline destination +until control returns to the top level, so pass those in `params`. A URL or inline destination runs as a separate document, including the inline form where nothing is fetched, and a document that defines its own `sections` uses those from then on. For a URL -destination the accumulated variables are also sent outward in the request body as `vars`, which -your server can read and fold into the document it returns. +destination the caller's variables are also sent outward in the request body as `vars`, including +any the fetched document cannot read itself, so your server can fold them into the document it +returns. Nothing comes back to the caller from any form of `transfer`. Use `execute` when you need a value returned. diff --git a/specs/swml/messaging/Methods/execute/main.tsp b/specs/swml/messaging/Methods/execute/main.tsp index 7f313492b1..3dc1b5905f 100644 --- a/specs/swml/messaging/Methods/execute/main.tsp +++ b/specs/swml/messaging/Methods/execute/main.tsp @@ -35,12 +35,16 @@ model Execute { reaching the end. `return_value` and the propagated `reply_*` and `request_*` variables come back with it. - Whichever form you use, the destination reads the inbound `message`, the runtime variables - the document has accumulated so far, and this step's `params`, which replace the caller's - `params` rather than merging with them. When the step itself runs inside a destination called - by `execute` or `transfer`, a URL or inline destination sees the variables accumulated before - that call but not the ones set inside it, so pass those in `params`. A URL or inline - destination is a separate document and cannot reach the caller's sections. + Every destination reads the inbound `message` and this step's `params`, which replace the + caller's `params` rather than merging with them. A section destination also reads everything + the caller can, including every runtime variable set so far. A URL or inline destination + reads the runtime variables of the top-level flow, the ones your `main` steps set, which is + the same thing whenever the step itself sits in `main`. Once the step is nested the two + differ: variables set inside a section or document that `execute` or `transfer` entered do + not reach a URL or inline destination until control returns to the top level, so pass those + in `params`. + + A URL or inline destination is a separate document and cannot reach the caller's sections. Accepts either an object, or a string used directly as `dest`. `%{...}` placeholders are not expanded in the string form — use the object form for a computed destination. diff --git a/specs/swml/messaging/Methods/transfer/main.tsp b/specs/swml/messaging/Methods/transfer/main.tsp index 192c893ce7..fd41b4aa97 100644 --- a/specs/swml/messaging/Methods/transfer/main.tsp +++ b/specs/swml/messaging/Methods/transfer/main.tsp @@ -54,13 +54,17 @@ model Transfer { `transfer` are skipped, including steps in calling sections. A transfer that cannot be resolved ends the run rather than falling through to the next step. - Whichever form you use, the destination reads the inbound `message`, the runtime variables - the document has accumulated so far, and this step's `params`, which replace the caller's - `params` rather than merging with them. When the step itself runs inside a destination called - by `execute` or `transfer`, a URL or inline destination sees the variables accumulated before - that call but not the ones set inside it, so pass those in `params`. A URL or inline - destination is a separate document, and one that defines its own `sections` uses those from - then on. + Every destination reads the inbound `message` and this step's `params`, which replace the + caller's `params` rather than merging with them. A section destination also reads everything + the caller can, including every runtime variable set so far. A URL or inline destination + reads the runtime variables of the top-level flow, the ones your `main` steps set, which is + the same thing whenever the step itself sits in `main`. Once the step is nested the two + differ: variables set inside a section or document that `execute` or `transfer` entered do + not reach a URL or inline destination until control returns to the top level, so pass those + in `params`. + + A URL or inline destination is a separate document, and one that defines its own `sections` + uses those from then on. Accepts either an object, or a string used directly as `dest`. `%{...}` placeholders are not expanded in the string form — use the object form for a computed destination. diff --git a/specs/swml/messaging/tsp-output/@typespec/json-schema/SWMLObject.json b/specs/swml/messaging/tsp-output/@typespec/json-schema/SWMLObject.json index 2ff4a7c39d..745254e417 100644 --- a/specs/swml/messaging/tsp-output/@typespec/json-schema/SWMLObject.json +++ b/specs/swml/messaging/tsp-output/@typespec/json-schema/SWMLObject.json @@ -250,7 +250,7 @@ "$ref": "#/$defs/ExecuteTarget" } ], - "description": "Call a section, a URL, or an inline document as a subroutine. Execution continues in the\ndestination, then returns to the caller when it completes — either via `return` or by\nreaching the end. `return_value` and the propagated `reply_*` and `request_*` variables\ncome back with it.\n\nWhichever form you use, the destination reads the inbound `message`, the runtime variables\nthe document has accumulated so far, and this step's `params`, which replace the caller's\n`params` rather than merging with them. When the step itself runs inside a destination called\nby `execute` or `transfer`, a URL or inline destination sees the variables accumulated before\nthat call but not the ones set inside it, so pass those in `params`. A URL or inline\ndestination is a separate document and cannot reach the caller's sections.\n\nAccepts either an object, or a string used directly as `dest`. `%{...}` placeholders are\nnot expanded in the string form — use the object form for a computed destination.", + "description": "Call a section, a URL, or an inline document as a subroutine. Execution continues in the\ndestination, then returns to the caller when it completes — either via `return` or by\nreaching the end. `return_value` and the propagated `reply_*` and `request_*` variables\ncome back with it.\n\nEvery destination reads the inbound `message` and this step's `params`, which replace the\ncaller's `params` rather than merging with them. A section destination also reads everything\nthe caller can, including every runtime variable set so far. A URL or inline destination\nreads the runtime variables of the top-level flow, the ones your `main` steps set, which is\nthe same thing whenever the step itself sits in `main`. Once the step is nested the two\ndiffer: variables set inside a section or document that `execute` or `transfer` entered do\nnot reach a URL or inline destination until control returns to the top level, so pass those\nin `params`.\n\nA URL or inline destination is a separate document and cannot reach the caller's sections.\n\nAccepts either an object, or a string used directly as `dest`. `%{...}` placeholders are\nnot expanded in the string form — use the object form for a computed destination.", "title": "execute" } }, @@ -274,7 +274,7 @@ "$ref": "#/$defs/TransferTarget" } ], - "description": "Hand execution to another SWML destination: a section in the current document, a URL that\nreturns a SWML document, or an inline SWML document.\n\nThis is a tail call — it replaces the current document and does not return. Steps after\n`transfer` are skipped, including steps in calling sections. A transfer that cannot be\nresolved ends the run rather than falling through to the next step.\n\nWhichever form you use, the destination reads the inbound `message`, the runtime variables\nthe document has accumulated so far, and this step's `params`, which replace the caller's\n`params` rather than merging with them. When the step itself runs inside a destination called\nby `execute` or `transfer`, a URL or inline destination sees the variables accumulated before\nthat call but not the ones set inside it, so pass those in `params`. A URL or inline\ndestination is a separate document, and one that defines its own `sections` uses those from\nthen on.\n\nAccepts either an object, or a string used directly as `dest`. `%{...}` placeholders are\nnot expanded in the string form — use the object form for a computed destination.", + "description": "Hand execution to another SWML destination: a section in the current document, a URL that\nreturns a SWML document, or an inline SWML document.\n\nThis is a tail call — it replaces the current document and does not return. Steps after\n`transfer` are skipped, including steps in calling sections. A transfer that cannot be\nresolved ends the run rather than falling through to the next step.\n\nEvery destination reads the inbound `message` and this step's `params`, which replace the\ncaller's `params` rather than merging with them. A section destination also reads everything\nthe caller can, including every runtime variable set so far. A URL or inline destination\nreads the runtime variables of the top-level flow, the ones your `main` steps set, which is\nthe same thing whenever the step itself sits in `main`. Once the step is nested the two\ndiffer: variables set inside a section or document that `execute` or `transfer` entered do\nnot reach a URL or inline destination until control returns to the top level, so pass those\nin `params`.\n\nA URL or inline destination is a separate document, and one that defines its own `sections`\nuses those from then on.\n\nAccepts either an object, or a string used directly as `dest`. `%{...}` placeholders are\nnot expanded in the string form — use the object form for a computed destination.", "title": "transfer" } },