--- title: Virtual Agent action scripts description: You need to write a set of mandatory Workflow Studio action and subflow scripts to pass the user data to VACS, apply transformations and send the response back to your chat interface endpoint. locale: en-US canonical_url: https://www.servicenow.com/docs/r/australia/conversational-interfaces/virtual-agent/va-action-scripts.html release: australia product: Virtual Agent classification: virtual-agent topic_type: reference last_updated: "2026-03-12" reading_time_minutes: 10 breadcrumb: [Supported features for custom chat integrations, Conversational custom chat integrations, Integrate VA with other channels, Virtual Agent, Conversational Interfaces] --- # Virtual Agent action scripts You need to write a set of mandatory Workflow Studio action and subflow scripts to pass the user data to VACS, apply transformations and send the response back to your chat interface endpoint. ## Provider and inbound/outbound scripts A provider, such as Twilio, provides the chat capability. User action scripts perform provider actions, as well as conversational custom chat integration configuration for inbound and outbound transformer actions for your chatbot or custom conversations. Examples of each script are provided. They include a description of what each script does, as well as the action input and action output. You also see examples of the script JSON. You use Workflow Studio to build these scripts to transform server and client messages. The provider, sender, and inbound/outbound transformer scripts are required. You can use the additional scripts to add more functionality to your chat.
| Script name | Purpose | Required \(Y/N\) |
|---|---|---|
| Provider attribute | Contains a Provider Auth token, a user identifier, user input, and context variables. | Y |
| Sender action | Bundles the request, and sends a response asynchronously via Workflow Studio or Integration Hub. | Y |
| Response processor | Performs platform actions, such as an update message status, that's based on a response received for an outbound message. | N |
| Contextual action | Supports contextual actions. | N |
| Link account action | Provides a callback about whether account linking was successful or not. | N |
| Outbound transformer | Transforms a chat server's supported control to chat interface-specific rendering. | Y |
| Inbound transformer | Optional. You can use the default rich controls if they apply to your chat provider. You do not need to provide a separate inbound transform for each input control. If a default behavior does not work, you can override the default with your own inbound transform for a specific Virtual Agent server control. | Y |
| Input | Output |
|---|---|
| - Headers \(JSON\) - Request headers - Payload \(JSON\) - Request body | - Token \(String\) - Authentication token. - provider\_user\_id \(String\) - Name of the sender, for example, a user name in a Slack message. - request\_context \(JSON\) - Actual content received. - typed\_value - Typed content such as an answer to a question. - attachment\_value.url - URL of the attachment. - attachment\_value.content\_type - Content type of the attachment. - attachment\_value.name - File name of the attachment. - contextual action - Action to perform, such as END\_CONVERSATION, START\_CONVERSATION, AGENT. This script looks at the incoming message. If there is a recognized keyword, then one of the actions can be set, such as outputs.request\_context.contextual\_action = "END\_CONVERSATION";. The framework recognizes this keyword and invokes the contextual action script. - context\_vars - Contextual variables to pass into a conversation, such as \`outputs.request\_context.context\_vars = \{language: "en"\};\`. |
| Input | Output |
|---|---|
| - Results \(JSON\) - Array of messages to send. - Payload \(JSON\) - Last payload received from a requester. - app\_inbound\_id \(String\) - Identifier of the sender, such as the "From" phone number in an SMS case. - channel\_user\_id \(String\) - Identifier of the recipient, such as the "To" phone number in an SMS case. | None |
| Input | Output |
|---|---|
| - conversation\_id \(String\) - The conversation ID. - request\_context \(JSON\) - Request passed from the provider attribute script that contains the action to perform, such as \(END\_CONVERSATION, START\_CONVERSATION, AGENT\). | None |
| Input | Output |
|---|---|
| - provider\_user\_id \(String\) - Identifier of the user who is linking, such as a phone number in an SMS case. - status \(String\) - Success/failure. - user\_id \(String\) - ServiceNow user\_sys\_id for the account that was linked to. | None |
| Input | Output |
|---|---|
| - message\_id \(String\) - Identifier of sys\_cs\_message record that is associated with this response. - headers \(JSON\) - Headers that are received. - body \(String\) - Body that is received. - status code \(Integer\) - The HTTP status code that is received. | None |
| Outbound transformer name | Script example |
|---|---|
| Virtual Agent - SMS Twilio Adapter Default Text Outbound Transformer | ``` (function execute(inputs, outputs) { var rich_control = inputs.rich_control; outputs.result = rich_control['label']; })(inputs, outputs); ``` |
| Virtual Agent - SMS Twilio Adapter Default Link Outbound Transformer | ``` (function execute(inputs, outputs) { var richControl = (inputs.rich_control); var value = richControl.value; outputs.result = richControl.header + ': ' + value.action; })(inputs, outputs); ``` |
| Virtual Agent - SMS Twilio Adapter Default Picker Outbound Transformer | ``` (function execute(inputs, outputs) { var rich_control = inputs.rich_control; var options = rich_control['options']; if (options && options.length > 0) { var optionsLength = options.length; var picker = rich_control['label'] || ""; for (var x = 0; x < optionsLength; x++) { picker += "\n" + (x+1) + ": " + options[x].label; } } outputs.text_message = picker; })(inputs, outputs); ``` |
| Virtual Agent - SMS Twilio Adapter Default Multi Link Outbound Transformer | ``` (function execute(inputs, outputs) { var rich_control = inputs.rich_control; var linkResult = rich_control['header']; var totalValues = rich_control['values'].length; for (var i = 0; i < totalValues; i++) { linkResult = linkResult + '\n'; var linkValue = rich_control['values'][i]; linkResult = linkResult + '\n' + linkValue['description'] + ': ' + linkValue['action']; } outputs.result = linkResult; })(inputs, outputs); ``` |
| Virtual Agent - SMS Twilio Adapter Default Card Outbound Transformer |
```
(function execute(inputs, outputs) {
var richControl = (inputs.rich_control);
var data = JSON.parse(richControl.data);
var tableTitle = data.title;
var fields = data.fields;
var subtitle = data.subtitle;
var url = data.url;
var message = subtitle ? getCardLine(tableTitle, subtitle) : tableTitle + "\n";
for (var i= 0; i |
| Inbound transformer name | Script example |
|---|---|
| Virtual Agent - SMS Twilio Adapter Default Text Inbound TransformerPurpose: Find the text response to the InputText question posed | ``` (function execute(inputs, outputs) { var requestContext = inputs.request_context; var richControl = inputs.rich_control; var typedValue = requestContext['typed_value']; var attachmentValue = requestContext['attachment_value']; var smsUtil = new VASMSTwilioUtil(); var result = {}; if(richControl['itemType'] == 'image' || richControl['itemType'] == 'file') { if (attachmentValue && attachmentValue.content_type.includes('image')) { result.url = attachmentValue.url; result.content_type = attachmentValue.content_type; result.name = attachmentValue.name; result.headers = attachmentValue.headers; } else if (typedValue) { result.url = typedValue; var contentType = smsUtil.getContentType(typedValue); if (typeof contentType === 'undefined') gs.error('Please enter a valid image link. Inbound image link: {0}', typedValue); else result.content_type = contentType; } else { gs.error('Please upload an image or enter a valid image link.'); } } result['value'] = typedValue; result['search_text'] = ''; outputs.result = result; })(inputs, outputs); ``` |
| Virtual Agent - SMS Twilio Adapter Default Picker Inbound TransformerPurpose: Find the sys\_id of the option that the user selected for this picker question. This script uses the same algorithm of the outbound transformer. If no options are selected, and the user typed something instead, then the "search\_text" is set and fed into NLU if it is enabled on this instance. | ``` (function execute(inputs, outputs) { var request_context = inputs.request_context; var rich_control = inputs.rich_control; var selectedValue = request_context["typed_value"]; var result = {}; result["value"] = ""; result["search_text"] = ""; if (rich_control['options']) { var options = rich_control['options']; var optionsLength = options.length; selectedValue === Number(selectedValue) if (selectedValue > 0 && selectedValue <= optionsLength) { var selectedOption = options[selectedValue - 1]; result["value"] = selectedOption.value; } else { result["search_text"] = request_context["typed_value"]; } } outputs.result = result; })(inputs, outputs); ``` |