---
title: "Upsert a batch of rules."
method: PUT
path: "/limits/rules"
tags: ["rule"]
---

# Upsert a batch of rules.

`PUT /limits/rules`

Each entry carries an optional [`Precondition`]. Setting it to
`DoesNotExist` makes the entry a strict insert; `Matches(v)` rejects
the batch unless the rule's current version is `v`; omitting it
(`None`) is unconditional. The whole batch is atomic: any failed
precondition or cap-exceeded condition rolls back the rest.

## Request body

- UpsertRuleRequest[]
  - `description` string, nullable — Free-form description shown in the rule book; not consulted at runtime.
  - `disabled` boolean — Soft-tombstone toggle. `true` parks the rule (the runtime treats it as absent) without removing it.
  - `limits` UserLimits — Per-rule effective limits. `None` on a field means "unlimited" (no rule constrains this dimension). Under the `bilrost` feature this type is also the wire shape persisted inside [`crate::PersistedRule`]; under `serde` it's the JSON wire shape for the admin REST model — adding a new limit kind here means allocating a fresh `bilrost(tag(...))` next to the new field.
    - `concurrency` integer, nullable — Maximum concurrent running invocations. `None` means unlimited.
  - `pattern` string, required — The pattern that selects which scope/limit-key combinations the rule applies to. Examples: `"*"`, `"scope1/*"`, `"scope1/foo/bar"`.
  - `precondition` union — Optimistic-concurrency guard for a [`RuleChange`]. - [`Precondition::None`] applies the change unconditionally. - [`Precondition::Matches`] requires the rule to be present at the given version; otherwise the change is rejected with [`RuleBookError::PreconditionFailed`]. - [`Precondition::DoesNotExist`] requires the rule to be absent. Combined with `Upsert` this is a pure insert.
    - object
      - `type` 'none', required
    - object
      - `type` 'matches', required
      - `version` integer, required — A type used for versioned metadata.
    - object
      - `type` 'does_not_exist', required

## Response `200`

Rules upserted

- RuleResponse[]
  - `description` string, nullable
  - `disabled` boolean, required
  - `last_modified_millis_since_epoch` integer, required — Millis since UNIX epoch.
  - `limits` UserLimits, required — Per-rule effective limits. `None` on a field means "unlimited" (no rule constrains this dimension). Under the `bilrost` feature this type is also the wire shape persisted inside [`crate::PersistedRule`]; under `serde` it's the JSON wire shape for the admin REST model — adding a new limit kind here means allocating a fresh `bilrost(tag(...))` next to the new field.
    - `concurrency` integer, nullable — Maximum concurrent running invocations. `None` means unlimited.
  - `pattern` string, required
  - `version` integer, required — Per-rule version: bumped on runtime-relevant changes.

## Other responses

- `409` — Conflict
- `422` — Unprocessable entity
- `500` — Internal server error

## Changes

> 8 revisions in range; 1 could not be searched.

- **2026-06-19** `7381abc4d9a3` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/restatedev/apis/admin-api/changes/limits/rules/put.md)

---

[API](https://skmtc.dev/restatedev/apis/admin-api.md) · [All operations](https://skmtc.dev/restatedev/apis/admin-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/restatedev/admin-api/revisions/5361f4818c71/schema)
