Tray Embedded / Advanced Topics / Custom JS / Components


The Tray Javascript class

To interact with the slots in the javascript code, you need to interact with the tray javascript class. This class comprises the following:

  1. Events: these allow you to listen to the load and change events of each slot in the wizard.

  2. Functions: these allow you to interact with the system and extend the functionality of your code.

  3. Environment variables: these allow you to define a set of variables in the code editor, allowing you to abstract the code more easily.

The slot state

The purpose of Custom JS code is to modify the state of a slot.

Each event is invoked with a set of arguments that describes the different states that you need to listen to.

In the javascript code you either return nothing (maintain the current slot state) or you return a new slot state.

The slot state is a javascript object that comprises the following properties:

  1. externalId: the external ID of the slot, which matches what you can find in the slot's settings

  2. jsonSchema: the schema that defines how the slot should be rendered in the wizard. Follows the JSON schema standard that you can learn more about here:

  3. status: the status of the slot - one of HIDDEN , LOADING , VISIBLE . When a slot is first mounted on to a screen (loaded), it's status is always LOADING .

  4. value: the value of the slot

  5. validation: an object that can be specified in order to throw an error message and block the user from proceeding past the current config wizard screen. When specified as an empty object (default) or undefined no error message will be shown.

  6. className: set a custom class name for the slot. You can use this when defining CSS rules to change the style of the slot.

The state schema is defined as follows in Tray (denoted by JavascriptConfigItemResult):

type JavascriptConfigItemResult {
status: 'loading' | 'hidden' | 'visible';
className ? : string;
validation: SlotValidation;
jsonSchema: TraySchema7;
value: any; // Config slot value
type SlotValidation = {
status: 'ERROR';
message: string;

Here is an example state that you could expect to see:

"externalId": "external_salesforce_object",
"jsonSchema": {
"type": "string"
"status": "VISIBLE",
"value": "First name",
"validation": {
"status": "ERROR",
"message": "First name is not a valid value for this field"

Tray events

A note on events: let's say we have a screen where we have two config slots, and on the second slot we have enabled custom javascript. The script for the second slot will listen to events occurring in all slots on the same screen. In this way, you can define dependencies between slots on the same screen (see example for Showing/hiding slots based on dependencies [link])

  1. The CONFIG_SLOT_MOUNT event is fired when a config slot has loaded onto the screen.

  2. The CONFIG_SLOT_VALUE_CHANGED event is fired when the value of a config slot has been changed.

  3. The CONFIG_SLOT_STATUS_CHANGED event is fired when the status of a config slot has been changed.

  4. The AUTH_SLOT_MOUNT event is fired when an auth slot has loaded onto the screen.

  5. The AUTH_SLOT_VALUE_CHANGED event is fired when the value of an auth slot has been changed, i.e. a new authentication has been selected.

When the custom javascript is enabled on a slot for the first time, you will notice an example implementation is prefilled that implements the CONFIG_SLOT_MOUNT event.

Event arguments

Each event is invoked with an object argument that contains the following properties:

  1. event: contains information about the type of event that has fired, as well as data about the state of the slot for which the event fired. As an example, if the slot with external ID external_slot_id fires the CONFIG_SLOT_MOUNT when first rendered on a screen, and has the schema illustrated in the above example [link to above example], then the value of event would be similar to the following:

"data": {
"externalId": "external_salesforce_object",
"jsonSchema": {
"type": "string"
"status": "LOADING"
  1. previousSlotState: contains information about the state of the slot before the current event fired. This is most useful when listening to changes in other slots, as this property will contain the current schema of the slot where the javascript is implemented. The example _ below shows how this is used in practice. The value of this is the slot's state schema, so in this example where the slot is mounted and no schema has been set, this would be:

"externalId": "external_salesforce_object",
"jsonSchema": {},
"status": "LOADING"
  1. previousWizardState: contains information about the state of the whole config wizard, including for every page. This is most useful when rendering the new slot state depends on the values of other configuration and authentication slots in the wizard. For instance, you can access the ID of an authentication that has been created by the user for a service in order to dynamically fetch data via API calls and display values to the user as dropdowns. This property has the following schema (denoted by EmbeddedState):

type EmbeddedState {
currentScreen: {
index: number;
status: 'loading' | 'visible';
values: {
[slotExternalId: string]: any
validation: {
[slotExternalId: string] ? : SlotValidation
type SlotValidation = {
status: 'error';
message: string;

Here's an example for our use case of the previousWizardState when the external_salesforce_object slot mounts and it's the second slot in the wizard:

"currentScreen": {
"index": 0,
"status": "VISIBLE"
"values": {
"external_acme_object": "Contact"
"validation": {}

If the slot external_acme_object is an authentication slot, then its value is the ID of the authentication that has been created for the end user in

Tray functions

The tray class currently supports the following function:

  1. callConnector: a wrapper around the GraphQL API mutation callConnector that can be invoked without requiring an API key, in order to call any connector operation that you can find in the workflow builder. This function will return a promise that will resolve to the output of the connector call. You can find more information on the API mutation here:

Here is an example of invoking this function in the javascript body to fetch information from Salesforce:

// in this example we assume that we have an authentication slot for Salesforce with external ID external_salesforce_authentication
const authId = previousWizardState.values.external_salesforce_authentication;
const fetchSalesforceAccountFields = await tray.callConnector({
connector: 'salesforce',
version: '7.5',
operation: 'so_object_describe',
input: {
object: "Account"

Note: unlike the API mutation callConnector, the input does not need to be JSON stringified before sending to the request.

Tray environment variables

Environment variables allow you to define a set of variables in the code editor, allowing to abstract the code more easily. You can set them inside the code editor, and then reference them through the tray class in the code under the key env.

The simplest example of this is by default the code editor is set up with the environment variable "slotExternalId" - in order to allow you to easily reference the external ID of the slot you are currently writing code for in the editor. You can access this environment variable in your code under the property tray.env.slotExternalId

const slot_external_id = tray.env.slotExternalId;

You can define environment variables in the right-hand panel of the javascript code editor:


Which you can use to reference previously set external variables: