---
title: "Filter rules"
canonical: "https://ingrid-support.refined.site/space/KB/11534881/Filter%20rules"
format: markdown
---
## What is it?

**Filter rules** control which delivery options are shown or hidden in checkout based on conditions like cart weight, item dimensions, order value, product attributes, or postal code. They are applied **before contacting carriers**, which means ineligible options are removed early — resulting in a faster, cleaner checkout.

Each rule is a conditional expression that evaluates to **true** (show the option) or **false** (hide it).

Filter rules can be applied at two levels:

- **Delivery category** — show or hide an entire category (e.g. "Pick-up points")
- **Carrier product** — show or hide a specific carrier within a category

> **Tip:** Filter rules run before carrier API calls. For rules that depend on which carriers actually responded, use **Post Filter Rules** instead.

---

## Key benefits

- **Faster checkout** — fewer carrier API calls means quicker page loads
- **Cleaner delivery selection** — shoppers only see options that are valid for their order
- **Flexible logic** — combine weight, size, value, attributes, postal codes, and time conditions
- **Reduce failed deliveries** — prevent shoppers from selecting options their order doesn't qualify for

---

## How it works

1. Shopper reaches checkout
2. Ingrid evaluates filter rules against the cart contents and delivery address
3. Delivery options that don't meet the conditions are removed
4. Only remaining options are sent to carriers for availability checks
5. Shoppers see a clean, relevant set of delivery choices

---

## Rule Syntax

Filter rules use an expression language entered as free text in the MAD UI. Rules combine functions, operators, and values to create conditions.

### Unit Conventions

All values in filter rules follow Ingrid's standard units:

| Measurement | Unit | Example |
| --- | --- | --- |
| Weight | Grams (g) | 7000 = 7 kg |
| Dimensions | Millimetres (mm) | 600 = 60 cm |
| Price / Value | Minor currency units (cents/öre) | 22500 = 225.00 SEK |
| Volume | Cubic millimetres (mm³) | 4500 mm³ |

### Operators

`&&` **— AND (all conditions must be met)**

```
total_weight <= 30000 && all_cartitems_dims_sum_below(2500) && max_item_length < 1500
```

`||` **— OR (at least one condition must be met)**

```
in_range(postal_code(), 55302, 56636) || in_range(postal_code(), 58128, 59077)
```

**Operators can be mixed:**

```
not(contains(cart_items_attributes(), 'Example')) && in_range(postal_code(), 13025, 13025) || in_range(postal_code(), 13033, 13034)
```

### Negation

Two ways to negate a condition:

`not()` **— wraps the condition:**

```
not(in_range(postal_code(), 11120, 12010))
```

`!` **— prefix before the condition:**

```
!all_cartitems_below_weight(68000)
```

Negation can create range logic:

```
all_cartitems_below_weight(68000) && !all_cartitems_below_weight(18000)
```

This means: show the option if items are between 18 kg and 68 kg.

### Pattern Matching

```
matches_any_pattern(base, pattern1, pattern2, ..., patternN)
```

Uses Glob syntax:

- — matches any sequence of characters
- `?` — matches any single character
- `[123aB]` — matches one character in brackets
- `[0-6]` — matches a character from range in brackets

Example:

```
matches_any_pattern(postal_code(), '1[0,1]*', '1[2-5]*', '1[6-9]*')
```

---

## How to set it up

1. In the **Ingrid Merchant Platform**, navigate to `Delivery Checkout → Regions → [Select Region]`
2. Select a **Delivery Category** or **Carrier Product**
3. Enter the rule expression in the **Filter rules** text field
4. Save your configuration
5. **Test in staging first** — use the Checkout Playground to verify rules with different addresses and cart configurations before deploying to production

> **Important:** Filter rule syntax is sensitive to typos. Always test before going live. Contact [support@ingrid.com](mailto:support@ingrid.com) if you need help.

---

## Available Functions Reference

### Value, Weight & Quantity

| Function | Description | Example |
| --- | --- | --- |
| `total_value` | Total cart value in minor units | `total_value >= 22500` (≥ 225.00) |
| `total_weight` | Total weight of all cart items in grams | `total_weight <= 7000` (≤ 7 kg) |
| `all_cartitems_below_weight(X)` | Every item weighs below X grams | `all_cartitems_below_weight(15000)` |
| `total_quantity` | Total number of items in cart | `total_quantity > 3` |

### Dimensions

> **Dimension order:** `all_cartitems_below_dims` and `all_cartitems_above_dims` take their arguments in the order **height, length, width (H, L, W)** — e.g. `all_cartitems_above_dims(1, 3, 5)` means height 1, length 3, width 5. (The other dimension functions below use the order shown in their signature.)

| Function | Description | Example |
| --- | --- | --- |
| `every_item_fits_size(L, W, H)` | All items fit within dimensions (mm) | `every_item_fits_size(600, 600, 500)` |
| `all_cartitems_dims_fits_size(L, W, H)` | All items fit separately; two highest dimensions compared with max, third dimension sum below max | `all_cartitems_dims_fits_size(400, 200, 300)` |
| `all_cartitems_3d_packed_fits_size(L, W, H)` | 3D packed size check — all items packed together fit | `all_cartitems_3d_packed_fits_size(340, 290, 120)` |
| `all_cartitems_sum_of_two_largest_dims_below(X)` | Sum of two largest dimensions below X (mm) | `all_cartitems_sum_of_two_largest_dims_below(800)` |
| `total_volume` | Sum of each item's volume (mm³) | `total_volume < 4500` |
| `all_cartitems_below_dims(H, L, W)` | Each item has dimensions below values. **Argument order is height, length, width** | `all_cartitems_below_dims(1500, 1500, 1500)` |
| `all_cartitems_above_dims(H, L, W)` | Each item has dimensions above values. **Argument order is height, length, width** | `all_cartitems_above_dims(100, 200, 300)` |
| `all_cartitems_dims_sum_below(X)` | Sum of each item's dimensions below X (mm) | `all_cartitems_dims_sum_below(3000)` |
| `all_cartitems_has_dims()` | Checks if all items have dimensions present | `all_cartitems_has_dims()` |
| `not(all_cartitems_has_dims())` | True when items are missing dimensions | `not(all_cartitems_has_dims())` |
| `total_items_height` | Sum of all items' heights, considering quantity | `total_items_height > 4000` |
| `all_cartitems_shortest_dim_sum_below(X)` | Sum of shortest dimension of each item below X | `all_cartitems_shortest_dim_sum_below(1890)` |
| `max_item_length` | Longest length for any single item | `max_item_length < 1000` |
| `max_item_long_edge` | Longest side of any cart item | `max_item_long_edge < 1200` |
| `max_item_circumference` | Max circumference: length + (2 × (width + height)) | `max_item_circumference <= 3000` |
| `all_cartitems_largest_surface_area_below(X)` | Largest item surface area below X (mm²) | `all_cartitems_largest_surface_area_below(1200000)` |

### Cart & Item Attributes

| Function | Description | Example |
| --- | --- | --- |
| `all_cartitems_have_attr('X')` | All items have the specified attribute | `all_cartitems_have_attr('sendasletter')` |
| `not(all_cartitems_have_attr('X'))` | Not all items have the attribute | `not(all_cartitems_have_attr('presentkort'))` |
| `contains(cart_items_attributes(), 'X')` | At least one item has the attribute | `contains(cart_items_attributes(), 'bulky')` |
| `not(contains_any(cart_items_attributes(), ('X', 'Y')))` | No items have any of the listed attributes | `not(contains_any(cart_items_attributes(), ('shipping_bulky_weight', 'frozen_items')))` |
| `contains(cart_attributes(), 'X')` | Cart-level attribute is present | `contains(cart_attributes(), 'Chairs')` |
| `cart_and_cart_items_attributes()` | Returns both cart and item attributes | Used in combination with `contains()` |
| `any_cart_item_has_substr_in_name("X")` | Any item name contains substring | `any_cart_item_has_substr_in_name("examp")` |
| `any_cart_item_has_sku("X")` | Any item matches SKU | `any_cart_item_has_sku("1234567")` |
| `any_cart_item_has_token_in_name('X', 'Y')` | Any item name matches one of the tokens | `not(any_cart_item_has_token_in_name('SKL 10', 'Cat Litter 10 kg'))` |
| `at_least_n_cartitems_have_attr(N, 'X')` | At least N items have the attribute | `at_least_n_cartitems_have_attr(1, 'bulky')` |

### Postal Codes

| Function | Description | Example |
| --- | --- | --- |
| `in_range(postal_code(), X, Y)` | Postal code within numeric range | `in_range(postal_code(), 00000, 21110)` |
| `not(in_range(postal_code(), X, Y))` | Postal code NOT in range | `not(in_range(postal_code(), 11120, 11124))` |
| `matches_any_pattern(postal_code(), 'P1', 'P2')` | Postal code matches glob patterns | `matches_any_pattern(postal_code(), '1234*', 'PH123*')` |
| `not(matches_any_pattern(postal_code(), 'P1'))` | Postal code does NOT match pattern | `not(matches_any_pattern(postal_code(), '350[0,1,7,8]*'))` |
| `contains(array('X', 'Y'), postal_code())` | Postal code is one of the listed values | `contains(array('11121', '35004'), postal_code())` |

### Date & Time

| Function | Description | Example |
| --- | --- | --- |
| `is_one_of_weekdays(now_time(), TZ, 'day')` | Current time is on specified weekday | `is_one_of_weekdays(now_time(), 'Europe/Stockholm', 'wed')` |
| `between_hours('HH:MM', 'HH:MM', TZ, 'day1', 'day2')` | Current time is within hours and weekday range | `between_hours('17:00', '20:00', 'Europe/Stockholm', 'wed', 'fri')` |
| `time_now_shipping_date_time_start_diff_in_days` | Days between now and shipping date start | `time_now_shipping_date_time_start_diff_in_days <= 4` |
| `shipping_date_end_start_diff_in_days` | Days between shipping date start and end | `shipping_date_end_start_diff_in_days < 2` |

### Vouchers, Discounts & Stock

| Function | Description | Example |
| --- | --- | --- |
| `contains(cart_vouchers, 'X')` | Cart has specified voucher code | `contains(cart_vouchers, 'HEAVY')` |
| `total_discount` | Total discount value in minor units | `total_discount >= 2000` |
| `out_of_stock` | Item stock status flag | `out_of_stock == false` |
| `pre_order` | Pre-order flag | `pre_order == true` |

### Group Attributes (Checkout V2 only)

Group attributes are treated as cart attributes. For example:

```
contains(cart_attributes(), 'free')
```

This evaluates whether the group attributes are present in the cart attributes for that specific group.

---

## Use Cases & Real Examples

### Use case 1: Size and weight limits for a home delivery carrier

**Scenario:** A home delivery carrier (e.g. Budbee) only accepts parcels within specific size and weight limits. Items that are too heavy, too long, or flagged as bulky/frozen should not be offered this delivery method.

**Rule:**

```
max_item_circumference <= 2900 && max_item_long_edge < 1200 && total_weight < 59000 && all_cartitems_below_weight(19600) && not(contains_any(cart_items_attributes(), ('shipping_bulky_weight', 'frozen_items'))) && not(all_cartitems_have_attr('presentkort'))
```

**How it reads:** Show this carrier product only if:

- No item's circumference exceeds 2900 mm
- No item's longest edge exceeds 1200 mm
- Total weight is under 59 kg
- Every individual item is under 19.6 kg
- No items are flagged as bulky or frozen
- Not all items are gift cards

### Use case 2: Pickup point carrier with weight, size and product exclusions

**Scenario:** A pickup point service has strict size limits (fits in a box 340×290×120 mm) and weight limits. Certain product categories (frozen items) must be excluded.

**Rule:**

```
not(all_cartitems_has_dims()) && total_weight <= 1600 && not(contains_any(cart_items_attributes(), ('shipping_bulky_weight', 'frozen_items')))
||
all_cartitems_has_dims() && all_cartitems_3d_packed_fits_size(340, 290, 120) && total_weight <= 19000 && not(contains_any(cart_items_attributes(), ('shipping_bulky_weight', 'shipping_frozen_items')))
```

**How it reads:** Two branches (OR):

- If items DON'T have dimensions: allow if total weight ≤ 1.6 kg and no bulky/frozen items
- If items DO have dimensions: allow if they fit packed in the box, total weight ≤ 19 kg, and no bulky/frozen items

### Use case 3: Mailbox delivery with value and weight cap

**Scenario:** Mailbox delivery (e.g. PostNord Varubrev) is only suitable for small, lightweight, low-value orders that don't contain gift cards.

**Rule:**

```
all_cartitems_below_weight(20001) && total_weight < 120000 && not(contains_any(cart_items_attributes(), ('shipping_bulky_weight', 'frozen_items'))) && not(all_cartitems_have_attr('presentkort'))
```

### Use case 4: Exclude specific postal codes

**Scenario:** A carrier doesn't serve certain remote areas. The retailer wants to hide this delivery option for those postal codes.

**Rule:**

```
contains_any(cart_items_attributes(), ('frozen_items')) && not(in_range(postal_code(), 63000, 63998)) && not(in_range(postal_code(), 85229, 99999))
```

**How it reads:** Show this option only for frozen items AND only if the postal code is NOT in the excluded ranges.

### Use case 5: Gift card — only digital delivery

**Scenario:** When all items in the cart are gift cards, only a digital delivery option (CSO) should appear — no physical shipping.

**Rule:**

```
all_cartitems_have_attr('presentkort')
```

**How it reads:** Show this delivery method only if every item in the cart has the "presentkort" (gift card) attribute.

### Use case 6: Freight service for oversized items

**Scenario:** A freight carrier (e.g. DHL Freight) is only needed for very heavy or oversized items that standard carriers can't handle.

**Rule:**

```
all_cartitems_below_weight(19001) && total_weight < 120000 && not(contains_any(cart_items_attributes(), ('shipping_bulky_weight', 'frozen_items'))) && not(all_cartitems_have_attr('presentkort'))
```

Combined with post filter rules on the standard carriers, this creates a fallback pattern: freight only appears when standard options are filtered out.

---

## Frequently Asked Questions

**Q: Can I apply filter rules to both delivery categories and carrier products?**    
A: Yes. Category-level rules control whether the entire category is shown. Carrier product-level rules control individual options within a category. If both are set, both must evaluate to true.

**Q: What happens if I make a syntax error?**    
A: The rule may not work as expected — the delivery option might always show or always be hidden. Always test in staging first and contact [support@ingrid.com](mailto:support@ingrid.com) if you're unsure.

**Q: Can I combine multiple conditions?**    
A: Yes. Use `&&` (AND) to require all conditions, `||` (OR) to require at least one, and `not()` to negate. You can mix operators in a single rule.

**Q: What if my items don't have dimensions set?**    
A: Dimension-based functions may behave unexpectedly. Use `all_cartitems_has_dims()` to check first, and create separate rule branches for items with and without dimensions.

**Q: Can filter rules change delivery prices?**    
A: No. Filter rules only show or hide options. For pricing logic, use **Price Rules**.

**Q: Do filter rules work in both Checkout V1 and V2?**    
A: Yes, filter rules work in both versions. However, group attributes (treated as cart attributes) are only supported in Checkout V2.

---

## Related documentation

- **[Checkout Rules Overview](https://ingrid-ab.atlassian.net/wiki/spaces/KB/pages/505610242)** — how all rule types work together
- **[Post Filter Rules](https://ingrid-ab.atlassian.net/wiki/spaces/KB/pages/505020420)** — post-carrier refinement based on method availability
- **[Price Rules](https://ingrid-ab.atlassian.net/wiki/spaces/KB/pages/504987652)** — dynamic delivery pricing
- **[Checkout Playground](https://ingrid-ab.atlassian.net/wiki/spaces/KB/pages/14647402)** — testing rule configurations before going live