> ## 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.

# Validate a CEL condition

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

Checks whether a CEL expression is valid without creating a rule. Pass the `condition` string in the request body. The response returns a `valid` boolean and a `message` with details.

Pass an optional `program_id` to validate references against a specific program: tier keys and asset symbols in the expression are checked against that program's configuration, and the response includes non-blocking vocabulary `warnings`. Pass an optional draft `actions` array to run the same checks on action expressions (template syntax, CEL compilation, references) before creating the rule.

Field references to `event.*` are never validated because events are schemaless. For example, `event.amount > 100` passes even if your events never include an `amount` field. Use the [simulate endpoint](/api-reference/rules/simulate-a-rule) to test a rule against realistic data.

<Note>
  For usage patterns and examples, see the [Writing Rules guide](/guides/writing-rules).
</Note>


## OpenAPI

````yaml POST /v1/rules/validate
openapi: 3.0.3
info:
  contact:
    email: support@scrip.dev
    name: Scrip Support
    url: https://scrip.dev/support
  description: >-
    Universal Incentive Infrastructure API for managing loyalty programs,
    rewards, and participant balances.


    Scrip provides a complete backend for building incentive and loyalty
    programs. Core concepts:

    - **Programs**: Containers for incentive logic (e.g., "Q1 Sales Bonus",
    "Customer Loyalty")

    - **Assets**: The currency or points being tracked (e.g., "Bonus Points",
    "Cash Rewards")

    - **Participants**: Users who earn and spend assets (identified by
    external_id)

    - **Groups**: Collections of participants for team-based incentives

    - **Rules**: Automated reward logic triggered by events

    - **Events**: Actions that trigger rule evaluation (e.g., "purchase",
    "referral")


    Response formats:

    - **Collection endpoints** return: {"data": [...], "pagination":
    {"has_more": true, "next_cursor": "..."}}

    - **Single-resource endpoints** return the resource directly

    - **Errors** return: {"code": "...", "message": "...", "details": {...}} —
    `details` is an optional object, present on input errors only
  license:
    name: Proprietary
    url: https://scrip.dev/license
  termsOfService: https://scrip.dev/terms
  title: Scrip API
  version: '1.0'
servers:
  - url: https://api.scrip.dev
security: []
tags:
  - description: >-
      Manage incentive programs. Programs are the top-level container for all
      incentive logic.
    name: Programs
  - description: >-
      Manage asset types (currencies, points). Assets define what participants
      can earn and spend.
    name: Assets
  - description: >-
      Manage participants and their balances. Participants are identified by
      external_id from your system.
    name: Participants
  - description: Manage participant groups for team-based incentives.
    name: Groups
  - description: >-
      Manage automated reward rules. Rules define conditions and actions
      triggered by events.
    name: Rules
  - description: Ingest events that trigger rule evaluation and reward distribution.
    name: Events
  - description: Transfer assets between participants.
    name: Transfers
  - description: Access ledger summaries and program activity reports.
    name: Reporting
  - description: >-
      Redeem participant balances for rewards. Supports raw amount redemptions
      and catalog item redemptions.
    name: Redemptions
  - description: >-
      Manage the reward catalog. Create and manage redeemable items with
      inventory tracking.
    name: Rewards
  - description: >-
      Schedule and manage automated event dispatching. Automations generate
      events on cron schedules, at specific times, or by evaluating participant
      state.
    name: Automations
  - description: >-
      Manage tier types and levels within programs. Tiers define status
      hierarchies that participants progress through based on qualification
      rules.
    name: Tiers
  - description: Inspect double-entry ledger records for auditing and reconciliation.
    name: Journal Entries
  - description: >-
      Manage webhook endpoints and delivery logs. Webhooks notify your
      application of real-time events via HTTP POST with HMAC-SHA256 signatures.
    name: Webhooks
paths:
  /v1/rules/validate:
    post:
      tags:
        - Rules
      summary: Validate a CEL condition
      description: >-
        Check whether a CEL expression is syntactically valid without creating a
        rule.
      operationId: validateRule
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/handlers.ValidateConditionRequest'
        description: Condition to validate
        required: true
        x-originalParamName: request
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/handlers.ValidateConditionResponse'
          description: Validation result
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/handlers.ErrBadRequestResponse'
          description: 'Invalid request (code: validation_error | bad_request)'
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/handlers.ErrUnauthorizedResponse'
          description: 'Missing or invalid credentials (code: unauthorized)'
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/handlers.ErrForbiddenResponse'
          description: 'Insufficient permissions (code: forbidden)'
        '415':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/handlers.ErrUnsupportedMediaTypeResponse'
          description: 'Content-Type must be application/json (code: unsupported_media_type)'
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/handlers.ErrInternalResponse'
          description: 'Internal server error (code: internal_error)'
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
components:
  schemas:
    handlers.ValidateConditionRequest:
      additionalProperties: false
      properties:
        actions:
          description: >-
            Optional draft actions. When supplied alongside program_id, the keys
            these

            actions write count as known vocabulary, so a condition that reads a
            key the

            same rule writes does not warn. Used for accurate live validation
            before the

            rule is saved; only the action keys/targets are read (no full
            validation).
          items:
            $ref: '#/components/schemas/models.RuleAction'
          type: array
        condition:
          description: CEL expression to validate
          example: event.type == 'purchase' && event.amount > 100
          minLength: 1
          type: string
        program_id:
          description: >-
            Optional program ID. When supplied, the condition is also checked
            against

            the program: tier key references (participant.tiers.<key>) are
            validated,

            and counter/tag/attribute key references that no rule in the program
            writes

            produce non-blocking warnings. Without it, only tier fields,
            participant

            roots, and deprecation advisories are checked.
          example: 550e8400-e29b-41d4-a716-446655440001
          format: uuid
          type: string
        rule_id:
          description: >-
            Optional ID of the rule being edited. When validating an edit, pass
            it so the

            rule's own persisted actions are excluded from the program
            vocabulary (the

            stale copy must not mask a renamed key) — matching the behavior on
            save.

            Omit when validating a brand-new rule.
          example: 550e8400-e29b-41d4-a716-446655440002
          format: uuid
          type: string
      required:
        - condition
      type: object
    handlers.ValidateConditionResponse:
      properties:
        message:
          description: Human-readable validation result or error detail
          example: CEL condition is valid
          type: string
        note:
          description: Additional context about validation scope (present only on success)
          example: >-
            Validation checks syntax only. Field references are not verified
            against an event schema.
          type: string
        valid:
          description: Whether the condition is syntactically valid
          example: true
          type: boolean
        warnings:
          description: >-
            Non-blocking advisories — e.g. a counter/tag/attribute key
            referenced in a

            condition/action expression that no rule in the program writes
            (likely a

            typo), or a deprecated CEL alias. Never affects validity.
          items:
            $ref: '#/components/schemas/models.RuleWarning'
          type: array
      type: object
    handlers.ErrBadRequestResponse:
      additionalProperties: false
      properties:
        code:
          description: Code is the machine-readable error code
          example: bad_request
          type: string
        details:
          allOf:
            - $ref: '#/components/schemas/handlers.ErrorDetails'
          description: >-
            Details provides optional structured error context (field errors,
            etc.)
        message:
          description: Message is the human-readable error description
          example: Invalid request parameters
          type: string
      type: object
    handlers.ErrUnauthorizedResponse:
      properties:
        code:
          description: Code is the machine-readable error code
          example: unauthorized
          type: string
        details:
          allOf:
            - $ref: '#/components/schemas/handlers.ErrorDetails'
          description: Details provides optional structured error context
        message:
          description: Message is the human-readable error description
          example: Missing or invalid credentials
          type: string
      type: object
    handlers.ErrForbiddenResponse:
      properties:
        code:
          description: Code is the machine-readable error code
          example: forbidden
          type: string
        details:
          allOf:
            - $ref: '#/components/schemas/handlers.ErrorDetails'
          description: Details provides optional structured error context
        message:
          description: Message is the human-readable error description
          example: Insufficient permissions for this action
          type: string
      type: object
    handlers.ErrUnsupportedMediaTypeResponse:
      properties:
        code:
          description: Code is the machine-readable error code
          example: unsupported_media_type
          type: string
        details:
          allOf:
            - $ref: '#/components/schemas/handlers.ErrorDetails'
          description: Details provides optional structured error context
        message:
          description: Message is the human-readable error description
          example: Content-Type must be application/json
          type: string
      type: object
    handlers.ErrInternalResponse:
      properties:
        code:
          description: Code is the machine-readable error code
          example: internal_error
          type: string
        message:
          description: Message is the human-readable error description
          example: An internal error occurred
          type: string
      type: object
    models.RuleAction:
      properties:
        allow_negative:
          description: >-
            Allow debit to overdraw and create a negative balance when funds are
            insufficient.

            DEBIT only. Defaults to false.
          example: false
          type: boolean
        amount:
          description: >-
            Static numeric string (e.g. "100") or a ${{ }} CEL expression
            returning a number (e.g. "${{ event.amount * 0.03 }}"). Legacy
            unmarked expressions remain supported for stored-rule compatibility
            but are deprecated for new rules.
          example: ${{ event.amount * 0.03 }}
          type: string
        asset_id:
          description: Asset to operate on
          example: 550e8400-e29b-41d4-a716-446655440000
          format: uuid
          type: string
        bucket:
          description: >-
            Balance bucket to target: AVAILABLE or HELD. Defaults to AVAILABLE
            for most actions, HELD for RELEASE.
          example: AVAILABLE
          type: string
        delay:
          description: >-
            How long to wait before firing the scheduled event; h/d/w units
            (e.g. "24h", "7d"). SCHEDULE_EVENT only.
          example: 24h
          type: string
        description:
          description: Context string recorded on the ledger entry
          example: Purchase reward
          type: string
        event_name:
          description: >-
            Event type to inject. For SCHEDULE_EVENT: fires after the delay
            expires. For BROADCAST: fans out to all participants immediately.
          example: check_status
          type: string
        expires_at:
          description: >-
            When the lot/tier expires. CREDIT: lot becomes unusable (LOT-mode
            assets). SET_TIER: tier assignment expires.

            Accepts an RFC3339 timestamp or a duration string with h/d/w units
            (e.g. "365d", "12w", "8760h").
          example: 365d
          format: date-time
          type: string
        key:
          description: Counter or attribute key name
          example: purchase_count
          type: string
        level:
          description: Tier level within the track (e.g. "gold", "platinum")
          example: gold
          type: string
        matures_at:
          description: >-
            When the credited lot becomes usable (CREDIT only, LOT-mode assets).
            Accepts an RFC3339 timestamp or a duration string with h/d/w units
            (e.g. "30d", "720h").
          example: 30d
          format: date-time
          type: string
        payload:
          description: >-
            Additional data included with the event when it fires. String values
            may embed ${{ }} CEL expressions

            (at any nesting depth, values only), resolved against the triggering
            rule's context when the action executes.
          type: object
        reference_id:
          description: >-
            Correlation ID for hold/release correlation and auth/settle
            reconciliation (LOT mode only).

            Static literal (e.g. "auth_12345") or ${{ }} CEL expression (e.g.
            "${{ event.authorization_id }}").
          example: ${{ event.authorization_id }}
          type: string
        reset_after:
          description: >-
            Auto-reset duration for COUNTER actions. The counter resets to 0
            after this duration elapses since the last reset; h/d/w units (e.g.
            "30d", "720h"). Send an empty string to remove auto-reset.
          example: 30d
          type: string
        tag:
          description: Tag name to add (TAG) or remove (UNTAG)
          example: VIP
          type: string
        target:
          allOf:
            - $ref: '#/components/schemas/models.ActionTarget'
          description: >-
            Alternate recipient for this action. Defaults to the event's
            participant. Supported for CREDIT, DEBIT, TAG, UNTAG, COUNTER,
            SET_ATTRIBUTE, SET_TIER.
        tier:
          description: Tier track name (e.g. "status", "loyalty")
          example: status
          type: string
        type:
          description: >-
            Action kind. Valid values: CREDIT, DEBIT, HOLD, RELEASE, FORFEIT,
            VOID_HOLD, TAG, UNTAG, COUNTER, SET_ATTRIBUTE, SET_TIER,
            SCHEDULE_EVENT, BROADCAST
          example: CREDIT
          type: string
        value:
          description: >-
            COUNTER: static numeric string or ${{ }} CEL expression.
            SET_ATTRIBUTE: literal string or ${{ }} CEL expression.
          example: '1'
          type: string
      type: object
    models.RuleWarning:
      properties:
        code:
          description: >-
            Machine-readable warning category, e.g.
            RuleWarningCodeUnknownStateKey.
          example: unknown_state_key
          type: string
        key:
          description: The referenced key, as written in the condition.
          example: purchaseCnt
          type: string
        kind:
          description: >-
            State kind: StateKindCounter, StateKindTag, StateKindAttribute, or
            StateKindAlias.
          example: counter
          type: string
        message:
          description: >-
            Human-readable explanation (includes the suggestion when one
            exists).
          example: >-
            unknown counter "purchaseCnt" — no rule in this program writes it;
            did you mean "purchase_count"?
          type: string
        scope:
          description: >-
            State scope of the reference: StateScopeParticipant,
            StateScopeProgram, or StateScopeGroup.
          example: participant
          type: string
        suggestion:
          description: Closest known key, when a near-match exists. Empty otherwise.
          example: purchase_count
          type: string
      type: object
    handlers.ErrorDetails:
      description: >-
        Optional structured details about the error. Always a JSON object: a
        `fields` array for validation errors, or flat
        field/reason/expected/received properties for other input errors.
      properties:
        expected:
          description: Expected value or format (non-validation input errors)
          example: uuid
          type: string
        field:
          description: Field name that caused the error (non-validation input errors)
          example: asset_id
          type: string
        fields:
          description: >-
            Fields lists each offending input field on validation_error
            responses.
          items:
            $ref: '#/components/schemas/handlers.ErrorFieldDetail'
          type: array
        reason:
          description: Machine-readable reason code (non-validation input errors)
          example: invalid
          type: string
        received:
          description: Value that was received (non-validation input errors)
          example: not-a-uuid
          type: string
      type: object
    models.ActionTarget:
      properties:
        external_id:
          description: >-
            ExternalID is a ${{ }} CEL expression that evaluates to an
            external_id string.

            Cannot be combined with ID or ParticipantID.

            Example: "${{ event.referrer_id }}" where referrer_id contains an
            external_id like "user_123"
          example: ${{ event.referrer_id }}
          type: string
        id:
          description: >-
            ID is the public entity ID: participant_id for PARTICIPANT, group_id
            for GROUP.
          example: 550e8400-e29b-41d4-a716-446655440000
          format: uuid
          type: string
        participant_id:
          description: >-
            ParticipantID is a ${{ }} CEL expression that evaluates to a
            participant UUID string.

            Cannot be combined with ID or ExternalID.

            Example: "${{ event.recipient_id }}" where recipient_id contains a
            participant UUID
          example: ${{ event.recipient_id }}
          type: string
        type:
          description: >-
            Type is the entity type: PARTICIPANT, GROUP, or PROGRAM.

            Optional when using id or dynamic ID fields (defaults to
            PARTICIPANT).
          example: PARTICIPANT
          type: string
      type: object
    handlers.ErrorFieldDetail:
      description: >-
        A single field-level error: which input field failed, why, and (where
        known) the expected and received values.
      properties:
        expected:
          description: >-
            Expected value or format. Usually a string; for "one of" constraints
            it

            is an array of the allowed values.
        field:
          description: Field name that caused the error
          example: amount
          type: string
        message:
          description: Human-readable explanation (validation errors only)
          example: This field is required
          type: string
        reason:
          description: Machine-readable reason code
          example: required
          type: string
        received:
          description: Value that was received
          example: '-10.00'
          type: string
      type: object
  securitySchemes:
    ApiKeyAuth:
      description: API key passed in the X-API-Key header.
      in: header
      name: X-API-Key
      type: apiKey
    BearerAuth:
      description: Bearer token passed in the Authorization header (e.g. "Bearer sk_...").
      in: header
      name: Authorization
      type: apiKey

````