# rules.json

The rules in precedence order.

## `rules`

array of objects

The rules in precedence order: the first rule is the highest.

- `id` (string · required): A rule's id, unique per release, such as `home/out-of-stock-last`. It appears in traces, tests, diffs and filter chips, so it is visible to storefronts and holds no secrets.

- `description` (string · required): One line, up to 200 characters, shown in the panel, the trace and diffs.

- `enabled` (boolean): A disabled rule is not evaluated. Default: `true`.

- `when` (object): The condition over the request. Without one, the rule always matches.
  
  A predicate over the request (`when`). Without a condition, a rule always matches.
  
  `{ "on": "search", "query": { "contains": ["bremsen", "bremse"] } }`
  
  - `query` (object): The query as typed, after normalization. Matching is literal: no typos, synonyms or plurals.
    
    How the query must look. A list of phrases means any of them.
    
    - `is` (string or array of strings): One value, or a list meaning any of them.
    
    - `contains` (string or array of strings): One value, or a list meaning any of them.
    
    - `starts_with` (string or array of strings): One value, or a list meaning any of them.
    
    - `empty` (boolean): True: nothing was typed. False: something was.
  
  - `on` (string or array of strings): The surface the request comes from.
    
    - `search`: The search page's results.
    
    - `category_page`: A category's listing.
    
    - `autocomplete`: A search box's suggestions while the shopper types.
  
  - `category` (object): The category page being browsed.
    
    A category, exactly or including everything below it.
    
    - `is` (string or array of strings): One value, or a list meaning any of them.
    
    - `within` (string or array of strings): One value, or a list meaning any of them.
  
  - `resolved` (object): What the query named. Rules that resolve or rewrite run before resolution and cannot read it.
    
    What the query named: its category, its detected filters, and whether any text is left.
    
    - `category` (object): A category, exactly or including everything below it.
      
      - `is` (string or array of strings): One value, or a list meaning any of them.
      
      - `within` (string or array of strings): One value, or a list meaning any of them.
    
    - `filters` (object): The detected filters, as a selector over the detected values.
      
      Picks entities (`where`). Attribute codes and the built-in fields `brand`, `price`, `on_sale`, `availability` and `delivery_days` are keys of the same object; `category` takes `is` or `within`.
      
      `{ "any": [{ "brand": "bosch" }, { "quality": "oe" }], "not": { "availability": "out_of_stock" } }`
      
      - `category` (object): The entity is in this category, or below it with `within`.
        
        A category, exactly or including everything below it.
        
        - `is` (string or array of strings): One value, or a list meaning any of them.
        
        - `within` (string or array of strings): One value, or a list meaning any of them.
      
      - `any` ([array of objects](#when.resolved.filters) · at least 1 item): At least one of these holds.
      
      - `all` ([array of objects](#when.resolved.filters) · at least 1 item): All of these hold; useful inside `any`.
      
      - `not` ([object](#when.resolved.filters)): This does not hold.
      
      - `<key>` (boolean, number, string, array of (boolean, number or string) or object): A plain value (equal), a list (any of them), or an operator object such as \{ "lt": 20 \}.
        
        - `lt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `lte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `gt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `gte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `between` (array of (boolean, number or string) · 2 items): From the first value to the second, both included.
        
        - `exists` (boolean): True: the field has a value. False: it has none.
    
    - `complete` (boolean): True when the query resolved completely and no text is left.
  
  - `channel` (string or array of strings): One value, or a list meaning any of them.
  
  - `locale` (string or array of strings): One value, or a list meaning any of them.
  
  - `context` (map of (string or array of strings)): Declared context keys and the values they must have, such as `{ "customer_group": "b2b" }`.
  
  - `any` ([array of objects](#when) · at least 1 item)
  
  - `all` ([array of objects](#when) · at least 1 item)
  
  - `not` ([object](#when)): A predicate over the request (`when`). Without a condition, a rule always matches.
    
    `{ "on": "search", "query": { "contains": ["bremsen", "bremse"] } }`

- `then` (object · required): What the rule does: at least one effect.
  
  What a rule does. Understand-phase effects (`resolve`, `rewrite`) run before the query is resolved; `redirect` ends planning; the rest shape the results, and `place` puts banners beside them.
  
  - `resolve` (array of objects): Decides what an ambiguous term means. Per term, the higher rule decides.
    
    What one term means: values such as `{ "term": "puma", "as": { "brand": "puma" } }`, a category such as `"as": { "category": "parts/brakes" }`, or `"as": "text"` to keep it as text.
    
    - `term` (string · required)
    
    - `as` (string or map of (boolean, number or string) · required): One of `text`.
  
  - `rewrite` (object): Removes words from the query before it is resolved and searched. Removals of all rules are united.
    
    - `remove` (array of strings · required)
  
  - `redirect` (object): Sends the shopper elsewhere. The highest redirect wins and ends planning.
    
    Where a redirect goes: an entity's page, whose URL follows the merchant's catalog, or a fixed URL.
    
    - `to` (string): An entity named with its type, `type:id`, such as `product:SOFA-LUND-3`.
    
    - `filters` (object): With a category: the filters chosen on its page, as a request's filters name them.
      
      Picks entities (`where`). Attribute codes and the built-in fields `brand`, `price`, `on_sale`, `availability` and `delivery_days` are keys of the same object; `category` takes `is` or `within`.
      
      `{ "any": [{ "brand": "bosch" }, { "quality": "oe" }], "not": { "availability": "out_of_stock" } }`
      
      - `category` (object): The entity is in this category, or below it with `within`.
        
        A category, exactly or including everything below it.
        
        - `is` (string or array of strings): One value, or a list meaning any of them.
        
        - `within` (string or array of strings): One value, or a list meaning any of them.
      
      - `any` ([array of objects](#then.redirect.filters) · at least 1 item): At least one of these holds.
      
      - `all` ([array of objects](#then.redirect.filters) · at least 1 item): All of these hold; useful inside `any`.
      
      - `not` ([object](#then.redirect.filters)): This does not hold.
      
      - `<key>` (boolean, number, string, array of (boolean, number or string) or object): A plain value (equal), a list (any of them), or an operator object such as \{ "lt": 20 \}.
        
        - `lt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `lte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `gt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `gte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `between` (array of (boolean, number or string) · 2 items): From the first value to the second, both included.
        
        - `exists` (boolean): True: the field has a value. False: it has none.
    
    - `url` (string): A fixed URL: a path such as `/werkstatt`, or one that starts with `https://`.
  
  - `filter` (object): Narrows the results. Shoppers see it as a chip they can remove.
    
    A filter a rule adds, optionally only for one type's section.
    
    - `type` (string): The code of an entity type, such as `product`, `category` or a merchant's own `store`.
    
    - `where` (object · required): Picks entities (`where`). Attribute codes and the built-in fields `brand`, `price`, `on_sale`, `availability` and `delivery_days` are keys of the same object; `category` takes `is` or `within`.
      
      `{ "any": [{ "brand": "bosch" }, { "quality": "oe" }], "not": { "availability": "out_of_stock" } }`
      
      - `category` (object): The entity is in this category, or below it with `within`.
        
        A category, exactly or including everything below it.
        
        - `is` (string or array of strings): One value, or a list meaning any of them.
        
        - `within` (string or array of strings): One value, or a list meaning any of them.
      
      - `any` ([array of objects](#then.filter.where) · at least 1 item): At least one of these holds.
      
      - `all` ([array of objects](#then.filter.where) · at least 1 item): All of these hold; useful inside `any`.
      
      - `not` ([object](#then.filter.where)): This does not hold.
      
      - `<key>` (boolean, number, string, array of (boolean, number or string) or object): A plain value (equal), a list (any of them), or an operator object such as \{ "lt": 20 \}.
        
        - `lt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `lte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `gt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `gte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `between` (array of (boolean, number or string) · 2 items): From the first value to the second, both included.
        
        - `exists` (boolean): True: the field has a value. False: it has none.
  
  - `hide` (array of objects): Removes entities from the results. Hide beats pin.
    
    The entities an effect is about: one entity, several, or all that match a selector, optionally of one type.
    
    - `entity` (string): An entity named with its type, `type:id`, such as `product:SOFA-LUND-3`.
    
    - `entities` (array of strings)
    
    - `where` (object): Picks entities (`where`). Attribute codes and the built-in fields `brand`, `price`, `on_sale`, `availability` and `delivery_days` are keys of the same object; `category` takes `is` or `within`.
      
      `{ "any": [{ "brand": "bosch" }, { "quality": "oe" }], "not": { "availability": "out_of_stock" } }`
      
      - `category` (object): The entity is in this category, or below it with `within`.
        
        A category, exactly or including everything below it.
        
        - `is` (string or array of strings): One value, or a list meaning any of them.
        
        - `within` (string or array of strings): One value, or a list meaning any of them.
      
      - `any` ([array of objects](#then.hide.where) · at least 1 item): At least one of these holds.
      
      - `all` ([array of objects](#then.hide.where) · at least 1 item): All of these hold; useful inside `any`.
      
      - `not` ([object](#then.hide.where)): This does not hold.
      
      - `<key>` (boolean, number, string, array of (boolean, number or string) or object): A plain value (equal), a list (any of them), or an operator object such as \{ "lt": 20 \}.
        
        - `lt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `lte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `gt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `gte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `between` (array of (boolean, number or string) · 2 items): From the first value to the second, both included.
        
        - `exists` (boolean): True: the field has a value. False: it has none.
    
    - `type` (string): With `where`: only entities of this type.
  
  - `pin` (array of objects): Puts entities at fixed positions, within every filter.
    
    - `entity` (string · required): An entity named with its type, `type:id`, such as `product:SOFA-LUND-3`.
    
    - `position` (integer · required): The slot, counted from 1. Positions are unique within a rule; on a tie between rules the higher one takes the slot and the other moves to the next free one.
    
    - `sponsored` (object): A paid placement: the hit names its campaign, so a storefront marks it as an ad and reports its views and clicks for the campaign. It is placed as any pin is, only where the product meets the page's category and filters.
      
      The campaign a paid placement belongs to: `{ "campaign": "Michelin spring" }`.
      
      - `campaign` (string · required): The merchant's name for it, which the campaign report counts by.
  
  - `boost` (array of objects): Moves entities up, within relevance.
    
    A boost or bury.
    
    - `entity` (string): An entity named with its type, `type:id`, such as `product:SOFA-LUND-3`.
    
    - `entities` (array of strings)
    
    - `where` (object): Picks entities (`where`). Attribute codes and the built-in fields `brand`, `price`, `on_sale`, `availability` and `delivery_days` are keys of the same object; `category` takes `is` or `within`.
      
      `{ "any": [{ "brand": "bosch" }, { "quality": "oe" }], "not": { "availability": "out_of_stock" } }`
      
      - `category` (object): The entity is in this category, or below it with `within`.
        
        A category, exactly or including everything below it.
        
        - `is` (string or array of strings): One value, or a list meaning any of them.
        
        - `within` (string or array of strings): One value, or a list meaning any of them.
      
      - `any` ([array of objects](#then.boost.where) · at least 1 item): At least one of these holds.
      
      - `all` ([array of objects](#then.boost.where) · at least 1 item): All of these hold; useful inside `any`.
      
      - `not` ([object](#then.boost.where)): This does not hold.
      
      - `<key>` (boolean, number, string, array of (boolean, number or string) or object): A plain value (equal), a list (any of them), or an operator object such as \{ "lt": 20 \}.
        
        - `lt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `lte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `gt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `gte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `between` (array of (boolean, number or string) · 2 items): From the first value to the second, both included.
        
        - `exists` (boolean): True: the field has a value. False: it has none.
    
    - `type` (string): With `where`: only entities of this type.
    
    - `strength` (string · required): One of `slight`, `medium` or `strong`.
  
  - `bury` (array of objects): Moves entities down, within relevance.
    
    A boost or bury.
    
    - `entity` (string): An entity named with its type, `type:id`, such as `product:SOFA-LUND-3`.
    
    - `entities` (array of strings)
    
    - `where` (object): Picks entities (`where`). Attribute codes and the built-in fields `brand`, `price`, `on_sale`, `availability` and `delivery_days` are keys of the same object; `category` takes `is` or `within`.
      
      `{ "any": [{ "brand": "bosch" }, { "quality": "oe" }], "not": { "availability": "out_of_stock" } }`
      
      - `category` (object): The entity is in this category, or below it with `within`.
        
        A category, exactly or including everything below it.
        
        - `is` (string or array of strings): One value, or a list meaning any of them.
        
        - `within` (string or array of strings): One value, or a list meaning any of them.
      
      - `any` ([array of objects](#then.bury.where) · at least 1 item): At least one of these holds.
      
      - `all` ([array of objects](#then.bury.where) · at least 1 item): All of these hold; useful inside `any`.
      
      - `not` ([object](#then.bury.where)): This does not hold.
      
      - `<key>` (boolean, number, string, array of (boolean, number or string) or object): A plain value (equal), a list (any of them), or an operator object such as \{ "lt": 20 \}.
        
        - `lt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `lte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `gt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `gte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
        
        - `between` (array of (boolean, number or string) · 2 items): From the first value to the second, both included.
        
        - `exists` (boolean): True: the field has a value. False: it has none.
    
    - `type` (string): With `where`: only entities of this type.
    
    - `strength` (string · required): One of `slight`, `medium` or `strong`.
  
  - `sections` (array of strings): The order of sections. Types not listed follow in the release's order.
  
  - `place` (array of objects): Puts banners and teasers above, among or below the results. A placement is not a hit: it changes no total, facet, page size or order of hits.
    
    A banner at a place of the page: `{ "slot": "top", "banner": { ... } }`, or among the tiles, `{ "slot": "grid", "position": 5, "banner": { ... } }`.
    
    - `slot` (string · required): Where on a page a banner sits.
      
      - `top`: Above the results, on the first page. Banners of several rules stand in rule order, the highest first.
      
      - `grid`: Among the tiles, before the one at its position, on the page that holds that tile.
      
      - `bottom`: After the results, on the last page. Banners of several rules stand in rule order, the highest first.
    
    - `position` (integer · at least 1): With `grid`, and only there: the tile it sits before, counted from 1 across the pages of the listed section, whose tiles are the page's products, or its services on a workshop's page. Positions are unique within a rule; on a tie between rules the higher one takes the position and the other moves to the next free one, as pins do. A position past the last tile the pages reach shows nowhere, and the trace says why.
    
    - `banner` (object · required): What a placement shows: a campaign's banner or a teaser to a page, with its words in every locale the release serves.
      
      - `kind` (string · required): - `banner`: A campaign's banner, as wide as the results.
        
        - `teaser`: A card to a page, such as a buying guide, the size of a tile.
      
      - `title` (string or map of strings · required): The headline, up to 200 characters.
      
      - `text` (string or map of strings): A line or two under the title.
      
      - `image` (object): A banner's image and the words that stand for it.
        
        - `url` (string · required): A path such as `/media/summer.jpg`, or a URL that starts with `https://`.
        
        - `alt` (string or map of strings · required): What the image shows, for a shopper who cannot see it.
      
      - `link` (object): Where it leads, written as a redirect's target: an entity's page, such as `{ "to": "category:wohnen/sofas" }` or `{ "to": "content:sofa-ratgeber" }`, or a fixed URL, `{ "url": "/sale" }`. Without one it only informs.
        
        Where a redirect goes: an entity's page, whose URL follows the merchant's catalog, or a fixed URL.
        
        - `to` (string): An entity named with its type, `type:id`, such as `product:SOFA-LUND-3`.
        
        - `filters` (object): With a category: the filters chosen on its page, as a request's filters name them.
          
          Picks entities (`where`). Attribute codes and the built-in fields `brand`, `price`, `on_sale`, `availability` and `delivery_days` are keys of the same object; `category` takes `is` or `within`.
          
          `{ "any": [{ "brand": "bosch" }, { "quality": "oe" }], "not": { "availability": "out_of_stock" } }`
          
          - `category` (object): The entity is in this category, or below it with `within`.
            
            A category, exactly or including everything below it.
            
            - `is` (string or array of strings): One value, or a list meaning any of them.
            
            - `within` (string or array of strings): One value, or a list meaning any of them.
          
          - `any` ([array of objects](#then.place.banner.link.filters) · at least 1 item): At least one of these holds.
          
          - `all` ([array of objects](#then.place.banner.link.filters) · at least 1 item): All of these hold; useful inside `any`.
          
          - `not` ([object](#then.place.banner.link.filters)): This does not hold.
          
          - `<key>` (boolean, number, string, array of (boolean, number or string) or object): A plain value (equal), a list (any of them), or an operator object such as \{ "lt": 20 \}.
            
            - `lt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
            
            - `lte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
            
            - `gt` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
            
            - `gte` (boolean, number or string): A single value in a selector, filter or test: text, a number or a boolean. Dates are text, such as `2026-10-01`.
            
            - `between` (array of (boolean, number or string) · 2 items): From the first value to the second, both included.
            
            - `exists` (boolean): True: the field has a value. False: it has none.
        
        - `url` (string): A fixed URL: a path such as `/werkstatt`, or one that starts with `https://`.

- `schedule` (object): When the rule is active, checked against the time in the request, never the engine's clock.
  
  A window in time: from `from` (inclusive) until `until` (exclusive). Either end may be open.
  
  - `from` (string): A point in time as RFC 3339 with an offset, such as `2026-11-27T00:00:00+01:00`.
  
  - `until` (string): A point in time as RFC 3339 with an offset, such as `2026-11-27T00:00:00+01:00`.

- `origin` (object): Where the rule came from. Without it, the merchant made it.
  
  Where an imported rule or overlay came from.
  
  - `import` (string · required): What it was imported from, such as the previous search's export.
  
  - `ref` (string · required)

## `$schema`

string

The JSON Schema an editor checks this file against; kept as written.

## Example

```json title="tests/fixtures/home/rules.json (trimmed)"
{
  "$schema": "../../../contracts/schema/rules.json",
  "rules": [
    {
      "id": "home/sofa-sale",
      "description": "Sofa sale on top of the sofas, armchairs among them",
      "when": { "category": { "is": "wohnen/sofas" } },
      "then": {
        "place": [
          {
            "slot": "top",
            "banner": {
              "kind": "banner",
              "title": { "de": "Sofas im Angebot", "en": "Sofas on sale" },
              "text": {
                "de": "Samt, Leder und Stoff: ausgewählte Sofas jetzt reduziert.",
                "en": "Velvet, leather and fabric: selected sofas, now reduced."
              },
              "image": {
                "url": "/demo/images/81dwblf8ogL.jpg",
                "alt": {
                  "de": "Grünes Samtsofa mit goldenen Beinen",
                  "en": "Green velvet sofa with gold legs"
                }
              },
              "link": {
                "to": "category:wohnen/sofas",
                "filters": { "on_sale": true }
              }
            }
          }
        ]
      }
    }
  ]
}
```

**Guide:** [Rules](/docs/rules) · [Banners and teasers](/docs/banners)
