Skip to content

Latest commit

 

History

History
321 lines (256 loc) · 14.4 KB

changesets_README.md

File metadata and controls

321 lines (256 loc) · 14.4 KB

Demo: Data-Driven IVR Routing with InProd Changesets

Objective

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 a Banking_Department_Map datatable that maps each DTMF digit to a department, a target queue, and a priority.
  • Demo Banking Flow - Manage Menu Options.yaml — creates the Interactive Banking Flow IVR, whose "Main Menu" DTMF choices and spoken prompt are not hand-authored — they're generated at changeset-run time by an evaluator_script that 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.


Architecture at a glance

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.


Prerequisites

  • Access to an InProd environment (referred to as environment: 10 in these changesets).
  • Deployment in this repo is done via the inprod/github-run-changesets GitHub Action (see .github/workflows/deploy-changesets.yml), triggered on push to the Dev, UAT, or Prod branches when files under changesets/** change. The branch name becomes the environment input to the action.
  • The workflow needs INPROD_API_KEY and INPROD_BASE_URL. Before running this for a real demo, check deploy-changesets.yml and make sure INPROD_API_KEY is sourced from ${{ secrets.INPROD_API_KEY }}, not a literal token committed in the file, and that INPROD_BASE_URL points at the environment you actually intend to demo against.

Step 1 — Deploy the routing queues

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.


Step 2 — Deploy the department datatable

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.


Step 3 — Deploy the IVR flow

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 Menu DTMF menu, whose audioTts is the tag [?? menuPrompt ??] and whose choices are 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 Department task that:
    1. Runs a dataTableLookup against Banking_Department_Map, keyed on Flow.SelectedDept (the department key set by whichever menu choice fired), and pulls out DepartmentName, PriorityLevel, TargetQueue.
    2. If the lookup doesn't find a match (notFound), it falls back to Bank_General_Inquiries_Queue at priority 1 — so Bank_General_Inquiries_Queue is both the row-5 target and the safety-net for anything unrecognized.
    3. Transfers to ACD (transferToAcd) at the queue/priority resolved above, with a spoken "please hold while we transfer you to <department>" prompt.

Step 4 — How the menu builds itself

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.


Step 5 — Live demo: the flow changeset updates the menu options

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:

  1. Deploy the queues (Step 1) and the datatable (Step 2) first.
  2. Deploy Demo Banking Flow - Manage Menu Options.yaml (Step 3). As it runs, its evaluator_script (Step 4) queries Banking_Department_Map live, finds all 5 records — including DEPT_GENERAL — and rebuilds Main Menu.choices from scratch, plus regenerates the audioTts prompt to match.
  3. 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.


Verification checklist

  • Before running for real, use InProd's changeset preview/documentation output for the flow changeset to check the resolved audioTts value looks right.
  • In Genesys Cloud Architect, open Interactive Banking Flow → Main Menu and confirm the choices list 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 notFound fallback sends the call to Bank_General_Inquiries_Queue at priority 1.

Troubleshooting / gotchas

  • Deploy order. If the flow changeset runs before the datatable exists, getObject(...) in the evaluator script won't find Banking_Department_Map, the if (datatable && routeToDeptTask) guard fails, menuDtmfChoices stays empty, and mainMenu.choices is 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 / menuDtmfChoices changeset variables are placeholders only. The checked-in default ("To end the call, press 9." for menuPrompt) is just a fallback value in case the evaluator script's return value isn't applied — the real prompt always comes from the script.

Appendix — file reference

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.