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..fbb8ec6b38 100644 --- a/fern/products/swml/pages/get-started/index.mdx +++ b/fern/products/swml/pages/get-started/index.mdx @@ -150,12 +150,14 @@ 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). - -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). +- **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 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 3feb2278e5..bfc2172340 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. @@ -47,11 +49,12 @@ 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. - 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`. @@ -64,14 +67,17 @@ 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`. 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,48 +114,75 @@ 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 -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 - - Unrouteable message received — no SWML handler is assigned to the inbound 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. + + + 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 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 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 @@ -189,7 +222,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 +255,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 +276,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 +299,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..a1ef25ca02 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. 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/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/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/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..316b281e22 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. + +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 +[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 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. + +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. +`params` is expanded before the request goes out, so it can carry a computed value into the fetched +document. + + + +```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..6903d3e9ef 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,66 @@ 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 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. - 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}`. These are the only +runtime variables a document can set; each method's reference page says which of them it writes. - - Message segment ID of the most recent successful `reply`. Set after the step completes. - +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 [`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. + + + + 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. @@ -184,13 +215,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..7f881dad08 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,104 @@ 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. + +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 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. + +### 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`. -When `transfer` fetches an external document, SignalWire `POST`s the +## **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`, - `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. +## **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 +164,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 +336,3 @@ sections: ``` - - diff --git a/fern/products/swml/pages/reference/variables.mdx b/fern/products/swml/pages/reference/variables.mdx index bc5eea8771..8bbb7168a7 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. @@ -100,12 +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. +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/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..beaa96cc05 100644 --- a/specs/swml/calling/Methods/execute/main.tsp +++ b/specs/swml/calling/Methods/execute/main.tsp @@ -20,41 +20,49 @@ 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. 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; + + @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..c3afdeed50 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. 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." }, - "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..3dc1b5905f 100644 --- a/specs/swml/messaging/Methods/execute/main.tsp +++ b/specs/swml/messaging/Methods/execute/main.tsp @@ -1,24 +1,53 @@ 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. + 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. + + 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. """) - execute: { - @doc("Name of the section to execute. Must reference a section in the current document.") - @example("greet") - dest: string; - - @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; - }; - }; + execute: string | ExecuteTarget; } diff --git a/specs/swml/messaging/Methods/transfer/main.tsp b/specs/swml/messaging/Methods/transfer/main.tsp index ad106901fe..fd41b4aa97 100644 --- a/specs/swml/messaging/Methods/transfer/main.tsp +++ b/specs/swml/messaging/Methods/transfer/main.tsp @@ -1,36 +1,73 @@ 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. + + 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. """) - 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..745254e417 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\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" } }, @@ -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\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" } }, @@ -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": [