Lab #6b: Moveworks Bender

View as Markdown

Overview

  • Learning Objectives: Understand how to leverage Moveworks Bender to configure attribute mappings for users and tickets. Use the DSL & Bender Mapper Playground to validate configurations before applying them.
  • Estimated Time: 15 minutes
  • Prerequisites:
    • Lab 1 complete (snow connector with sys_user access)
    • Lab 2 complete (user identity configured — provides the Bender mapping to test against)

Key Concepts

Bender is the transformation engine that sits between connected raw system data and the Moveworks UI. It is essential for formatting data correctly:

  • Bender Mapping: The logic used to tell Moveworks how to display raw JSON values. For example, turning a technical “state” code into a human-readable word.
  • Display Value: A ServiceNow-specific concept where value is a raw ID (e.g., a sys_id) and display_value is the actual text shown to the user.
  • Fallback Logic: The use of the OR operator (e.g., phone.display_value OR "") to provide a default value and prevent the bot from breaking if a field is empty.
  • DSL & Data Mapper Playground: A sandbox where you can paste raw JSON from the API Playground to test if your Bender mappings will work before applying them. Relevant Documentation:
  • Moveworks Help: Bender Data Mapping
  • Moveworks Help: DSL & Data Mapper Playground
  • Moveworks Agent Marketplace: Agent Architect

🛠️ 1: Walkthrough

1.1: Generate a Moveworks API Key via Agent Studio

  1. Navigate to the HTTP Connectors page via the Waffle Menu to generate a Moveworks API Key
  2. Select the Credentials module
  3. Click Create
  4. Give your credential a name, and generate it as an API Key and Publish it
  5. Select Copy to Clipboard, and store this API key on your machine.
  6. In a separate tab, navigate to the Moveworks DSL and Data Mapper Playground. Enter your API key in the API Key section.

Note: If you receive an error that states that an on-prem agent cannot connect, try opening the page in an incognito browser window and try again

1.2: Get the Default Bender Mapping for Moveworks ServiceNow User Identity

  1. In Moveworks Setup, navigate to User Identity > Import Users > Edit
  2. Now that we have confirmed we can see users, we will use the following configuration example in Advanced Mode to grab the JSON block representing the configuration for ingesting users from ServiceNow
  3. Copy the JSON block that defines the ServiceNow User Ingestion Logic (Also available below)
1{
2 "internal_fields": {
3 "manager_resolver": "$TRIM(IF sys_id THEN sys_id.display_value OR \"\" ELSE NULL)"
4 },
5 "phone_number": "$TRIM(IF phone THEN phone.display_value OR \"\" ELSE NULL)",
6 "user_name": "$TRIM(IF user_name THEN user_name.display_value OR \"\" ELSE NULL)",
7 "employee_id": "$TRIM(IF employee_number THEN employee_number.display_value OR \"\" ELSE NULL)",
8 "department": "$TRIM(IF department THEN department.display_value OR \"\" ELSE NULL)",
9 "full_name": "$TRIM(IF name THEN name.display_value OR \"\" ELSE NULL)",
10 "user_id_info.user_email": "if $TRIM(IF email THEN email.display_value OR \"\" ELSE NULL) then [$TRIM($TRIM(IF email THEN email.display_value OR \"\" ELSE NULL))] else []",
11 "city": "$TRIM(IF city THEN city.display_value OR \"\" ELSE NULL)",
12 "country_code": "$TRIM(IF country THEN country.display_value OR \"\" ELSE NULL)",
13 "email_addr": "$TRIM(IF email THEN email.display_value OR \"\" ELSE NULL)",
14 "first_name": "$TRIM(IF first_name THEN first_name.display_value OR \"\" ELSE NULL)",
15 "timezone": "$TRIM(IF time_zone THEN time_zone.display_value OR \"\" ELSE NULL)",
16 "cost_center_name": "$TRIM(IF cost_center THEN cost_center.display_value OR \"\" ELSE NULL)",
17 "role": "$TRIM(IF title THEN title.display_value OR \"\" ELSE NULL)",
18 "manager_email": "(manager).value.$TRIM()",
19 "user_id_info.user_itsm_id_info": [
20 {
21 "first_name": "$TRIM(IF first_name THEN first_name.display_value OR \"\" ELSE NULL)",
22 "external_id": "$TRIM(IF sys_id THEN sys_id.display_value OR \"\" ELSE NULL)",
23 "integration_id": "\"snow\"",
24 "full_name": "$TRIM(IF name THEN name.display_value OR \"\" ELSE NULL)",
25 "itsm_user_id": "$TRIM(IF user_name THEN user_name.display_value OR \"\" ELSE NULL)",
26 "system": "\"SNOW\"",
27 "last_name": "$TRIM(IF last_name THEN last_name.display_value OR \"\" ELSE NULL)"
28 }
29 ],
30 "state": "$TRIM(IF state THEN state.display_value OR \"\" ELSE NULL)",
31 "user_tags": {
32 "FILTER()": {
33 "items": [
34 "\"BASIC_USER\"",
35 {
36 "CONDITIONAL()": {
37 "condition": "$LOWERCASE(((vip.value OR \"\"))) == $LOWERCASE((\"TRUE\"))",
38 "on_pass": "\"VIP\""
39 }
40 }
41 ]
42 }
43 },
44 "location": "$TRIM(IF location THEN location.display_value OR \"\" ELSE NULL)",
45 "last_name": "$TRIM(IF last_name THEN last_name.display_value OR \"\" ELSE NULL)"
46}
  1. Paste it into the Mappers section of the DSL & Bender Playground
  2. Navigate to API Playground, and query a user record, then copy an instance of the user record

Note: We’re using a single record here to mirror how Moveworks actually works. The system maps records one by one. Comparing your configuration against a single instance is the best way to preview how the data will look once it’s ingested.

1 {
2 "calendar_integration": {
3 "display_value": "Outlook",
4 "value": "1"
5 },
6 "country": {
7 "display_value": null,
8 "value": ""
9 },
10 "user_password": {
11 "display_value": "********",
12 "value": ""
13 },
14 "last_login_time": {
15 "display_value": "2019-04-05 22:16:30",
16 "value": "2019-04-05 22:16:30"
17 },
18 "source": {
19 "display_value": "",
20 "value": ""
21 },
22 "sys_updated_on": {
23 "display_value": "2025-12-29 10:39:30",
24 "value": "2025-12-29 10:39:30"
25 },
26 "building": {
27 "display_value": "",
28 "value": ""
29 },
30 "web_service_access_only": {
31 "display_value": "false",
32 "value": "false"
33 },
34 "notification": {
35 "display_value": "Enable",
36 "value": "2"
37 },
38 "enable_multifactor_authn": {
39 "display_value": "false",
40 "value": "false"
41 },
42 "sys_updated_by": {
43 "display_value": "system",
44 "value": "system"
45 },
46 "sys_created_on": {
47 "display_value": "2019-04-05 21:09:12",
48 "value": "2019-04-05 21:09:12"
49 },
50 "sys_domain": {
51 "display_value": "global",
52 "link": "https://mvwrk-feb-2539-instructor-0001.lab.service-now.com/api/now/table/sys_user_group/global",
53 "value": "global"
54 },
55 "state": {
56 "display_value": "",
57 "value": ""
58 },
59 "fax": {
60 "display_value": "",
61 "value": ""
62 },
63 "identity_type": {
64 "display_value": "-",
65 "value": "unclassified"
66 },
67 "vip": {
68 "display_value": "false",
69 "value": "false"
70 },
71 "sys_created_by": {
72 "display_value": "admin",
73 "value": "admin"
74 },
75 "zip": {
76 "display_value": "",
77 "value": ""
78 },
79 "home_phone": {
80 "display_value": "",
81 "value": ""
82 },
83 "time_format": {
84 "display_value": null,
85 "value": ""
86 },
87 "last_login": {
88 "display_value": "2019-04-05",
89 "value": "2019-04-05"
90 },
91 "default_perspective": {
92 "display_value": "",
93 "value": ""
94 },
95 "active": {
96 "display_value": "true",
97 "value": "true"
98 },
99 "sys_domain_path": {
100 "display_value": "/",
101 "value": "/"
102 },
103 "transaction_log": {
104 "display_value": "",
105 "value": ""
106 },
107 "cost_center": {
108 "display_value": "",
109 "value": ""
110 },
111 "phone": {
112 "display_value": "",
113 "value": ""
114 },
115 "name": {
116 "display_value": "survey user",
117 "value": "survey user"
118 },
119 "employee_number": {
120 "display_value": "",
121 "value": ""
122 },
123 "password_needs_reset": {
124 "display_value": "false",
125 "value": "false"
126 },
127 "gender": {
128 "display_value": null,
129 "value": ""
130 },
131 "city": {
132 "display_value": "",
133 "value": ""
134 },
135 "hr_integration_source": {
136 "display_value": "",
137 "value": ""
138 },
139 "failed_attempts": {
140 "display_value": "0",
141 "value": "0"
142 },
143 "user_name": {
144 "display_value": "survey.user",
145 "value": "survey.user"
146 },
147 "roles": {
148 "display_value": "",
149 "value": ""
150 },
151 "manager_hp1": {
152 "display_value": "/T1}C(",
153 "value": "/T1}C("
154 },
155 "title": {
156 "display_value": "",
157 "value": ""
158 },
159 "sys_class_name": {
160 "display_value": "User",
161 "value": "sys_user"
162 },
163 "sys_id": {
164 "display_value": "005d500b536073005e0addeeff7b12f4",
165 "value": "005d500b536073005e0addeeff7b12f4"
166 },
167 "federated_id": {
168 "display_value": "UU/OJDA/H2viaQb8VqlJIYSYKwmbkOCLoFDQkTPv7XM=",
169 "value": "UU/OJDA/H2viaQb8VqlJIYSYKwmbkOCLoFDQkTPv7XM="
170 },
171 "internal_integration_user": {
172 "display_value": "false",
173 "value": "false"
174 },
175 "ldap_server": {
176 "display_value": "",
177 "value": ""
178 },
179 "mobile_phone": {
180 "display_value": "",
181 "value": ""
182 },
183 "street": {
184 "display_value": "",
185 "value": ""
186 },
187 "company": {
188 "display_value": "",
189 "value": ""
190 },
191 "department": {
192 "display_value": "",
193 "value": ""
194 },
195 "first_name": {
196 "display_value": "survey",
197 "value": "survey"
198 },
199 "email": {
200 "display_value": "survey.user@email.com",
201 "value": "survey.user@email.com"
202 },
203 "introduction": {
204 "display_value": null,
205 "value": ""
206 },
207 "preferred_language": {
208 "display_value": null,
209 "value": ""
210 },
211 "u_password_expiry_date": {
212 "display_value": "",
213 "value": ""
214 },
215 "manager": {
216 "display_value": "",
217 "value": ""
218 },
219 "locked_out": {
220 "display_value": "false",
221 "value": "false"
222 },
223 "sys_mod_count": {
224 "display_value": "2",
225 "value": "2"
226 },
227 "last_name": {
228 "display_value": "user",
229 "value": "user"
230 },
231 "photo": {
232 "display_value": "",
233 "value": ""
234 },
235 "avatar": {
236 "display_value": "",
237 "value": ""
238 },
239 "middle_name": {
240 "display_value": "",
241 "value": ""
242 },
243 "sys_tags": {
244 "display_value": "",
245 "value": ""
246 },
247 "time_zone": {
248 "display_value": null,
249 "value": ""
250 },
251 "schedule": {
252 "display_value": "",
253 "value": ""
254 },
255 "u_coe_specialization": {
256 "display_value": "",
257 "value": ""
258 },
259 "correlation_id": {
260 "display_value": "",
261 "value": ""
262 },
263 "date_format": {
264 "display_value": null,
265 "value": ""
266 },
267 "location": {
268 "display_value": "",
269 "value": ""
270 }
271 }
  1. Copy this JSON into the Input Payload for the DSL and Mapper Playground
  2. Click Process and see how the bender mapping is applied to the input payload!

Note: To ensure your Moveworks object representation is accurate, use this iterative process during the build phase. Always verify that fields are populating exactly as intended by following these steps:

  • Retrieve a sample record from your external system.
  • Configure the Bender to map your required fields.
  • Validate the mapping results within the DSL and Data Mapper tool.

1.3: Leverage Moveworks DSL Architect for Creating New Bender Mappings

Note: The Moveworks Agent Architect is an AI powered tool connected to Moveworks documentation. The DSL Architect can help you write DSL & Bender logic for all of your configurations!

  1. Navigate to the Moveworks Agent Architect and select the DSL Architect
  2. Provide the DSL Architect with the input mapping and what your stated goal is, leverage the following example

Based on the following input payload, please generate a complete, valid Bender mapping in both JSON and YAML formats. I am using this for a class, so please include a brief HTML introduction explaining the concepts covered. Specifically, I need the following 5 complex data transformation examples included in the mapping:

  1. String Templating (user_summary): Use RENDER() to create a sentence combining calendar_integration.display_value and sys_updated_on.display_value.
  2. Conditional Logic (security_risk_assessment): Use EVAL() with an IF/THEN/ELSE statement to return ‘High Risk’ if both enable_multifactor_authn.value and web_service_access_only.value are ‘false’, otherwise ‘Standard’.
  3. Risk Score (risk_score): Use the CONDITIONAL() operator to return 100 if enable_multifactor_authn.value is ‘false’, and 0 if it passes.
  4. Time Parsing (last_login_epoch): Use EVAL() and $PARSE_TIME() to convert last_login_time.value into an epoch timestamp.
  5. Data Fallbacks (location_info): Use COALESCE() to return the first available value among building.display_value, country.display_value, or a hardcoded string ‘“Unknown Location”’. Please ensure the JSON is fully closed and valid. Here is the payload:
1{
2 “calendar_integration”: { “display_value”: “Outlook”, “value”: “1” },
3 “country”: { “display_value”: null, “value”: “” },
4 “last_login_time”: { “display_value”: “2019-04-05 22:16:30”, “value”: “2019-04-05 22:16:30” },
5 “sys_updated_on”: { “display_value”: “2025-12-29 10:39:30”, “value”: “2025-12-29 10:39:30” },
6 “web_service_access_only”: { “display_value”: “false”, “value”: “false” }
7}
  1. This will allow Agent Architect to give us an initial JSON construction for us to use in our mapping. Once it has provided it, we will want to copy the Bender JSON
  2. And then we can paste it into the Editor section and see how how it would transform how our user is mapped within the Moveworks User Roster!

✅ 2: Verification & Next Steps

  1. Finalize:
    • Confirm that the JSON mapping processes successfully without errors.
  2. Inspect:
    • Analyze the functions and dot-walking logic used in the Editor section of the Data Mapping Playground. Review how these functions transform the input and why the resulting output appears the way it does.
  3. Experiment:
    • Modify the Input Payload and Editor values to see how the output changes. Try incorporating different logic or nested fields to test the limits of the mapping.
  4. Reference:

🪞 3: Reflecting on This Configuration

Through this guide, you’ve learned the following:

  • How to leverage the DSL & Data Mapper Playground
  • Where to pull existing configuration JSON values from to test how the configuration will be applied
    • API Playground (Resource)
    • User Identity or Ticketing Configuration (Bender Mapping)
  • Where to find additional examples of the Moveworks Bender language & associated functions
  • How to use the Moveworks Agent Architect to help write complex bender mappings

🧠 4: Knowledge Check: Lab #6b

1. Based on the ServiceNow JSON structure, why do we use .display_value in our Bender mappings instead of just the field name?

  • Answer: ServiceNow stores data in objects where value is often a raw ID (like a sys_id) or a code, while display_value contains the human-readable string. 2. In the following Bender snippet from your lab, what is the purpose of the OR ""? $TRIM(IF phone THEN phone.display_value OR "" ELSE NULL)
  • Answer: It provides a fallback to an empty string to prevent the mapping from failing or returning an error if the display_value property is missing. 3. Which Moveworks tool should you use if you want to test how a specific user’s raw ServiceNow data will look after being processed by your Bender configuration?
  • Answer: The DSL and Data Mapper Playground. 4. True or False: The RENDER() function is used for mathematical calculations like risk scores.
  • Answer: False. RENDER() is used for string templating to create human-readable sentences. (Bonus: CONDITIONAL() or EVAL() would be used for scores).

⚙️ 5: Configuration Details

Use the table below to fill in the required fields accurately.

Field NameAction / Value to Enter
Moveworks API KeyGenerated within Moveworks Setup → Waffle Menu (HTTP Connectors) → Credentials → Create
API Key → Credential Nameany_value
API Key → Credential TypeAPI Key
Data Mapper Playground → Input PayloadCopy the full JSON record from the code block above (Step 1.2)
Data Mapper Playground → EditorCopy the Bender mapping JSON from the code block above (Step 1.2)
Agent Architect QuerySee the example prompt in the code block in Step 1.3 above