> For a complete page index of the Captain API documentation, fetch https://docs.captain.dev/llms.txt?excludeSpec=true

# Replace Rules

PUT https://acme.captain.dev/v1/indexes/{index}/rules
Content-Type: application/json

Replace the index's rules. They apply to the next search, with no rebuild.

Reference: https://docs.captain.dev/reference/product-search/indexes/replace-rules

## Authentication

- `Authorization` header (bearer token, required) — A write key, issued by Captain. Server side only. Sent as Authorization: Bearer \<key>.

## Request

### Path parameters

- `index` (string, required) — index name

### Headers

- `Idempotency-Key` (string, optional) — Replays within 24 hours return the stored response (header Idempotent-Replayed: true). The same key on a different request answers 422. A concurrent duplicate answers 409. Failed requests do not keep the key.

### Body (application/json)

This endpoint expects an object.

- `rules` (list of Rule, optional)

## Response

### 200

saved

## Types

### Rule

- `objectID` (string, optional)
- `enabled` (boolean, optional)
- `condition` (RuleCondition, optional)
- `consequence` (RuleConsequence, optional)

### RuleCondition

- `pattern` (string, optional)
- `anchoring` (enum, optional)
  - Allowed values: `is`, `contains`, `startsWith`, `endsWith`
- `context` (string, optional) — fires only for a search that sends it in rule_contexts

### RuleConsequence

- `promote` (list of RuleConsequencePromoteItems, optional)
- `hide` (list of RuleConsequenceHideItems, optional)
- `filters` (map from string to any, optional) — narrow the results to records matching these filters, ANDed with the request's (a key the request also sets is replaced). Values match exactly and are case-sensitive unless the index turns on typed\_boost\_filters, which also enables range filters \{gte, gt, lte, lt} on number and date fields and the field names the index settings define. A shape the engine cannot evaluate answers 400 VALIDATION\_ERROR, with the flag on or off
- `params` (RuleConsequenceParams, optional)
- `boost` (RuleConsequenceBoost, optional) — raise the results whose fields match filter. A boost reorders only the top 50 results the search already found, and never adds a record. Strength 1 lifts a match above every non-matching result in that top 50 except, at most, the one it ties with; use a strength above 1 to be sure it comes first. 0.5 moves it about halfway. Filter values match exactly and are case-sensitive unless the index turns on typed\_boost\_filters, which also enables range filters. With typed\_boost\_filters on (an index config setting Captain turns on per index; off by default), a number or date field also takes a range \{gte, gt, lte, lt}, values match case-insensitively, and a field may be named as the index settings name it. A filter value the engine cannot evaluate (an unknown range key, an object that is not a range, a list of objects, null, a non-number for price\_min, price\_max or rating\_min) answers 400 VALIDATION\_ERROR, with the flag on or off. A range saved while the flag is off is kept and matches nothing until the flag is on. A rule stored before this check, sent back unchanged (same objectID, same content) or copied with its index, is kept as it is, and GET rules names it with a RULE\_FILTER\_UNEVALUABLE warning
- `userData` (RuleConsequenceUserData, optional) — any JSON object, at most 1 kB minified: returned in the search's user_data
- `facet_ordering` (FacetOrdering, optional) — how a page orders facets and their values (Algolia renderingContent.facetOrdering, snake_case). Returned on search and browse when the index or an applied rule sets one; the client orders with it
- `redirect` (RuleConsequenceRedirect, optional) — send the shopper to this page instead of the results: returned as the search's redirect, which the captain-search box follows. Algolia's params.renderingContent.redirect is accepted

### RuleConsequencePromoteItems

- `objectID` (string, optional)
- `position` (integer, optional)

### RuleConsequenceHideItems

- `objectID` (string, optional)

### RuleConsequenceParams

- `filters` (string, optional)

### RuleConsequenceBoost

raise the results whose fields match filter. A boost reorders only the top 50 results the search already found, and never adds a record. Strength 1 lifts a match above every non-matching result in that top 50 except, at most, the one it ties with; use a strength above 1 to be sure it comes first. 0.5 moves it about halfway. Filter values match exactly and are case-sensitive unless the index turns on typed\_boost\_filters, which also enables range filters. With typed\_boost\_filters on (an index config setting Captain turns on per index; off by default), a number or date field also takes a range \{gte, gt, lte, lt}, values match case-insensitively, and a field may be named as the index settings name it. A filter value the engine cannot evaluate (an unknown range key, an object that is not a range, a list of objects, null, a non-number for price\_min, price\_max or rating\_min) answers 400 VALIDATION\_ERROR, with the flag on or off. A range saved while the flag is off is kept and matches nothing until the flag is on. A rule stored before this check, sent back unchanged (same objectID, same content) or copied with its index, is kept as it is, and GET rules names it with a RULE\_FILTER\_UNEVALUABLE warning

- `filter` (map from string to any, optional) — field -> value (exact), or list (any of). Also price\_min, price\_max, rating\_min, t\_\<field>*min / t*\<field>\_max for typed numbers (flexible schema), attributes.\<key> for attribute values. A filterable field is named as the settings name it: a value or a list of values, or, on a number or date field, a range \{gte, gt, lte, lt}. A bare date as lte covers that whole day (lte 2003-12-31 keeps filings made on the 31st). The filter expression (filter) says the same and more
- `strength` (double, optional)

### RuleConsequenceUserData

any JSON object, at most 1 kB minified: returned in the search's user_data

### FacetOrdering

how a page orders facets and their values (Algolia renderingContent.facetOrdering, snake_case). Returned on search and browse when the index or an applied rule sets one; the client orders with it

- `facets` (FacetOrderingFacets, optional)
- `values` (map from string to FacetOrderingValues, optional)

### RuleConsequenceRedirect

send the shopper to this page instead of the results: returned as the search's redirect, which the captain-search box follows. Algolia's params.renderingContent.redirect is accepted

- `url` (string, required) — a path on the store (/returns) or an http(s) URL

### FacetOrderingFacets

- `order` (list of string, optional)

### FacetOrderingValues

- `order` (list of string, optional) — values pinned first, in this order
- `sort_remaining_by` (enum, optional, default: count)
  - Allowed values: `count`, `alpha`, `hidden`
- `hide` (list of string, optional)

## Examples

**Request**

```json
{
  "rules": [
    {
      "objectID": "r1",
      "condition": {
        "pattern": "rain jacket",
        "anchoring": "contains"
      },
      "consequence": {
        "promote": [
          {
            "objectID": "sku-20311",
            "position": 0
          }
        ]
      }
    }
  ]
}
```

**Response**

```json
{
  "rules": [
    {
      "objectID": "r1",
      "condition": {
        "pattern": "rain jacket",
        "anchoring": "contains"
      },
      "consequence": {
        "promote": [
          {
            "objectID": "sku-20311",
            "position": 0
          }
        ]
      }
    }
  ]
}
```

**SDK Code**

```python Python REST
import json
import requests
import uuid

BASE_URL = "https://acme.captain.dev"
API_KEY = "your_write_key"
INDEX = "products"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
    "Idempotency-Key": str(uuid.uuid4()),
}

response = requests.put(
    f"{BASE_URL}/v1/indexes/{INDEX}/rules",
    headers=headers,
    json={
        "rules": [
            {
                "objectID": "r1",
                "condition": {"pattern": "rain jacket", "anchoring": "contains"},
                "consequence": {"promote": [{"objectID": "sku-20311", "position": 0}]},
            },
        ],
    },
    timeout=30.0,
)

print(json.dumps(response.json(), indent=2))
```

```typescript TypeScript REST
const BASE_URL = "https://acme.captain.dev";
const API_KEY = "your_write_key";
const index = "products";

const response = await fetch(`${BASE_URL}/v1/indexes/${index}/rules`, {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    rules: [
      {
        objectID: "r1",
        condition: { pattern: "rain jacket", anchoring: "contains" },
        consequence: { promote: [{ objectID: "sku-20311", position: 0 }] },
      },
    ],
  }),
});

const result = await response.json();
console.log(JSON.stringify(result, null, 2));
```