Common DSL & Mapper Patterns

Recipes for the patterns that come up most in real Agent Studio builds.
View as Markdown

Practical recipes for DSL and Data Mapper patterns that come up constantly in real builds. Each pattern includes a use case and a working example you can adapt.

For the full function references, see the DSL Reference and the Data Mapper Reference.

Data Mapper Patterns

Omit null or empty keys from a payload

APIs often reject requests with null fields. Use EVAL() with $FILTER to build an object and strip out any keys that are null or empty before sending.

Use case: Building a calendar event payload where recurrence and location are optional.

1event_payload:
2 EVAL():
3 args:
4 obj:
5 attendees: data.attendees
6 description: data.event_description
7 end: data.end_time
8 location: data.location_display_name
9 recurrence: data.recurrence
10 start: data.start_time
11 summary: data.title
12 expression: obj.$FILTER(val => val)

If location and recurrence are null, they disappear from the payload entirely.

Transform a list of objects

Use MAP() to reshape each item in an array — rename fields, flatten nested values, or add computed fields.

Use case: Rename API field names to user-friendly names for SDA.

1accounts:
2 MAP():
3 items: data.raw_accounts
4 converter:
5 account_name: item.Name
6 annual_revenue: item.AnnualRevenue__c
7 owner_email: item.Owner.Email
8 stage: item.StageName

Conditionally include a field

Use CONDITIONAL() to include a field only when a condition is met — useful for building dynamic query filters.

Use case: Only add a date filter to a SOQL query when the user provided a date.

1query_parts:
2 date_filter:
3 CONDITIONAL():
4 condition: data.close_date_before != "ANY"
5 on_pass: $CONCAT(["CloseDate >= ", data.close_date_before])
6 on_fail: '""'

Merge data from multiple actions

Use MERGE() to combine objects from different action outputs into a single result — useful for compound actions that fetch from multiple sources.

Use case: Combine user data from one action with company data from another.

1return:
2 output_mapper:
3 enriched_users:
4 MAP():
5 items: data.user_list
6 converter:
7 MERGE():
8 - item
9 - company: data.company_info.name
10 office: data.company_info.hq_address

Map a code to a human-readable label

Use LOOKUP() when an API returns status codes or category IDs that need to be translated for the user.

Use case: Convert ServiceNow incident state numbers to labels.

1status_label:
2 LOOKUP():
3 key: data.incident.state
4 mapping:
5 '1': "'New'"
6 '2': "'In Progress'"
7 '3': "'On Hold'"
8 '6': "'Resolved'"
9 '7': "'Closed'"
10 default: "'Unknown'"

Build a dynamic URL

Use RENDER() or $CONCAT to construct URLs from dynamic data — for linking back to source records.

Use case: Generate a clickable link to a ServiceNow ticket.

1ticket_url:
2 RENDER():
3 template: "https://{{instance}}.service-now.com/nav_to.do?uri=incident.do?sys_id={{sys_id}}"
4 args:
5 instance: data.snow_instance
6 sys_id: data.incident.sys_id

Sort results

Use SORT() to order an array by a field.

1sorted_tickets:
2 SORT():
3 items: data.tickets
4 key: item.created_at

Add reverse: 'true' for descending order.

DSL Patterns

Validate a date is in the future

Use in a slot validation policy to reject past dates.

$PARSE_TIME(value) >= $TIME()

Validate a date is within a range

Check that a date is at least 7 days from now (e.g., for travel booking lead time).

$PARSE_TIME(value) >= $TIME().$ADD_DATE(0, 0, 7)

Check if a value is in a list

Use in for allowlist/blocklist validation.

value in ["US", "CA", "UK", "DE"]

Or with $ANY for more complex matching:

["urgent", "critical", "blocker"].$ANY(level => data.priority.$LOWERCASE() == level)

String formatting and transformation

Chain string functions for normalization:

data.user_input.$TRIM().$LOWERCASE()

Build a full name from parts:

$CONCAT([data.first_name, " ", data.last_name])

Serialize an object to a string

When passing structured data to mw.generate_text_action (which only accepts string input), use $STRINGIFY_JSON:

$STRINGIFY_JSON(data.payload)

Count items matching a condition

data.tickets.$FILTER(t => t.status == "open").$LENGTH()

Conditional logic in compound action switch expressions

Use comparison operators in switch conditions:

1- switch:
2 cases:
3 - condition: data.amount < 500
4 steps:
5 - action:
6 action_name: auto_approve
7 output_key: approval_result
8 - condition: data.amount >= 500 AND data.amount < 5000
9 steps:
10 - action:
11 action_name: manager_approval
12 output_key: approval_result
13 default:
14 steps:
15 - action:
16 action_name: vp_approval
17 output_key: approval_result

Combined Patterns

These patterns use both DSL expressions inside Data Mappers.

Dynamic message with data from multiple sources

Use RENDER() (mapper) with DSL expressions in the args:

1message:
2 RENDER():
3 template: |
4 Hi {{name}}, your {{request_type}} request ({{id}}) has been {{status}}.
5 {{comment}}
6 args:
7 name: data.user.first_name
8 request_type: data.request.type.$TITLECASE()
9 id: data.request.display_id
10 status: data.approval_result.status.$LOWERCASE()
11 comment:
12 CONDITIONAL():
13 condition: data.approval_result.denied_by[0].comment != null
14 on_pass: $CONCAT(["Reason: ", data.approval_result.denied_by[0].comment])
15 on_fail: '""'

Filter and transform in one step

Use DSL $FILTER inside a mapper MAP():

1active_high_value_accounts:
2 MAP():
3 items: data.accounts.$FILTER(a => a.status == "Active" AND a.annual_revenue > 100000)
4 converter:
5 name: item.name
6 revenue: item.annual_revenue
7 owner: item.owner_email