---
title: "Validate a CEL condition"
method: POST
path: "/v1/rules/validate"
tags: ["Rules"]
---

# Validate a CEL condition

`POST /v1/rules/validate`

## Request

- Base URL: `https://api.scrip.dev`
- URL: `https://api.scrip.dev/v1/rules/validate`
- Auth: HTTP bearer

Check whether a CEL expression is syntactically valid without creating a rule.

## Request body

- ValidateConditionRequest: A rule condition and actions to check for errors without saving them.
  - `actions` RuleActionDraft[]: Draft actions to check along with the condition. Incomplete actions are accepted. Expressions are checked as they are when saving a rule. With program_id, validation also checks asset restrictions and recognizes keys these actions set, so reading those keys does not produce an unknown-key warning. (unresolved $ref)
  - `condition` string, required: CEL expression to validate
  - `program_id` string, uuid: Program to check the draft against. Checks tier keys and warns about counters, tags, or attributes that no rule in the program sets. With actions, also checks whether the asset supports expires_at, matures_at, and reference_id, plus reference_id length and format. Without program_id, checks cover tier field structure, participant fields, and deprecated names.
  - `rule_id` string, uuid: Rule you are editing. Excludes its saved actions from the known state keys so renaming a key in the draft does not hide a warning. Omit when creating a rule.

## Response `200`

Validation result

- ValidateConditionResponse: Whether a condition and its actions are valid, with any warnings.
  - `message` string: Human-readable validation result or error detail
  - `note` string: Additional context about validation scope (present only on success)
  - `valid` boolean: Whether the condition is syntactically valid
  - `warnings` RuleWarning[]: Advisories that never affect validity, such as a counter, tag or attribute key that no rule in the program writes (likely a typo), or a deprecated CEL alias.
    - `client_ref` string: The `client_ref` of the rule, when the rule is new.
    - `code` string: What the warning is about. On a rule: `unknown_state_key` (a key no rule writes), `deprecated_alias`, or `possible_unmarked_expression` (a literal that looks like CEL without `${{ }}`). On a simulation: `missing_participant_context`. On a configuration preview, also `stop_after_match_has_no_following_rule`, `unconditional_stop_shadows_following_rules`, or `later_rule_set_retained_empty`.
    - `key` string: The referenced key, as written in the condition.
    - `kind` string: Kind of reference: `counter`, `tag`, `attribute`, or `alias`.
    - `message` string: Human-readable explanation (includes the suggestion when one exists).
    - `rule_id` string: Rule identity is populated for complete-configuration validation. The single-rule authoring surfaces omit these fields.
    - `rule_set_id` string: The id of the rule set that holds the rule.
    - `scope` string: Where the referenced state belongs: `participant`, `program`, or `group`.
    - `suggestion` string: Closest known key, when a near-match exists. Empty otherwise.

## Other responses

- `400`: Invalid request (code: validation_error | invalid_json)
- `401`: The API key is missing or not valid. See [Authentication](https://docs.scrip.dev/authentication).
- `403`: The caller is not allowed to do this.
- `429`: Too many requests. Wait the number of seconds in the `Retry-After` header, then retry. See [Limits and durations](https://docs.scrip.dev/limits#rate-limits).
- `500`: Something failed on Scrip's side. Retry the request, and quote `request_id` if it keeps failing. [Errors](https://docs.scrip.dev/errors) lists every code, and the [API introduction](https://docs.scrip.dev/api-reference/introduction) describes the error format.

---

[API](https://skmtc.dev/scrip/apis/scrip-api.md) · [All operations](https://skmtc.dev/scrip/apis/scrip-api/llms.txt) · [OpenAPI document](https://skmtc.dev/scrip/apis/scrip-api/revisions/c52d35da8d97?raw)
