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

# Managing rule configuration

> Change, audit, and restore a program's rules without overwriting another writer's edits

A program's rule configuration is every [rule set](/guides/rule-sets) and non-archived rule Scrip will evaluate: order, status, actions, and budgets. Scrip stores that snapshot under one number, `rule_configuration_version`, and increases the number by one on every change.

[Writing rules](/guides/writing-rules) covers creating and updating one rule at a time. Use this page when more than one writer can change the same program, or when one change touches several rules or sets. The dashboard and the [Scrip MCP server](/mcp/overview) follow the same flow.

## Choose an endpoint

| What you want                         | Where to send it                                                                                    |
| ------------------------------------- | --------------------------------------------------------------------------------------------------- |
| Create or edit one rule               | `POST /v1/rules` or `PATCH /v1/rules/{id}`                                                          |
| Create or rename one rule set         | `POST /v1/programs/{programId}/rule-sets` or `PATCH /v1/programs/{programId}/rule-sets/{ruleSetId}` |
| Move one rule or set                  | `/move`, with the current version                                                                   |
| Replace one full order                | `/order`, with the current version                                                                  |
| Change several rules or sets together | `/rule-configuration/preview`, then `/rule-configuration/apply`                                     |
| See what the program looked like      | `/rule-configuration/revisions`                                                                     |
| Restore an earlier configuration      | `/rollback/preview`, then standard apply                                                            |

Create and patch on a single rule or set do not check `rule_configuration_version`, so two writers can overwrite each other. They still return the new version when the write changes the configuration.

## Read the current configuration

```bash theme={null}
curl https://api.scrip.dev/v1/programs/$PROGRAM_ID/rule-configuration \
  -H "Authorization: Bearer $SCRIP_API_KEY"
```

The response is one snapshot: every rule set in execution order, and every `ACTIVE` and `SUSPENDED` rule in each set. Archived rules are omitted.

```json theme={null}
{
  "program_id": "71000000-0000-4000-8000-000000000001",
  "rule_configuration_version": 12,
  "rule_sets": [
    {
      "rule_set": {
        "id": "71000000-0000-4000-8000-000000000010",
        "program_id": "71000000-0000-4000-8000-000000000001",
        "key": "default",
        "name": "Default",
        "order": 1,
        "created_at": "2026-07-16T10:30:00Z",
        "updated_at": "2026-07-18T14:20:00Z"
      },
      "rules": [
        {
          "id": "71000000-0000-4000-8000-000000000021",
          "rule_set_id": "71000000-0000-4000-8000-000000000010",
          "name": "Base earn",
          "order": 1,
          "status": "ACTIVE",
          "condition": "event.type == 'purchase'",
          "actions": [
            {"type": "CREDIT", "asset_id": "71000000-0000-4000-8000-000000000020", "amount": "${{ event.amount }}"}
          ],
          "budgets": [],
          "stop_after_match": false
        }
      ]
    }
  ]
}
```

Pass `rule_configuration_version` from this response on move, reorder, preview, and rollback requests. If another writer changes the program first, those requests return `409 rule_configuration_version_conflict`. Read the configuration again and rebuild the request from the new snapshot.

See [Get the complete rule configuration](/api-reference/rules/get-rule-configuration).

## Move or reorder

To place one rule or set, send a `position` of `first`, `last`, `before`, or `after`. `before` and `after` need `reference_id`. See [Rule sets](/guides/rule-sets) for execution order.

Move one set:

```bash theme={null}
POST /v1/programs/{programId}/rule-sets/{ruleSetId}/move
{
  "position": {"placement": "first"},
  "expected_rule_configuration_version": 12
}
```

Replace the full set order when you already have the sequence you want. The list must contain every current rule set exactly once:

```bash theme={null}
PUT /v1/programs/{programId}/rule-sets/order
{
  "rule_set_ids": [
    "71000000-0000-4000-8000-000000000011",
    "71000000-0000-4000-8000-000000000010"
  ],
  "expected_rule_configuration_version": 12
}
```

Reorder the `ACTIVE` and `SUSPENDED` rules in one set the same way. Include every non-archived rule in that set exactly once. An empty `rule_ids` list is valid for an empty set:

```bash theme={null}
PUT /v1/programs/{programId}/rule-sets/{ruleSetId}/rules/order
{
  "rule_ids": [
    "71000000-0000-4000-8000-000000000022",
    "71000000-0000-4000-8000-000000000021"
  ],
  "expected_rule_configuration_version": 13
}
```

Move a rule inside its set, or into another set, in one request:

```bash theme={null}
POST /v1/rules/{ruleId}/move
{
  "rule_set_id": "71000000-0000-4000-8000-000000000011",
  "position": {
    "placement": "after",
    "reference_id": "71000000-0000-4000-8000-000000000022"
  },
  "expected_rule_configuration_version": 14
}
```

A cross-set move renumbers the source and destination in the same transaction.

## Check a draft

Single-rule validation and simulation evaluate one rule. They do not run the rest of the program, so they cannot prove cross-set order, `stop_after_match`, or conditions that depend on an earlier rule's writes. Analyze the draft and run a test before you apply a change that spans several rules.

Send `{}` to analyze the live configuration:

```bash theme={null}
POST /v1/programs/{programId}/rule-configuration/analysis
{}
```

To analyze a draft, send that draft as `proposed_configuration` in the same shape as preview. Each finding includes `severity`, `confidence`, `runtime_semantics`, and `remediation`.

<Warning>
  Analysis is conservative and advisory. Treat a `likely` or `advisory` finding as a reason to inspect and test the draft.
</Warning>

See [Analyze a rule configuration](/api-reference/rules/analyze-rule-configuration).

Then send the same draft as `rule_configuration` to `POST /v1/test-runs`. The test run evaluates in a transaction that rolls back. It does not change live rules, balances, budgets, or stored state.

[Testing](/guides/testing#testing-multiple-rule-sets) covers scenario vs replay, `compare_live`, and traces. The [multi-set example](/examples/rule-configuration-workflow) shows a layout where set-local stops and the event-start snapshot matter.

## Preview and apply

Use preview and apply when one change updates several rule sets, rules, positions, statuses, actions, or budgets.

The preview body is the configuration you want, in execution order:

```bash theme={null}
POST /v1/programs/{programId}/rule-configuration/preview
{
  "base_rule_configuration_version": 14,
  "source": {
    "type": "config_as_code",
    "reference": "rewards-config-v42"
  },
  "rule_sets": [
    {
      "id": "71000000-0000-4000-8000-000000000010",
      "key": "default",
      "name": "Default",
      "rules": [
        {
          "id": "71000000-0000-4000-8000-000000000021",
          "name": "Base earn",
          "condition": "event.type == 'purchase'",
          "actions": [
            {"type": "CREDIT", "asset_id": "71000000-0000-4000-8000-000000000020", "amount": "${{ event.amount }}"}
          ],
          "budgets": [],
          "status": "ACTIVE",
          "stop_after_match": false
        },
        {
          "client_ref": "summer-bonus",
          "name": "Summer bonus",
          "condition": "event.type == 'purchase' && event.region == 'US'",
          "actions": [
            {"type": "CREDIT", "asset_id": "71000000-0000-4000-8000-000000000020", "amount": "${{ event.amount * 0.25 }}"}
          ],
          "budgets": [],
          "status": "SUSPENDED",
          "stop_after_match": false
        }
      ]
    }
  ]
}
```

| In the request              | What Scrip does                                                                     |
| --------------------------- | ----------------------------------------------------------------------------------- |
| Existing rule set or rule   | Identify it with `id`                                                               |
| New rule set or rule        | Omit `id` and send a unique `client_ref`                                            |
| Existing rule set           | Repeat its immutable `key`                                                          |
| Every existing rule set     | Include it exactly once. Sets cannot be removed.                                    |
| Rule you want to keep       | Include the full definition: `actions`, `budgets`, `status`, and `stop_after_match` |
| Existing rule you leave out | Archived when you apply                                                             |
| Array order                 | Becomes the set order and the rule order inside each set                            |

Preview runs the same CEL, action, target, asset, and budget checks as a single-rule write. It does not change the live configuration or increment the version.

The response includes:

| Field                                         | Meaning                                              |
| --------------------------------------------- | ---------------------------------------------------- |
| `proposed_rule_configuration`                 | The plan the server will commit                      |
| `diff`                                        | Set-level and rule-level changes                     |
| `warnings`                                    | Issues to review before you apply                    |
| `local_references`                            | Each `client_ref` mapped to the UUID Scrip allocated |
| `preview_token`, `request_hash`, `expires_at` | What you pass to apply                               |

Read the plan, diff, and warnings. Then apply exactly what preview stored:

```bash theme={null}
POST /v1/programs/{programId}/rule-configuration/apply
{
  "preview_token": "opaque-preview-token",
  "request_hash": "64-character-sha256-hash-from-preview"
}
```

The apply body cannot change the proposal. Scrip verifies the token, expiry, request hash, configuration version, and referenced dependencies, then commits the plan in one transaction.

A successful apply increases `rule_configuration_version` by one. A preview can be applied once. If it expires, was already applied, or no longer matches current dependencies, create a new preview.

See [Preview a rule-configuration change set](/api-reference/rules/preview-rule-configuration) and [Apply a rule-configuration change set](/api-reference/rules/apply-rule-configuration).

## History

Each change that takes effect creates one revision. A revision stores the configuration at that version and the diff from the previous one.

List revisions, newest first:

```bash theme={null}
GET /v1/programs/{programId}/rule-configuration/revisions?limit=20
```

Use `pagination.next_cursor` for the next page.

Get one revision:

```bash theme={null}
GET /v1/programs/{programId}/rule-configuration/revisions/{version}
```

Find the latest tracked revision at or before a timestamp:

```bash theme={null}
GET /v1/programs/{programId}/rule-configuration/revisions/as-of?at=2026-07-18T14:20:00Z
```

If the timestamp is before tracked history, the response omits `revision` and returns a `caveats` entry.

Compare two tracked revisions:

```bash theme={null}
GET /v1/programs/{programId}/rule-configuration/revisions/diff?from_version=10&to_version=14
```

Omit `to_version`, or pass `to_version=current`, to compare a tracked revision with the live configuration.

<Warning>
  Program-level history starts at `earliest_tracked_version`. Requests that reach before it return `caveats` (`history_not_yet_tracked`, `pre_history_legacy_data`).
</Warning>

## Roll back

Rollback builds a normal preview that restores a past configuration. History is left in place; the restore is a new revision.

```bash theme={null}
POST /v1/programs/{programId}/rule-configuration/revisions/{version}/rollback/preview
{
  "expected_rule_configuration_version": 18,
  "source": {
    "type": "dashboard",
    "reference": "incident-rollback"
  }
}
```

Review the plan, inverse diff, and warnings. Apply it with the same `preview_token` and `request_hash` as any other preview.

Rule sets created after the target revision remain, with no rules, because rule sets cannot be deleted. The preview warns about each of those sets. Rules created after the target are archived. Existing rule and rule-set IDs stay the same.

The new revision records `source_revision`, so you can see which historical configuration was restored and who applied it.

See [Preview a rule-configuration rollback](/api-reference/rules/preview-rule-configuration-rollback).

## If a request fails

| Signal                                       | What to do                                                                                   |
| -------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `409 rule_configuration_version_conflict`    | Read the configuration again. Rebuild the change from that snapshot, then retry.             |
| `422 invalid_position` or invalid membership | Read the containing set. Use `before` or `after`, or send every current member exactly once. |
| Failed test run                              | Inspect the rule trace. Fix the draft or the scenario before you preview.                    |
| Analysis or preview warning                  | Read `severity`, `confidence`, `runtime_semantics`, and `remediation`.                       |
| `rule_configuration_preview_expired`         | Preview the current draft again.                                                             |
| `rule_configuration_dependency_drift`        | Re-read the assets, targets, tiers, or other referenced resources, then preview again.       |
| `rule_configuration_preview_already_applied` | Read the current configuration and history before doing more work.                           |
| `401` or `403`                               | Fix credentials. Do not retry the write through another tool.                                |
