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

# Constraints

> Constraints are guardrails: an event rate a variant may not breach and stay live, relative to the control or absolute, changeable without a restart.

A **constraint** is a guardrail. Where a reward says what to pursue, a constraint says what a variant may not do: raise support tickets, depress completions, push a score below a floor. A variant that clearly breaches one is concluded, whatever its rewards look like.

```json theme={null}
"constraints": [
  { "name": "support_ticket_created", "threshold_type": "relative", "threshold": 0.10, "bound": "upper" },
  { "name": "session_completed", "threshold_type": "relative", "threshold": -0.10, "bound": "lower" },
  { "name": "csat_promoter", "threshold_type": "absolute", "threshold": 0.30, "bound": "lower" },
  { "name": "nps_submitted", "type": "continuous", "threshold_type": "absolute", "threshold": 30, "bound": "lower" }
]
```

## Fields

| Field | Default | Meaning |
| - | - | - |
| `name` | required | The event's past-tense name. |
| `type` | `binary` | `binary` (a rate: did the event happen) or `continuous` (the event's value). |
| `threshold_type` | required | `relative` to the control, or `absolute` on the metric's own scale. |
| `threshold` | required | The bound; see below. |
| `bound` | required | `upper`: the metric must stay below the bound. `lower`: it must stay above. |
| `window_seconds` | `86400` | How long after a decision an event counts. |
| `properties` | none | Only events whose properties match are counted. |

**Relative** thresholds are a fractional change against the control's observed value: the bound is the control's value times `1 + threshold`. `0.10, upper` means the variant may not exceed the control by more than ten percent; `-0.10, lower` means it may not fall more than ten percent below. **Absolute** thresholds are the bound itself: a rate between 0 and 1 for a binary constraint, a value for a continuous one.

## How enforcement works

Before variants are compared, each live variant's observed metric is evaluated against each constraint, literally and in your units. A variant is concluded for a **clear breach**: enough observations to judge, and the breach not attributable to sampling noise. A noisy early reading never concludes anything. The conclusion is recorded with its reason and the observed value against the threshold, and Curious generates replacements in the next round. The control is never evaluated and never concluded.

A relative constraint needs the control's own observations to resolve its bound; until the control has matured observations for that event, the constraint waits.

## Constraints are policy, not identity

Changing a constraint never restarts the experiment. Tighten a threshold, loosen a window, add or remove a guardrail, and the new declaration applies from the next evaluation. Each `(name, type, bound, properties)` identity may appear once in a declaration; `threshold`, `threshold_type` and `window_seconds` are its adjustable fields.

## Declaring one well

Name the event you would be alarmed to see move, declare the direction that would alarm you, and report that event for every user, including the ones who saw the control. A guardrail on an event you never report can never fire.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.