# Data Transformations

## Running data transformations

Laminar's [Lam Language](broken://pages/HKxG0mHw1Qa5maH9rRwF) is built entirely on top of [JQ](https://jqlang.github.io/jq/). Lam is run on each flow execution and is a JSON data transformation language that can support extremely complex conditional data transformations.

Data transformations access keys via `dot` notation and are able to reference data retrieved or transformed by previous flows in a workflow using the [Global Workflow Object](/building-an-integration/advanced/workflows/global-workflow-object.md).

<details>

<summary>View a simple JQ example</summary>

{% code lineNumbers="true" %}

```jq
# Remove some fields
.data | map(select(.value > 5))
```

{% endcode %}

</details>

<details>

<summary>View a complex JQ example</summary>

{% code overflow="wrap" lineNumbers="true" %}

```jq
# Define safe_notes with robust checking for nested structures
def safe_notes:
  if .step_1.response.notes then
    [.step_1.response.notes[] | select(.custom_types != null and any(.custom_types[]; .note_custom_value == "0 = \"DELIVERY_SUCCESSFUL\""))]
  else
    []
  end;

# Extract image URLs from notes that have them
def images:
  if .step_1.response.notes then
    [.step_1.response.notes[] | 
     select(.upload_url? and .upload_url != "") | 
     {url: .upload_url, extension: .upload_extension}]
  else
    []
  end;

# Evaluate the detailed_event and contents of notes
if (.input.detailed_event == "completed" and (safe_notes | length > 0)) then
  # Output for confirmed completed status with valid delivery success note and image URLs
  {
    "lam.resolveThenExecute": {
      "lam.workflowId": 55,
      "lam.payload": {
        "images": (images),
        "data": {
            "TrackingNumber": .input.address_custom_data.barcode,
            "ScanLatitude": .step_1.response.visited_lat,
            "ScanLongitude": .step_1.response.visited_lng,
        }
      },
      "lam.result": {}
    }
  }
elif (.input.detailed_event == "completed" and (safe_notes | length == 0)) or .input.detailed_event == "skipped" or .input.detailed_event == "failed" then
  # Output for delivery issues or no valid delivery success note found
  {
    "WS_Key": "..",
    "WSKeyLevel": "Carrier",
    "CarrierId": 10204,
    "TrackingNumber": .input.address_custom_data.barcode,
    "StartDate": (now | strflocaltime("%Y-%m-%dT%H:%M:%S")),
    "EndDate": (now + 86400 | strflocaltime("%Y-%m-%dT%H:%M:%S")),
    "TrackingInfo": {
      "EstimatedDeliveryDate": (now + 86400 | strflocaltime("%Y-%m-%dT%H:%M:%S")),
      "DeliveryDate": (now + 86400 | strflocaltime("%Y-%m-%dT%H:%M:%S")),
      "Event": {
        "ScanDateTime": (now | strflocaltime("%Y-%m-%dT%H:%M:%S")),
        "Status": "DeliveryException",
        "Description": "Exception occurred during delivery",
        "AlternateLanguageCode": "English",
        "ScanCountry": "CA",
        "ScanState": "ON",
        "ScanCity": "...",
        "ScanPostal": "..."
      },
      "PerformanceState": 0
    },
    "UpdateTrackingInfoShipmentLevel": true
  }
elif .input.detailed_event == "loaded" then
  # Output for 'loaded' status
  {
    "WS_Key": "...",
    "WSKeyLevel": "Carrier",
    "CarrierId": 10204,
    "TrackingNumber": .input.address_custom_data.barcode,
    "StartDate": (now | strflocaltime("%Y-%m-%dT%H:%M:%S")),
    "EndDate": (now + 86400 | strflocaltime("%Y-%m-%dT%H:%M:%S")),
    "TrackingInfo": {
      "EstimatedDeliveryDate": (now + 86400 | strflocaltime("%Y-%m-%dT%H:%M:%S")),
      "DeliveryDate": (now + 86400 | strflocaltime("%Y-%m-%dT%H:%M:%S")),
      "Event": {
        "ScanDateTime": (now | strflocaltime("%Y-%m-%dT%H:%M:%S")),
        "Status": "OutForDelivery",
        "Description": "Package is out for delivery",
        "AlternateLanguageCode": "English",
        "ScanCountry": "CA",
        "ScanState": "ON",
        "ScanCity": "...",
        "ScanPostal": "..."
      },
      "PerformanceState": 0
    },
    "UpdateTrackingInfoShipmentLevel": true
  }
else
  { "lam.exit": true }
end
```

{% endcode %}

</details>

## Invoking Laminar Keywords

Having a data transformation output one of the specified [Laminar Keywords](/building-an-integration/keywords.md) allows for more complex control flow logic to take place.

This is done by formatting the output in a way that is recognizable to the Laminar system as specified by the [Laminar Keywords](/building-an-integration/keywords.md).

```jq
.input |
if ((.activity_type == "route-destination-status" or
     .activity_type == "update-destinations") and
    (.detailed_event? | tostring | test("skipped|failed|completed|loaded")) and
    (.order_id | length > 0))
then
{
  "lam.resolveThenExecute": {
    "lam.workflowId": 60,
    "lam.payload": .,
    "lam.result": { "status": "OK" }
  }
}
else 
    { "lam.exit": true } 
end
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.laminar.run/building-an-integration/integration-design/data-transformations.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
