This tutorial walks through a demo scenario built on three InProd changesets that together stand up a small banking call-routing IVR in Genesys Cloud:
Demo Banking Routing Queues.yaml— creates 5 department queues.Demo Banking Flow Datatable.yaml— creates aBanking_Department_Mapdatatable that maps each DTMF digit to a department, a target queue, and a priority.Demo Banking Flow - Manage Menu Options.yaml— creates theInteractive Banking FlowIVR, whose "Main Menu" DTMF choices and spoken prompt are not hand-authored — they're generated at changeset-run time by anevaluator_scriptthat reads the datatable and builds the menu from it.
The point of this demo is that goal: the IVR's menu options and prompt text are
config-driven. The DEPT_GENERAL record (DTMF digit 5, general inquiries) is already
present in the datatable — but the flow's own checked-in "Main Menu" only lists 4
choices. The live-demo moment (Step 5 below) is deploying the flow changeset itself: its
evaluator_script reads the datatable, notices the 5th record, and updates the menu
choices and audioTts prompt accordingly — with no manual edits to the flow needed.
None of the three reference YAML files in
changesets/need to be edited to follow this tutorial. Everywhere this guide says "edit," it means authoring a new changeset — the reference files stay as committed.
Demo Banking Routing Queues.yaml Demo Banking Flow Datatable.yaml
│ │
│ creates 5 queues │ creates Banking_Department_Map
│ (Bank_Credit_Card_Queue, ...) │ 5 records: dept name, DTMF digit,
│ │ priority, target queue name
└───────────────┬─────────────────────────┘
│ both must exist before the flow
│ changeset is deployed
▼
Demo Banking Flow - Manage Menu Options.yaml
creates "Interactive Banking Flow" (INBOUNDCALL)
its evaluator_script queries the datatable live via getObject()
and rebuilds the Main Menu choices + audioTts prompt from it
Deployment order matters: the flow's evaluator_script calls getObject(...) against
Banking_Department_Map while the flow changeset is being processed, so the datatable
must already exist in the target environment first. The datatable's TargetQueue values
(Bank_Credit_Card_Queue, etc.) must also match real queue names, so the queues should
exist too before you rely on the flow actually transferring calls successfully.
Recommended deploy order: Queues → Datatable → Flow.
- Access to an InProd environment (referred to as
environment: 10in these changesets). - Deployment in this repo is done via the
inprod/github-run-changesetsGitHub Action (see.github/workflows/deploy-changesets.yml), triggered on push to theDev,UAT, orProdbranches when files underchangesets/**change. The branch name becomes theenvironmentinput to the action. - The workflow needs
INPROD_API_KEYandINPROD_BASE_URL. Before running this for a real demo, checkdeploy-changesets.ymland make sureINPROD_API_KEYis sourced from${{ secrets.INPROD_API_KEY }}, not a literal token committed in the file, and thatINPROD_BASE_URLpoints at the environment you actually intend to demo against.
File: changesets/Demo Banking Routing Queues.yaml
This changeset has two actions:
Action 1 (gencloud-create) creates one queue per entry in the routeQueues
changeset variable:
["Bank_Credit_Card_Queue", "Bank_Fraud_Hotline_Queue", "Bank_General_Inquiries_Queue",
"Bank_Wealth_Management_Queue", "Bank_Retail_Support_Queue"]It uses the iterator/evaluator script pair to loop once per name:
// iterator_script — repeat count = number of queue names
(function() {
return JSON.parse(routeQueues).length;
})()
// evaluator_script — pick this iteration's queue name
(function() {
return {
"currentQueue": JSON.parse(routeQueues)[inprod_iteration - 1]
}
})()The queue's name field is set to '[?? currentQueue ??]', which resolves to whatever
currentQueue the evaluator script returned for that pass. inprod_iteration is 1-based
(first pass is 1), so the - 1 correctly maps passes 1–5 onto array indices 0–4 — all
5 queue names get created, none skipped.
Action 2 (gencloud-update, static, no scripts) looks up
Bank_General_Inquiries_Queue (object_lookup: query, name="Bank_General_Inquiries_Queue")
and adds the "All Staff" member group to it — this is the queue that ends up handling
DTMF digit 5 / general inquiries.
File: changesets/Demo Banking Flow Datatable.yaml
Static gencloud-create of a datatable named Banking_Department_Map
(referenceKeyLabel: Department_ID), no scripts. Schema:
| Field | Type | Notes |
|---|---|---|
DepartmentName |
string | Spoken/human-readable department name |
TargetQueue |
string | Must match a queue name from Step 1 |
PriorityLevel |
integer | Transfer priority |
DtmfDigit |
integer | Which key the caller presses |
All 5 records already ship with this file:
| key | DtmfDigit | DepartmentName | PriorityLevel | TargetQueue |
|---|---|---|---|---|
DEPT_SAVINGS |
1 | Savings & Checking | 1 | Bank_Retail_Support_Queue |
DEPT_CREDIT |
2 | Credit Card Services | 2 | Bank_Credit_Card_Queue |
DEPT_INVEST |
3 | Wealth & Investment | 5 | Bank_Wealth_Management_Queue |
DEPT_FRAUD |
4 | Fraud and Security Alerts | 10 | Bank_Fraud_Hotline_Queue |
DEPT_GENERAL |
5 | Other Concerns | 1 | Bank_General_Inquiries_Queue |
Digit 5 / general inquiries (DEPT_GENERAL) is already present in this datatable —
nothing needs to be added here. What's missing is on the flow side: its checked-in Main
Menu only lists 4 choices. Step 4/5 use the flow changeset itself to update the menu
options so they catch up with this record.
File: changesets/Demo Banking Flow - Manage Menu Options.yaml
This creates Interactive Banking Flow (type: INBOUNDCALL) with:
- An initial greeting: "Welcome to Demo Bank."
- A
Main MenuDTMF menu, whoseaudioTtsis the tag[?? menuPrompt ??]and whosechoicesare generated at run time (see Step 4 — the checked-in static snapshot only shows 4 choices, but that's just the last export; the evaluator script rebuilds this from the datatable every time the changeset runs). - A
Route to Departmenttask that:- Runs a
dataTableLookupagainstBanking_Department_Map, keyed onFlow.SelectedDept(the department key set by whichever menu choice fired), and pulls outDepartmentName,PriorityLevel,TargetQueue. - If the lookup doesn't find a match (
notFound), it falls back toBank_General_Inquiries_Queueat priority 1 — soBank_General_Inquiries_Queueis both the row-5 target and the safety-net for anything unrecognized. - Transfers to ACD (
transferToAcd) at the queue/priority resolved above, with a spoken "please hold while we transfer you to<department>" prompt.
- Runs a
This is the mechanism worth walking an audience through. The flow action's
evaluator_script does the following (reformatted from the YAML for readability):
(function() {
// 1. Find the menu and the task to jump to, out of this action's own data
var flowMenus = inprod_data.menus || [];
var flowTasks = inprod_data.tasks || [];
var mainMenu = flowMenus.find(function(menu) {
return menu.name === "Main Menu";
});
var routeToDeptTask = flowTasks.find(function(task) {
return task.name == "Route to Department";
});
// 2. Fetch the current department datatable — a live query, not this action's data
var datatable = getObject(inprod_environment, "GenCloudFlowsDatatables",
"name=Banking_Department_Map", true);
var menuDtmfChoices = [];
var menuPromptItems = [];
var menuPrompt = "";
if (datatable && routeToDeptTask) {
datatable = JSON.parse(datatable); // getObject returns a JSON string
var records = datatable.records || [];
// 3. Order the choices by DTMF digit
var sortedRecords = records.slice().sort(function(a, b) {
return a.record.DtmfDigit - b.record.DtmfDigit;
});
// 4. Build one spoken prompt fragment + one menuTask choice per record
sortedRecords.forEach(function(x) {
var key = x.key;
var record = x.record;
if (record && key && record.DepartmentName && record.DtmfDigit) {
var departmentName = record.DepartmentName;
var dtmfDigit = record.DtmfDigit;
menuPromptItems.push(`For ${departmentName}, press ${dtmfDigit}.`);
menuDtmfChoices.push({
"menuTask": {
"dtmf": `digit_${dtmfDigit}`,
"name": key,
"task": {
"actions": [
{ "updateData": { "name": "Update Data", "statements": [
{ "string": { "value": { "exp": `"${key}"` },
"variable": "Flow.SelectedDept" } }
]}},
{ "jumpToTask": { "name": "Jump to Reusable Task",
"targetTaskRef": `/inboundCall/tasks/task[${routeToDeptTask.refId}]` } }
]
},
"globalDtmf": false,
"globalSpeechRecTerms": false
}
});
}
});
if (menuPromptItems.length) {
menuPrompt = menuPromptItems.join(' ');
}
// 5. Write the whole choices array back — mainMenu is the SAME object
// referenced inside flowMenus, so mutating it here mutates flowMenus too;
// setChangesetProperty still needs the full field value, not a partial patch.
if (mainMenu && menuDtmfChoices.length) {
mainMenu.choices = menuDtmfChoices;
setChangesetProperty("menus", flowMenus);
}
}
// 6. Return the generated prompt text — resolves [?? menuPrompt ??] in audioTts
return {
"menuPrompt": menuPrompt
};
})();In plain language: find the menu → fetch the datatable → sort by DTMF digit → rebuild the choice list → write it back → generate the spoken prompt from the same data. Since the datatable already has 5 rows including digit 5, deploying this flow changeset already produces a 5-option menu and a prompt that reads "For Savings & Checking, press 1. For Credit Card Services, press 2. For Wealth & Investment, press 3. For Fraud and Security Alerts, press 4. For Other Concerns, press 5." — none of that sentence is written anywhere in the flow changeset itself.
This is the actual "watch it happen" moment for an audience. The record is already sitting in the datatable (Step 2) — the demo is running the flow changeset and watching it pick that record up:
- Deploy the queues (Step 1) and the datatable (Step 2) first.
- Deploy
Demo Banking Flow - Manage Menu Options.yaml(Step 3). As it runs, itsevaluator_script(Step 4) queriesBanking_Department_Maplive, finds all 5 records — includingDEPT_GENERAL— and rebuildsMain Menu.choicesfrom scratch, plus regenerates theaudioTtsprompt to match. - Show the result: instead of the 4 choices in the flow's last checked-in snapshot, the deployed menu now has all 5, and the spoken prompt ends with "...For Other Concerns, press 5." — even though nobody hand-edited the flow's menu or prompt text. The changeset only needed to run for the menu to catch up with what's already true in the datatable.
Optionally, to make the "config-driven" point even more visible, follow up with a second
pass: author a new changeset (don't edit Demo Banking Flow Datatable.yaml) that
does a gencloud-update on Banking_Department_Map, changing DEPT_GENERAL's
DepartmentName to something like "General Inquiries", deploy it, then redeploy
only the flow changeset again and show the prompt text change to
"...For General Inquiries, press 5." with no flow edits at all.
- Before running for real, use InProd's changeset preview/documentation output for the
flow changeset to check the resolved
audioTtsvalue looks right. - In Genesys Cloud Architect, open
Interactive Banking Flow→Main Menuand confirm thechoiceslist matches the datatable (5 entries, correct DTMF digits). - Place a test call, listen to the generated prompt, and walk each DTMF digit to confirm it lands on the expected queue at the expected priority.
- Press an unmapped digit (or let the lookup fail) and confirm the
notFoundfallback sends the call toBank_General_Inquiries_Queueat priority 1.
- Deploy order. If the flow changeset runs before the datatable exists,
getObject(...)in the evaluator script won't findBanking_Department_Map, theif (datatable && routeToDeptTask)guard fails,menuDtmfChoicesstays empty, andmainMenu.choicesis never overwritten — you'll get whatever the last checked-in static snapshot was (digits 1–4 only), not a failure. Always deploy the datatable first. menuPrompt/menuDtmfChoiceschangeset variables are placeholders only. The checked-in default ("To end the call, press 9."formenuPrompt) is just a fallback value in case the evaluator script's return value isn't applied — the real prompt always comes from the script.
| File | object_type | Purpose |
|---|---|---|
changesets/Demo Banking Routing Queues.yaml |
GenCloudRoutingQueues |
Creates the 5 department queues, adds "All Staff" to the general-inquiries queue. |
changesets/Demo Banking Flow Datatable.yaml |
GenCloudFlowsDatatables |
Creates the Banking_Department_Map datatable (department ↔ DTMF digit ↔ queue ↔ priority). |
changesets/Demo Banking Flow - Manage Menu Options.yaml |
GenCloudFlows |
Creates the Interactive Banking Flow IVR; its evaluator script builds the Main Menu from the datatable. |