> This page is for API Reference & Tools.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.moveworks.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.moveworks.com/_mcp/server.

# Common DSL & Mapper Patterns

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.

> **Info**
>
> For the full function references, see the [DSL Reference](/api-reference/configuration-languages/moveworks-dsl-reference) and the [Data Mapper Reference](/api-reference/configuration-languages/moveworks-bender-language-reference).

# String Constants in YAML (Why Triple Quotes?)

Before diving into patterns, you need to understand how constants work in compound action YAML. This is the single most confusing syntax element for new builders.

In compound actions and data mappers, **bare values are treated as data bank references**, not strings. If you write:

```yaml
input_args:
  status: approved
```

The system looks for `data.approved` in the data bank — it does NOT pass the string `"approved"`. To pass a literal constant, you need quotes.

### The quoting rules

| What you want                   | Syntax                 | Example                            |
| ------------------------------- | ---------------------- | ---------------------------------- |
| String constant                 | `'''text'''`           | `status: '''approved'''`           |
| String with spaces              | `'''some text here'''` | `message: '''Request submitted'''` |
| Integer constant                | `'2'`                  | `count: '2'`                       |
| Boolean constant                | `'true'`               | `enabled: 'true'`                  |
| String that looks like a number | `'"42"'`               | `code: '"42"'`                     |

### Why triple quotes?

This is standard YAML quoting interacting with DSL evaluation:

* **Outer single quotes** (`'...'`) tell YAML to treat the content as a literal string (no YAML processing)
* **Inner single quotes** (`'...'`) are the DSL string delimiter
* So `'''approved'''` is YAML for: the DSL expression `'approved'`, which evaluates to the string constant `approved`

### When do you NOT need quotes?

When referencing the data bank — that's the default behavior:

```yaml
input_args:
  user_email: data.user.email_addr          # data bank reference (no quotes)
  status: '''approved'''                     # string constant (triple quotes)
  count: '5'                                 # integer constant (single quotes)
  message: data.generated_output             # data bank reference (no quotes)
```

# 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.

```yaml
event_payload:
  EVAL():
    args:
      obj:
        attendees: data.attendees
        description: data.event_description
        end: data.end_time
        location: data.location_display_name
        recurrence: data.recurrence
        start: data.start_time
        summary: data.title
    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.

```yaml
accounts:
  MAP():
    items: data.raw_accounts
    converter:
      account_name: item.Name
      annual_revenue: item.AnnualRevenue__c
      owner_email: item.Owner.Email
      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.

```yaml
query_parts:
  date_filter:
    CONDITIONAL():
      condition: data.close_date_before != "ANY"
      on_pass: $CONCAT(["CloseDate >= ", data.close_date_before])
      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.

```yaml
return:
  output_mapper:
    enriched_users:
      MAP():
        items: data.user_list
        converter:
          MERGE():
            - item
            - company: data.company_info.name
              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.

```yaml
status_label:
  LOOKUP():
    key: data.incident.state
    mapping:
      '1': "'New'"
      '2': "'In Progress'"
      '3': "'On Hold'"
      '6': "'Resolved'"
      '7': "'Closed'"
    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.

```yaml
ticket_url:
  RENDER():
    template: "https://{{instance}}.service-now.com/nav_to.do?uri=incident.do?sys_id={{sys_id}}"
    args:
      instance: data.snow_instance
      sys_id: data.incident.sys_id
```

## Sort results

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

```yaml
sorted_tickets:
  SORT():
    items: data.tickets
    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:

```yaml
- switch:
    cases:
      - condition: data.amount < 500
        steps:
          - action:
              action_name: auto_approve
              output_key: approval_result
      - condition: data.amount >= 500 AND data.amount < 5000
        steps:
          - action:
              action_name: manager_approval
              output_key: approval_result
    default:
      steps:
        - action:
            action_name: vp_approval
            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:

```yaml
message:
  RENDER():
    template: |
      Hi {{name}}, your {{request_type}} request ({{id}}) has been {{status}}.
      {{comment}}
    args:
      name: data.user.first_name
      request_type: data.request.type.$TITLECASE()
      id: data.request.display_id
      status: data.approval_result.status.$LOWERCASE()
      comment:
        CONDITIONAL():
          condition: data.approval_result.denied_by[0].comment != null
          on_pass: $CONCAT(["Reason: ", data.approval_result.denied_by[0].comment])
          on_fail: '""'
```

## Filter and transform in one step

Use DSL `$FILTER` inside a mapper `MAP()`:

```yaml
active_high_value_accounts:
  MAP():
    items: data.accounts.$FILTER(a => a.status == "Active" AND a.annual_revenue > 100000)
    converter:
      name: item.name
      revenue: item.annual_revenue
      owner: item.owner_email
```