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

# Author a rule

> **Beta.** The request and response shapes may still change.

Author a rule: a name and a condition over the signals Prelude resolves for an evaluation. The expression is compiled before it is stored, so a rule that cannot compile is refused here rather than failing silently at evaluation time.

A rule on its own scores nothing. Add it to a recipe, with a weight, for it to take part in an evaluation.

A rule is a name and a condition. The condition is a [CEL](https://cel.dev) expression over what Prelude resolves for an evaluation, plus the values a recipe supplies, and it must return a boolean.

```json theme={null}
{
  "name": "free_plan_signup",
  "expression": "attr.plan_tier == \"free\""
}
```

The expression is compiled before it is stored, so a typo in a name is refused here rather than turning into a rule that never fires. The error reports the offending identifier:

```json theme={null}
{
  "code": "invalid_rule",
  "details": [
    {
      "path": "expression",
      "message": "The expression does not compile: ERROR: <input>:1:1: undeclared reference to 'attr.plan_teir'"
    }
  ]
}
```

See [Rules, recipes and flows](/watch/v2/documentation/rules-recipes-flows#writing-an-expression) for the language and the `attr.` and `param.` namespaces. The Prelude signal vocabulary is not published yet - [contact us](mailto:support@prelude.so) for the names available to your rules.

## A rule on its own scores nothing

Creating a rule does not put it into service. It takes part in an evaluation once a [recipe](/watch/v2/api-reference/management/recipes/create-recipe) associates it and gives it a weight, and that recipe is referenced by a [flow](/watch/v2/api-reference/management/flows/create-flow).

## Rules bound to a recipe

An expression referencing `attr.plan_tier` or `param.blocked_countries` compiles here against those names declared as strings, which is how a recipe would declare them - string is the only type either supports. What is **not** checked here is whether any recipe declares them at all: that is the recipe's check, since it is the recipe that declares them.

So a rule reading `attr.` or `param.` names is usable only by recipes declaring them. Adding it to one that doesn't is refused at recipe save time, against that rule's entry.


## OpenAPI

````yaml post /v2/watch/management/rules
openapi: 3.1.0
info:
  title: Prelude API
  version: 2.0.0
  description: The Prelude API allows you to send messages to your users.
  contact:
    email: support@prelude.so
servers:
  - url: https://api.prelude.dev
    description: Production server
security:
  - apiToken: []
tags:
  - name: Notify
    description: Send transactional and marketing messages with compliance enforcement.
  - name: Transactional
    description: Send transactional messages (deprecated - use Notify API instead).
  - name: Verify
    description: Verify phone numbers.
  - name: Watch
    description: Evaluate email addresses and phone numbers for trustworthiness.
  - name: Lookup
    description: >-
      Retrieve detailed information about a phone number including carrier data,
      line type, and portability status.
paths:
  /v2/watch/management/rules:
    post:
      tags:
        - Watch
      summary: Create a rule
      description: >-
        **Beta.** The request and response shapes may still change.


        Author a rule: a name and a condition over the signals Prelude resolves
        for an evaluation. The expression is compiled before it is stored, so a
        rule that cannot compile is refused here rather than failing silently at
        evaluation time.


        A rule on its own scores nothing. Add it to a recipe, with a weight, for
        it to take part in an evaluation.
      operationId: createWatchRule
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WatchRuleRequest'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WatchRule'
        '400':
          description: KO
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
                example:
                  code: invalid_rule
                  message: >-
                    The provided rule is invalid. See `details` field for more
                    information.
                  type: bad_request
                  param: rule
                  details:
                    - path: expression
                      message: >-
                        The expression does not compile: ERROR: <input>:1:1:
                        undeclared reference to 'is_vpnn'
                  request_id: 3d19215e-2991-4a05-a41a-527314e6ff6a
components:
  schemas:
    WatchRuleRequest:
      type: object
      properties:
        name:
          type: string
          maxLength: 128
          description: >-
            A label for the rule. It is returned in evaluation results, so make
            it say what the rule looks for.
          examples:
            - free_plan_signup
        expression:
          type: string
          description: >
            The condition, written in [CEL](https://cel.dev). It must evaluate
            to a boolean, and it is compiled before it is stored: an expression
            that does not compile is refused.


            It may reference the signals Prelude resolves for an evaluation, a
            recipe's request attributes as `attr.<key>`, and a recipe's
            configured values as `param.<key>`. A rule that references either
            namespace is bound to the recipes declaring those names, and can
            only be used by them. See [Rules, recipes and
            flows](/watch/v2/documentation/rules-recipes-flows#writing-an-expression);
            the signal vocabulary is not published yet, so contact us for the
            names available to your rules.
          examples:
            - attr.plan_tier == "free"
      required:
        - name
        - expression
    WatchRule:
      type: object
      properties:
        id:
          type: string
          examples:
            - rul_01jc0t6fwwfgfsq1md24mhyztj
          description: >-
            The rule identifier. Reference it in a recipe's `rules` to score
            over it.
        name:
          type: string
          examples:
            - free_plan_signup
        expression:
          type: string
          examples:
            - attr.plan_tier == "free"
        type:
          type: string
          enum:
            - CUSTOM
          description: >-
            Always `CUSTOM`. A rule you author is your own; Prelude's are a
            separate, read-only listing.
      required:
        - id
        - name
        - expression
        - type
    Error:
      type: object
      properties:
        code:
          type: string
          description: The error code.
          examples:
            - invalid_phone_number
        message:
          type: string
          examples:
            - >-
              The provided phone number is invalid. Provide a valid E.164 phone
              number.
          description: A human-readable message describing the error.
        type:
          type: string
          examples:
            - bad_request
          description: The error type.
        request_id:
          type: string
          examples:
            - 3d19215e-2991-4a05-a41a-527314e6ff6a
          description: >-
            A string that identifies this specific request. Report it back to us
            to help us diagnose your issues.
        param:
          type: string
          examples:
            - phone_number
          description: The parameter the error refers to, when it applies to a single one.
      required:
        - code
        - message
        - type
  securitySchemes:
    apiToken:
      type: http
      scheme: bearer

````