> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tracecat.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Data transforms

## `core.transform.apply`

Apply a Python lambda function to a value.

### Inputs

<ParamField path="python_lambda" type="string" required>
  Python lambda function as a string (e.g. `"lambda x: x.get('name')"`).
</ParamField>

<ParamField path="value" type="any" required>
  Value to apply the lambda function to.
</ParamField>

### Examples

**Filter, transform, and compact**

```yaml theme={null}
- ref: normalize_hostname
  action: core.transform.apply
  args:
    value: ${{ TRIGGER.hostname }}
    python_lambda: "lambda value: value.strip().lower()"
- ref: keep_open_findings
  action: core.transform.filter
  args:
    items: ${{ TRIGGER.findings }}
    python_lambda: "lambda finding: finding.get('status') == 'open'"
- ref: finding_ids
  action: core.transform.map
  args:
    items: ${{ ACTIONS.keep_open_findings.result }}
    python_lambda: "lambda finding: finding.get('id')"
- ref: compact_ids
  action: core.transform.drop_nulls
  args:
    items: ${{ ACTIONS.finding_ids.result }}
```

## `core.transform.filter`

Filter a collection using a Python lambda function.

### Inputs

<ParamField path="items" type="array[any]" required>
  Items to filter.
</ParamField>

<ParamField path="python_lambda" type="string" required>
  Filter condition as a Python lambda expression (e.g. `"lambda x: x > 2"`).
</ParamField>

### Examples

**Filter, transform, and compact**

```yaml theme={null}
- ref: normalize_hostname
  action: core.transform.apply
  args:
    value: ${{ TRIGGER.hostname }}
    python_lambda: "lambda value: value.strip().lower()"
- ref: keep_open_findings
  action: core.transform.filter
  args:
    items: ${{ TRIGGER.findings }}
    python_lambda: "lambda finding: finding.get('status') == 'open'"
- ref: finding_ids
  action: core.transform.map
  args:
    items: ${{ ACTIONS.keep_open_findings.result }}
    python_lambda: "lambda finding: finding.get('id')"
- ref: compact_ids
  action: core.transform.drop_nulls
  args:
    items: ${{ ACTIONS.finding_ids.result }}
```

## `core.transform.map`

Map a Python lambda function to each item in a list.

### Inputs

<ParamField path="items" type="array[any]" required>
  Items to map the lambda function to.
</ParamField>

<ParamField path="python_lambda" type="string" required>
  Python lambda function as a string (e.g. `"lambda x: x.get('name')"`).
</ParamField>

### Examples

**Filter, transform, and compact**

```yaml theme={null}
- ref: normalize_hostname
  action: core.transform.apply
  args:
    value: ${{ TRIGGER.hostname }}
    python_lambda: "lambda value: value.strip().lower()"
- ref: keep_open_findings
  action: core.transform.filter
  args:
    items: ${{ TRIGGER.findings }}
    python_lambda: "lambda finding: finding.get('status') == 'open'"
- ref: finding_ids
  action: core.transform.map
  args:
    items: ${{ ACTIONS.keep_open_findings.result }}
    python_lambda: "lambda finding: finding.get('id')"
- ref: compact_ids
  action: core.transform.drop_nulls
  args:
    items: ${{ ACTIONS.finding_ids.result }}
```

## `core.transform.drop_nulls`

Remove null values from a list.

### Inputs

<ParamField path="items" type="array[any]" required>
  List of items to filter.
</ParamField>

### Examples

**Filter, transform, and compact**

```yaml theme={null}
- ref: normalize_hostname
  action: core.transform.apply
  args:
    value: ${{ TRIGGER.hostname }}
    python_lambda: "lambda value: value.strip().lower()"
- ref: keep_open_findings
  action: core.transform.filter
  args:
    items: ${{ TRIGGER.findings }}
    python_lambda: "lambda finding: finding.get('status') == 'open'"
- ref: finding_ids
  action: core.transform.map
  args:
    items: ${{ ACTIONS.keep_open_findings.result }}
    python_lambda: "lambda finding: finding.get('id')"
- ref: compact_ids
  action: core.transform.drop_nulls
  args:
    items: ${{ ACTIONS.finding_ids.result }}
```

## `core.transform.is_in`

Filters items in a list based on whether they are in a collection.

### Inputs

<ParamField path="collection" type="array[any]" required>
  Collection of hashable items to check against.
</ParamField>

<ParamField path="items" type="array[any]" required>
  Items to filter.
</ParamField>

<ParamField path="python_lambda" type="string | null">
  Python lambda applied to each item before checking membership (e.g. `"lambda x: x.get('name')"`). Similar to `key` in the Python `sorted` function.

  Default: `null`.
</ParamField>

### Examples

**Keep or exclude matching items**

```yaml theme={null}
- ref: keep_known_users
  action: core.transform.is_in
  args:
    items: ${{ TRIGGER.users }}
    collection:
      - alice@example.com
      - bob@example.com
    python_lambda: "lambda user: user.get('email')"
- ref: remove_known_users
  action: core.transform.not_in
  args:
    items: ${{ TRIGGER.users }}
    collection:
      - alice@example.com
      - bob@example.com
    python_lambda: "lambda user: user.get('email')"
```

## `core.transform.not_in`

Filters items in a list based on whether they are not in a collection.

### Inputs

<ParamField path="collection" type="array[any]" required>
  Collection of hashable items to check against.
</ParamField>

<ParamField path="items" type="array[any]" required>
  Items to filter.
</ParamField>

<ParamField path="python_lambda" type="string | null">
  Python lambda applied to each item before checking membership (e.g. `"lambda x: x.get('name')"`). Similar to `key` in the Python `sorted` function.

  Default: `null`.
</ParamField>

### Examples

**Keep or exclude matching items**

```yaml theme={null}
- ref: keep_known_users
  action: core.transform.is_in
  args:
    items: ${{ TRIGGER.users }}
    collection:
      - alice@example.com
      - bob@example.com
    python_lambda: "lambda user: user.get('email')"
- ref: remove_known_users
  action: core.transform.not_in
  args:
    items: ${{ TRIGGER.users }}
    collection:
      - alice@example.com
      - bob@example.com
    python_lambda: "lambda user: user.get('email')"
```

## `core.transform.deduplicate`

Deduplicate a JSON object or a list of JSON objects given a list of keys. Returns a list of deduplicated JSON objects.

### Inputs

<ParamField path="items" type="object | array[object]" required>
  JSON object or list of JSON objects to deduplicate.
</ParamField>

<ParamField path="keys" type="array[string]" required>
  List of JSONPath keys to deduplicate by. Supports dot notation for nested keys (e.g. `['user.id']`).
</ParamField>

<ParamField path="expire_seconds" type="integer">
  Time to live for the deduplicated items in seconds. Defaults to 1 hour.

  Default: `3600`.
</ParamField>

<ParamField path="persist" type="boolean">
  Whether to persist deduplicated items across calls. If True, deduplicates across calls. If False, deduplicates within the current call only.

  Default: `true`.
</ParamField>

### Examples

**Deduplicate by a stable key**

```yaml theme={null}
- ref: deduplicate_findings
  action: core.transform.deduplicate
  args:
    items: ${{ TRIGGER.findings }}
    keys:
      - finding_id
    persist: true
    expire_seconds: 3600
- ref: finding_seen
  action: core.transform.is_duplicate
  args:
    item: ${{ TRIGGER.finding }}
    keys:
      - finding_id
    expire_seconds: 3600
```

## `core.transform.is_duplicate`

Check if a JSON object was recently seen.

### Inputs

<ParamField path="item" type="object" required>
  JSON object to check.
</ParamField>

<ParamField path="keys" type="array[string]" required>
  List of JSONPath keys to check.
</ParamField>

<ParamField path="expire_seconds" type="integer">
  Time to live for the deduplicated items in seconds. Defaults to 1 hour.

  Default: `3600`.
</ParamField>

### Examples

**Deduplicate by a stable key**

```yaml theme={null}
- ref: deduplicate_findings
  action: core.transform.deduplicate
  args:
    items: ${{ TRIGGER.findings }}
    keys:
      - finding_id
    persist: true
    expire_seconds: 3600
- ref: finding_seen
  action: core.transform.is_duplicate
  args:
    item: ${{ TRIGGER.finding }}
    keys:
      - finding_id
    expire_seconds: 3600
```

## `core.transform.flatten_json`

Flatten a JSON object into a single level of fields.

### Inputs

<ParamField path="json" type="string | object" required>
  JSON object to flatten.
</ParamField>

### Examples

**Flatten and query JSON**

```yaml theme={null}
- ref: flatten_alert
  action: core.transform.flatten_json
  args:
    json: ${{ TRIGGER.alert }}
- ref: extract_fields
  action: core.transform.eval_jsonpaths
  args:
    json: ${{ TRIGGER.alert }}
    jsonpaths:
      user: $.actor.email
      source_ip: $.network.source.ip
```

## FAQ

<AccordionGroup>
  <Accordion title="Why does inline lambda not work inside expressions?">
    Expressions support Tracecat literals, operators, and `FN.*` helper functions.
    Python lambda functions are not part of the expression language, so they must be passed as strings to actions such as `core.transform.apply`, `core.transform.filter`, or `core.transform.map`.

    Use `FN.*` for short inline transforms, and use `core.transform.*` when you need Python-style per-item logic.

    ```yaml theme={null}
    - ref: extract_ids
      action: core.transform.map
      args:
        items: ${{ TRIGGER.findings }}
        python_lambda: "lambda finding: finding.get('id')"
    ```
  </Accordion>

  <Accordion title="How do I inspect the shape of API results before using map, filter, or for_each?">
    Add a small debug reshape step first.
    This lets you confirm whether you have a list or object, inspect a count, and verify the field path you plan to iterate over.

    ```yaml theme={null}
    - ref: inspect_findings
      action: core.transform.reshape
      args:
        value:
          result_type: ${{ FN.typeof(ACTIONS.fetch_findings.result) }}
          findings_type: ${{ FN.typeof(ACTIONS.fetch_findings.result.findings) if ACTIONS.fetch_findings.result != None else None }}
          findings_count: ${{ FN.length(ACTIONS.fetch_findings.result.findings) if ACTIONS.fetch_findings.result != None else 0 }}
    ```
  </Accordion>
</AccordionGroup>

## `core.transform.eval_jsonpaths`

Eval multiple JSONPath expressions on an object.

### Inputs

<ParamField path="json" type="string | object" required>
  JSON object to eval JSONPath expressions on.
</ParamField>

<ParamField path="jsonpaths" type="array[string]" required>
  JSONPath expressions to eval.
</ParamField>

### Examples

**Flatten and query JSON**

```yaml theme={null}
- ref: flatten_alert
  action: core.transform.flatten_json
  args:
    json: ${{ TRIGGER.alert }}
- ref: extract_fields
  action: core.transform.eval_jsonpaths
  args:
    json: ${{ TRIGGER.alert }}
    jsonpaths:
      user: $.actor.email
      source_ip: $.network.source.ip
```
