Architect Scripting

Architect Scripting lets you create and manipulate Genesys Cloud Architect flows using JavaScript. This help documents the Architect Scripting JavaScript SDK, provides installation instructions to get Architect Scripting up and running on your machine and also provides some example bits to help get you started.

Installation

Before installing Architect Scripting, you'll want make sure you have Node Package Manager (npm) installed. If you don't have npm installed, you can install npm here.

Then to install Architect Scripting at the command line using npm, execute this:

npm install purecloud-flow-scripting-api-sdk-javascript

Here is the link to Architect Scripting in npm.

Changelog

As newer versions of Architect Scripting are released, we have a changelog that describes version changes.

Getting Started

Architect Scripting is a JavaScript SDK for Genesys Cloud's Architect. It lets you programmatically create and modify Architect flows using code instead of using the Architect UI.

In order to use Architect Scripting to do work, you'll need to have either a Genesys Cloud authorization token ( a.k.a. auth token ) or an OAuth client whose client id and secret can be passed to Architect Scripting for authentication purposes at startup. When you're using Scripting, remember that the permissions set for the authenticated user / OAuth client which you're using still come in to play because Architect Scripting uses the same core bits that the Architect UI uses. For example, if the authenticated user or OAuth client you're using for Scripting does not
have permission to search queues, then calls to set a queue by name will fail.

The next sections describe how to set up an OAuth client that's compatible with Architect Scripting. If you have an auth token that you're planning to use for authentication instead, you can skip the OAuth client setup steps and go to the Overview section where we start showing "real code".

OAuth Client Setup

Architect Scripting supports OAuth clients with either a code authorization or client credentials grant. If you want to run Architect Scripting in a truly automated fashion with no UI, an OAuth client with a client credentials grant is the way to go. You need to remember that actions performed by Scripting when using this OAuth client type are recorded on behalf of the OAuth client and not in the context of a user. An OAuth client with a code authorization grant will perform actions on behalf of a user but it's possible that a login UI will be presented for a user to provide their email and password. For example, if you created an inbound call flow using an OAuth client with client credentials, the creator of that flow in Architect's flow version history dialog will be the OAuth client and not a user. If you use an OAuth client with a code authorization grant, the creator of that flow would be a user.

The Genesys Cloud Resource Center has this help page that discusses how to set up an OAuth client. The next two sections describe what to configure when setting up an OAuth client with a code authorization or client credentials grant.

** Once you create an OAuth client, you'll want to save off the client id and client secret as you'll need them later. **

Client Credentials Grant

When setting up an OAuth client with a client credentials grant, make sure to configure these settings:

  • Token duration -> 86400 seconds ( recommended )
  • Roles on the Roles tab. The roles are what gives this OAuth client permissions in your organization. In the Genesys Cloud Resource Center we have an Architect permissions overview page that describes various permissions needed by Architect. One thing to remember is that the 'architect:ui:view' permission is needed to use Architect Scripting itself.

Code Authorization Grant

When setting up an OAuth client with a code authorization grant, make sure to configure these settings:

  • Token duration -> 86400 seconds ( recommended )
  • Authorized redirect URI -> http://localhost:9001/callback

And if scopes are available to configure on the OAuth client, it's technically dependent on how you plan to use Scripting. But in general, outside of flows, Scripting itself is mainly a read-only consumer of data. Here is an example list of scopes set for Architect Scripting:

  • architect, authorization:readonly, groups:readonly, integrations:readonly, notifications, organization:readonly, outbound:readonly, response-management:readonly, routing:readonly, scripts:readonly, telephony:readonly, users:readonly

It's just an example but if you plan to do other work with this OAuth client such as create queues using the Genesys Cloud Platform API JavaScript SDK and then create a flow that transfers to a newly created queue using Architect Scripting, you'd need to update the scopes to have more than routing:readonly.

Overview

Once you have either an authToken or a client id and secret, you're ready to start using Architect Scripting.

The typical structure of an Architect Scripting project looks like the following:

  • If you're using a client id and secret, your code starts the scripting session by calling the ArchSession's startWithClientIdAndSecret method. If you're using an auth token, your code calls the ArchSession's startWithAuthToken method. In either of the start methods you pass in a "main" callback function that the session that contains the logic you want to perform.

  • Once authenticated, the session's status transitions to running and your callback function will be invoked. It's there where your function will do work like creating a flow and configuring it. Your callback function will often times return a promise. That's fine. Scripting will wait until it resolves.

  • When your code execution is complete, the scripting session will transition to ended and end.

  • It is important to note that the Architect Scripting package should only be required by or imported into your project once. If it is needed by multiple files across your project, the single instance should be made available to other files by exporting it or by passing a reference to it within function calls.

When you become more familiar with Scripting, you'll see that you can also specify a session end callback that gets called when a session ends, etc. etc. Architect Scripting has a lot of functionality and while we could talk about more of the overall design of Architect Scripting, let's start off with some example code that demonstrates creating a flow, configures some settings on it, validates it, and if there are no validation warnings or errors, it will publish it to make it available in Architect.

Basic JavaScript Example

This example code shows how to create an inbound call flow, add a task with a disconnect action in it, configure some speech rec settings, validate it and if there are no validation errors or warnings then publish it. Do note that if you actually try to run the example code below, it won't run because it requires modification to specify a real client id, client secret and organization location.

Finally, here's some code!

// --------------------------------------------------------------------------------
// Require in the Genesys Cloud Architect Scripting SDK
// --------------------------------------------------------------------------------
const architectScripting     = require('purecloud-flow-scripting-api-sdk-javascript');

// --------------------------------------------------------------------------------
// See above in the readme for information on creating a client id / secret.
// We will use these when starting the Architect Scripting session below.
// Remember, the Architect Scripting session object also has a way to start
// with you supplying an auth token too.
// --------------------------------------------------------------------------------
const clientId                  = '<client id>';
const clientSecret              = '<client secret>>';
const clientIsClientCredentials = true;  // set to false if using an OAuth client
                                         // that has a code authorization grant
                                         // instead of client credentials grant. 

// --------------------------------------------------------------------------------
// Flow name and description constants for the flow that will be created.
// --------------------------------------------------------------------------------
const flowName               = 'MyFlow';
const flowDescription        = 'This is a flow created using Architect Scripting';

// --------------------------------------------------------------------------------
// Helpers to make sample code more readable below.
// --------------------------------------------------------------------------------
const scriptingActionFactory = architectScripting.factories.archFactoryActions; // Factory to create actions
const scriptingEnums         = architectScripting.enums.archEnums;              // Enum support
const scriptingFlowFactory   = architectScripting.factories.archFactoryFlows;   // Factory to create flows
const scriptingLanguages     = architectScripting.languages.archLanguages;      // Language support
const scriptingSession       = architectScripting.environment.archSession;      // Session support
const scriptingTaskFactory   = architectScripting.factories.archFactoryTasks;   // Factory to create tasks
const scriptingLogger        = architectScripting.services.archLogging;         // Logging support

// --------------------------------------------------------------------------------
// Enables additional logging during execution.  It definitely helps when
// debugging your code so we want to show how to enable it in this example.
// --------------------------------------------------------------------------------
scriptingLogger.logNotesVerbose = true;

// --------------------------------------------------------------------------------
// Set up a constant for the organization's location.
// --------------------------------------------------------------------------------
const location = scriptingEnums.LOCATIONS.prod_us_east_1;

// --------------------------------------------------------------------------------
// This is the main function where we'll do the work of creating a flow,
// configuring it and then publishing it if there are no validation warnings
// or errors.
// --------------------------------------------------------------------------------
function doWork(scriptSession)  {

    // Return the flow creation promise here and pass in a callback function
    // to call when the flow is created.  By "created", this flow exists in
    // memory at this point.  We'll publish it later to make it available to
    // someone using Architect.  We use the flow factory to create the inbound
    // call flow.
    return scriptingFlowFactory.createFlowInboundCallAsync(flowName, flowDescription, scriptingLanguages.englishUnitedStates, function(archInboundCallFlow) {

        // This shows how you can configure audio in a call flow.  We'll set up
        // the initial greeting to use text to speech and say "welcome to the call flow"
        // using an expression.
        archInboundCallFlow.initialAudio.setDefaultCaseExpression('ToAudioTTS("welcome to the flow")');

        // Here we will turn off company directory and speech recognition for the flow.
        archInboundCallFlow.settingsSpeechRec.asrCompanyDir = scriptingEnums.SPEECH_REC_COMPANY_MODES.none;
        archInboundCallFlow.settingsSpeechRec.asrEnabledOnFlow = false;

        // In Architect Scripting, it's different than the Architect UI where there is
        // a main menu automatically created when you create a flow.  In Scripting you
        // get a blank flow so it's up to you how to configure its startup.  You'll need
        // to set a startup object which should be either a task or a menu for an inbound
        // call flow.  For this example we'll create a startup task with a disconnect
        // action in it to keep things simple.

        // Here is how you can add a reusable task to the flow.  Notice how we're
        // using the task factory to do this.  We'll also make this the flow's startup task
        // by setting true at the end of this call.
        const startupTask = scriptingTaskFactory.addTask(archInboundCallFlow, 'startup task', true);

        // Add a decision action that checks to see if 5 is greater than 3 using an expression.
        // You could assign the expression on this call but we'll show how to do it by accessing
        // the condition property.
        const decisionAction = scriptingActionFactory.addActionDecision(startupTask, 'greater than check');

        // The expression text we assign is like you'd enter in the Architect UI but escaped for
        // JavaScript since we're assigning the expression text in code.
        decisionAction.condition.setExpression('5 > 3');

        // For the fun of it, we'll add a disconnect action to the yes output on the decision action.
        scriptingActionFactory.addActionDisconnect(decisionAction.outputYes, 'yes output disconnect');

        // Now we'll add a disconnect action to the end of the task.  As you can see, factories are
        // used when creating various things like menus, actions, tasks or even flows themselves.
        scriptingActionFactory.addActionDisconnect(startupTask, 'end of task disconnect');

        // This flow doesn't really do anything but the above code shows how
        // to interact with Architect Scripting.

        // Next we'll validate the flow.
        // When we get the validation results back, we'll then check the
        // results to see if there are any validation errors or warnings.
        return archInboundCallFlow.validateAsync()
            .then(function (validationResults) {

                // Does the flow have any errors or warnings?
                if (validationResults.hasErrors) {
                    scriptingLogger.logError('There is at least one validation error in the created flow.  Not publishing.');
                }
                else if (validationResults.hasWarnings) {
                    scriptingLogger.logWarning('There is at least one validation warning in the created flow.  Not publishing.');
                }
                else {

                    scriptingLogger.logNote('The flow has no validation errors or warnings.  Time to publish it.');

                    // One thing to note during a publish is that Architect looks to see if there is a flow that
                    // already exists with this name.  If so, it will delete that flow first then publish this one.
                    // As such, you'd want to amke sure that the user associated with this session has the
                    // architect:flow:delete permission. :)
                    return archInboundCallFlow.publishAsync()
                        .then(function () {
                            scriptingLogger.logNote();
                            scriptingLogger.logNote('****************************************************************************');
                            scriptingLogger.logNote('The flow \'' + archInboundCallFlow.name + '\' is now published in and available in Architect.');
                            scriptingLogger.logNote('Flow URL: ' + archInboundCallFlow.url);
                            scriptingLogger.logNote('****************************************************************************');
                            scriptingLogger.logNote();
                        }
                    );
                }
            }
        );
    });
}

// This will start off the Architect Scripting code and call the doWork function.
scriptingSession.startWithClientIdAndSecret(location, doWork, clientId, clientSecret, void 0, clientIsClientCredentials);

We have an example later on this page that shows how you can use Architect Scripting in conjunction with the Genesys Cloud Platform SDK.

Concepts and Terminology

In this section we discuss some of the main Architect Scripting concepts and terminology used in Architect Scripting and this help documentation.

Factories

There are lots of Architect scripting classes that you'll use when writing Architect scripting code. One set that is very important to know about are the Architect scripting factory classes that let you create flows, add actions, menus or tasks to flows and more. To put it simply, factories create stuff and things. Notice in the sample code above that we use the Architect Scripting flow factory to create an inbound call flow, the task factory to create a task that's added to the flow and then the action factory to add an action to the task.

Here is the list of various factories and what they should be used for:

  • ArchFactoryFlows - This factory lets you create flows. When you create a flow, the returned ArchFlowInboundCall, ArchFlowInQueueCall or ArchFlowOutboundCall resides locally in memory. An example method that creates an inbound call flow would be the createFlowInboundCallAsync method. It is when you call saveAsync, checkInAsync or publishAsync on the returned flow from the create call that the flow gets created in Architect. By doing this it makes local development go quickly so you can iterate with code changes and not have to create the flow on the server when it is initially created.

  • ArchFactoryActions - The actions factory will let you add actions to a task or to an output on an action. For each action that's support in Architect scripting, there will be an add method for it such as addActionDecision. These add methods all have common parameters such as the name of the action you're adding and then there will be additional parameters on many of the add calls as well that let you specify additional settings on that action when it's created. These additional parameters are simple helpers that set properties on the action that you will have available to you in scripting. The add methods all return the Architect scripting class for the newly added action.

  • ArchFactoryMenus - This factory lets you add menus and sub-menus to a flow. The methods on this factory are similar to the actions factory in that the add methods will return instances of the newly added menu or sub-menu. Many of the returned menu / sub-menu instances from the add methods will have a pointer to the action associated with that menu. For example if you add a Transfer to ACD menu via. addMenuTransferToAcd, it will return an ArchMenuTransferToAcd instance. On that instance is an actionTransferToAcd property that contains the underlying ArchActionTransferToAcd action associated with that menu choice.

  • ArchFactoryPrompts - The prompt factory can be used to look up system or user prompts. It currently does not have any way to add, delete or edit prompts. If you wish to do that, please use the Genesys Cloud Platform API JavaScript SDK itself.

  • ArchFactoryTasks - This final factory lets you add tasks to a flow. It's the most basic of all factories in that it only has one method called ArchFactoryTasks#addTask which will return a the newly added ArchTask to the flow. Remember that tasks can be added to all flow types except the ArchFlowInQueueCall flow type.

Values

Values in Architect Scripting are very important. They are pretty much the core of what you see in the Architect UI when you specify a value on a setting such as the condition property where you would specify the boolean value to check at runtime to determine if execution should take the yes or no output.

A value in Architect scripting such as ArchValueInteger has an Architect data type associated with it. This data type tells you what kind of data is contained within the value. They also have a settings property that will provide meta data to let you know how they can be configured if you're not sure.

Let's look at what can be specified for a value:

  • Literal - Literal values are set on values with setLiteral calls such as setLiteralInt on the integer data type. If the data type supports methods for setting a literal you can call the method to set the value. It's important to note that values do have the concept if being an output which can be viewed from the settings on a value. If you attempt to assign a literal to a value that is set to an isOutput, the assignment will fail. If you are curious if a literal can be assigned to a value, you can always check the canBeLiteral property on the value's settings. Some values like ArchValueUser will represent a value that is network based. For example, a user would be a Genesys Cloud user set up for the organization that you're logged in to. Setting literal values on network based objects is an asynchronous operation where a promise returned while the scripting layer looks up the literal value supplied. An example of this would be the setLiteralByUserNameAsync method that would set the user value by their username / email address.

  • Variable - Assigning a variable to a value is done by calling the setVariable method on the variable. You can assign variables by using an existing Architect variable or by passing in text that specifies the variable such as 'Flow.MyValue'. One handy feature of setting a variable on a value is that if the variable you're trying to assign doesn't exist, the scripting framework will create it for you with a type appropriate for the value. And lastly, you can check to see if a variable can be assigned to a value by checking the canBeVariable property on the value's settings.

  • Expression - When you assign an expression to a value, that's assigning expression text. This would be the same expression text that you would assign in the Architect UI. You can assign expression text by calling setExpression to set a value as an expression. Similar to the variable and literal, there is a canBeExpression property on the value's settings that will tell you if setting an expression on the value is allowed. Different value types will have other methods to set an expression on a value. The setExpression is the most basic version available but as an example, there is a setExpressionMaxInt method on ArchValueInteger which will set the maximum integer value allowed in Architect as an expression. So when you're looking to set an expression, you can always use setExpression but there might be other setExpression helper methods available as well. It will depend on the value you're working with.

  • No Value - You serious? A "no value" value? Yup. This isn't something that you'll normally need to use but certain settings do allow having no value specified. That is different than a NOT_SET value or a blank string. It really does mean that no value is being specified. To see if no value is allowed, there is a canBeNoValue property. For values that are allowed to have no value specified, this is the case when it's optional for a flow author to specify a value as well as values that have defaults either at the flow action setting level or some other kind of default runtime handling. See setNoValue for more information.

Menu Choices

Menu choices are pretty straightforward. They are slightly different from top level menus in that they have DTMF settings that a top level menu would not. If you've ever looked at menu choices in Architect's UI, outside of settings specific to a menu choice they have settings that match the action with the same type. It probably makes sense because in the Architect UI users drag actions out to the menu are create menu choices. As such, you'll see the action associated with the menu choide in scripting is available off of ArchBaseMenuChoice instances. Menu choices are containers that have an underlying action associated with them. For example, the ArchMenuDisconnect menu has an actionDisconnect property which returns the disconnect action that's associated with the disconnect menu choice. One thing to remember is that when an action is part of a menu choice, you cannot add any new actions to the outputs. You'll notice that in Architect's UI we don't allow that and the same restriction exists in scripting. It's fine to add actions to an action's output when the action exists within a task.

Other Stuff

The 'is' identity properties

If you're ever wondering what the various read-only is properties are for on Scripting objects like isArchMenuSubMenu for a sub menu, their purpose is to give script authors a quick and easy way to check if an object is a particular Architect scripting type. It's used extensively within the Scripting framework to do parameter validation. It may not seem all that useful but you might need it. For example, when you validate a flow, the validation results that come back will give you access to the validation issues. If you look at a validation issue, there is an archObject property that returns the item that has the error or warning associated with it. You could ask the returned object for its displayTypeName or use one of these is properties to determine what kind of object it is that has the warning or error.

What's the deal with all the read-only properties on objects in this documentation?

Within Architect scripting we're making improvements in the documentation to address properties that have only a getter and mark them as readonly. For example, many of the value properties are marked as read-only. Don't fret. This doesn't mean that the values can't be configured to a literal, expression or whatever. It only means that you can't assign the underlying value instance itself. That management is handled by Architect Scripting. You can think of it more like the value you interact with in code as a pointer where you can call methods / properties off it but not change out the pointer itself. For example, the positionInQueue property on the play position in queue action returns an ArchValueInteger object. You cannot change the ArchValueInteger object itself to a different ArchValueInteger instance on the action so it's marked as readonly but you can call methods on it like setLiteralInt to set a literal integer value of 5. We wanted to point this out up front so you don't mistake the readonly setting in documentation to mean you can't call methods on properties that return objects.

Running JavaScript that uses Architect Scripting and the console

The Architect scripting code will trace out lots of information to the console when you're creating flows, adding tasks, menus, setting values, etc. etc. It's probably obvious but when you encounter errors or unexpected behavior please take a look at the console tracing. It will often times hold the information you'll need to troubleshoot what happened when your JavaScript code ran. :)

Also remember that you can turn on verbose logging by setting it to true to get more console output.

Asynchronous methods

Asynchronous methods in Architect scripting take a callback function for most calls. Asynchronous methods end with "Async" in their name. The rule for asynchronous method calls is that they will return the object on which the asynchronous method was called back to the callback function if specified. For example, if you have code like this:

actionFactory.addActionCallData(archTask, 'Lookup a Contact').setDataActionByNameAsync('Data Action Name', function(newCallDataAction) {
    // do work here - presumably set values on inputs / outputs for the call data action
    // remember this gets called asynchronously after the data action is looked up successfully  
})

The first line calls the action factory to add a new call data action to a task. The ArchActionCallData instance is returned from the addActionCallData method and the setDataActionByNameAsync method is called on that. This is an asynchronous operation because the data action name 'Data Action Name' needs to be looked up. Once it is successfully resolved, your callback function will be called and the newCallDataAction parameter above would have the action returned by the addActionCallData call.

The one exception to this is the validateAsync function which is called on a flow when the promise resolves it will return the validation results from that call. Note that you can get back to the flow from the validation results by accessing the parentFlow property on it. ( yes, this will be updated to a callback function eventually )

Flow-level action defaults

Certain action or menu properties have flow-level defaults. To set a flow-level default, access the flowLevelDefault property off of the ArchBaseValue associated with the property like this:

 collectInputAction.interDigitTimeout.flowLevelDefault.setLiteralTimeParts(0, 0, 0, 6); 

To query an ArchBaseValue to see if it has a flow level default, you can use the hasFlowLevelDefault property.

Sample Code - Using the Genesys Cloud Platform SDK and Architect Scripting together

If you want to use both the Genesys Cloud Platform API JavaScript SDK and Architect Scripting in the same JavaScript file, below shows an example of how you can do it:


// --------------------------------------------------------------------------------
// Require in the Genesys Cloud Platform SDK and Architect Scripting SDK
// --------------------------------------------------------------------------------
const architectScripting = require('purecloud-flow-scripting-api-sdk-javascript');
const platformApi        = require('purecloud-platform-client-v2');

// --------------------------------------------------------------------------------
// See above in the readme for information on creating a client id / secret.
// We will use these when starting the Architect Scripting session.
// --------------------------------------------------------------------------------
const clientId                  = '<your client id>';
const clientSecret              = '<your client secret>>';
const clientIsClientCredentials = true;  // set to false if using an OAuth client
                                         // that has a code authorization grant
                                         // instead of client credentials grant. 

// --------------------------------------------------------------------------------
// Helpers to make sample code more readable.
// --------------------------------------------------------------------------------
const archEnums          = architectScripting.enums.archEnums;
const archSession        = architectScripting.environment.archSession;
const platformApiClient  = platformApi.ApiClient.instance;

// --------------------------------------------------------------------------------
// This function will be what is called by Architect Scripting since it is
// specified in the start call at the bottom.
// --------------------------------------------------------------------------------
function doWork(scriptSession) {

    // Once the session starts you can do this to assign the authToken from
    // the Architect session object to the platform API client.
    platformApiClient.setAccessToken(scriptSession.authToken);

    // Now you can make calls using the Genesys Cloud Platform client calls as well
    // as well as Architect Scripting calls because we've assigned the authorization
    // token.  :)

}

// This will start off the Architect Scripting code and call the doWork function
archSession.startWithClientIdAndSecret(archEnums.LOCATIONS.prod_us_east_1, doWork, clientId, clientSecret, void 0, clientIsClientCredentials);

Obviously there are other ways to accomplish this, but the above sample code shows how to get at the authorization token from the Architect Scripting session and then use it to set up the token on the platformApiClient. If you are using startWithAuthToken, you already have the authorization token and can set it on the platformApiClient directly or get it from the Architect Scripting session as shown above.