REST Lens
← Back to Documentation

Custom Rules

Define organization-specific rules to enforce your API design standards

REST Lens allows organizations to define their own API evaluation rules beyond the built-in rule set. Custom rules let you enforce organization-specific standards and import existing Spectral rulesets.

Overview

Custom rules are defined at the organization level and can be used across all projects within that organization. Each rule uses REST Lens YAML format to define what to check and how to report violations.

Creating Custom Rules

Navigate to your organization's Custom Rules page from the sidebar. Click New Rule to create a new custom rule.

Rule Structure

Each custom rule consists of:

  • Name: A human-readable name for the rule
  • Slug: A unique identifier (auto-generated from the name)
  • Description: What the rule checks for
  • Severity: error, warning, or info
  • YAML Content: The rule definition

REST Lens YAML Format

Basic Structure

slug: rule-id-in-kebab-case
name: Human Readable Name
description: What this rule checks for
severity: error|warning|info

target: paths|operations|responses|parameters|schemas|headers|tags|info

filter:  # Optional - restrict which items to check
  method: GET|POST|PUT|DELETE|PATCH
  pattern: "regex"
  status: "200|201|4XX"
  in: path|query|header|cookie

when:  # Optional - only check if condition passes
  - field: fieldName
    matches: "regex"

check:  # Validate a field on the CURRENT element ("does this have X?")
  field: fieldName
  matches: "regex"

require:  # Validate RELATED elements exist ("does Y exist for this?")
  responses:
    has: "201"  # Operation must have this response (supports wildcards: "2XX", "4XX")
    hasAnyOf: ["200", "201", "2XX"]  # At least one must exist
    hasOneOf: ["200", "201"]  # Exactly one must exist
  headers:
    has: "Location"  # Response must have this header
  siblings:  # Path must have sibling operation
    - method: GET
  parameters:  # Operation must have this parameter
    has:
      name: "id"
      in: path

consistent:  # Cross-spec consistency
  group_by: [status, content_type]
  field: schema

message: "Error message with {{path}}, {{method}}, {{status}}, {{segment}}, {{name}} placeholders"

Targets

TargetDescription
pathsIterates over all path segments
operationsIterates over all operations (GET /users, POST /users, etc.)
responsesIterates over all response definitions
parametersIterates over all parameter definitions
schemasIterates over all schema definitions
headersIterates over all header definitions
tagsIterates over all tags defined in the spec
infoEvaluates the API info object (title, version, description, etc.)

Check Operators

OperatorDescription
existsField exists (not null/undefined)
notExistsField does NOT exist (null/undefined)
emptyField is null or empty
notEmptyField is NOT null and NOT empty
matchesRegex pattern match
notMatchesRegex pattern does NOT match
equalsExact equality
notEqualsValue does NOT equal
containsSubstring match
notContainsSubstring NOT found
startsWithString starts with value
endsWithString ends with value
oneOfValue is in list
notOneOfValue is NOT in list
includesArray contains element
notIncludesArray does NOT contain element
everyAll array elements match regex pattern
someAt least one array element matches regex pattern
lengthMinMinimum string/array length
lengthMaxMaximum string/array length
lengthEqualsExact length
alphabeticalArray is alphabetically sorted
uniqueAll values in array are unique (no duplicates)
xorExactly one of specified fields must exist

Tip: Operators come in pairs (e.g., exists/notExists, matches/notMatches). Using the negated operator is equivalent to using the base operator with negate: true.

To negate any operator, add negate: true:

check:
  field: description
  exists: true
  negate: true  # Field must NOT exist

Pre-Filtering Arrays with where

Use where to pre-filter array elements before applying the operator. Supports two forms:

Regex (string) — filters by element text, or by name field on object arrays:

# At least 2 error responses (4xx or 5xx)
check:
  field: responses
  where: "^[45]"
  lengthMin: 2

Property match (mapping) — filters object arrays where all specified properties match exactly:

# At most 5 query parameters
check:
  field: parameters
  where:
    in: query
  lengthMax: 5

The where filter reduces the array first, then the operator applies to the filtered result. Works with all array operators (lengthMin, lengthMax, includes, every, some, etc.).

Boolean Combinators

Combine multiple conditions with anyOf, allOf, and not in both check and when blocks:

CombinatorDescription
anyOfAt least one condition must pass (OR)
allOfAll conditions must pass (AND) — same as listing multiple conditions
notInner condition must fail (NOT)
# Description OR summary must exist
check:
  anyOf:
    - field: description
      notEmpty: true
    - field: summary
      notEmpty: true

Combinators can be nested:

check:
  allOf:
    - not:
        field: deprecated
        equals: true
    - anyOf:
        - field: description
          notEmpty: true
        - field: summary
          notEmpty: true

Conditional Branching (if/then/else)

Apply different checks based on a condition:

# POST needs description, others need summary
check:
  if:
    field: method
    equals: "post"
  then:
    field: description
    notEmpty: true
  else:
    field: summary
    notEmpty: true
  • then is required when if is present
  • else is optional — when omitted and the if condition fails, the check passes

Filter Options

Filters restrict which elements to check:

filter:
  method: POST  # For operations: GET, POST, PUT, DELETE, PATCH
  pattern: "^/api/v[0-9]+"  # For paths: regex pattern
  status: "2XX"  # For responses: exact or wildcard (2XX, 4XX, 5XX)
  in: query  # For parameters: path, query, header, cookie
  isParameter: true  # For paths: true matches {param} segments, false matches static segments
  content_type: "application/json"  # For responses/operations: check content type
FilterTargetDescription
methodoperationsHTTP method (GET, POST, PUT, DELETE, PATCH)
patternpathsRegex pattern to match path
statusresponsesStatus code, exact or wildcard (200, 2XX, 4XX)
inparametersParameter location (path, query, header, cookie)
isParameterpathstrue for path parameters like {id}, false for static segments
content_typeresponses, operationsMedia type (e.g., application/json)

Variables in Messages

Use placeholders in messages for dynamic context:

VariableDescription
{{path}}Full path (e.g., /users/{id})
{{segment}}Current path segment
{{method}}HTTP method (e.g., GET)
{{operation}}Operation ID (alias for {{operation_id}})
{{operation_id}}The operationId value
{{status}}Response status code (e.g., 200)
{{name}}Parameter name
{{in}}Parameter location (path, query, header, cookie)
{{tag}}Tag name (alias for {{tag_name}})
{{tag_name}}Tag name
{{version}}API version from info object (only for info target)
{{title}}API title from info object (only for info target)
{{api_description}}API description from info object (only for info target)

Accessible Fields

Fields you can access with check.field depend on the target type:

paths

  • segment - Current path segment
  • path - Full path
  • key - Same as segment
  • variable_for - True if segment is a path parameter

operations

  • summary - Operation summary
  • description - Operation description
  • responses - List of response status codes (array, e.g. ["200", "404"])
  • parameters - List of parameter objects (array of {name, in, required, description?})
  • tags - Operation tags (array)
  • trailing_slash - Whether path has trailing slash
  • request_body / requestBody - The request body object
  • deprecated - Whether operation is deprecated (boolean)

responses

  • description - Response description
  • content - Content types and schemas
  • headers - Response header objects (array of {name, description?})

parameters

  • name - Parameter name
  • in - Location (path, query, header, cookie)
  • required - Whether required
  • description - Parameter description
  • schema - Parameter schema

schemas

  • type - Schema type (string, object, array, etc.)
  • enum - Enum values array
  • properties - Object properties
  • format - Format (date, uuid, email, etc.)
  • items - Array item schema
  • any_of - anyOf schemas
  • default - Default value
  • title - Schema title
  • ref - $ref reference
  • additional_properties - Additional properties schema

headers

  • description - Header description
  • schema - Header schema

tags

  • name - Tag name
  • description - Tag description

info

  • title - API title
  • description - API description
  • version - API version
  • Any other property in the info object

check vs require

checkrequire
PurposeValidate a field on the current elementValidate that related elements exist
Question"Does THIS element have X?""Does related element Y exist?"
Example"Does this operation have a description?""Does this operation have a 201 response?"

Relational Checks (require)

The require block validates relationships between API elements.

require.responses

Check that an operation has specific responses. Supports wildcards (2XX, 4XX, 5XX):

slug: post-returns-201
name: POST Should Return 201
description: POST operations should return 201 for creation
severity: warning
target: operations
filter:
  method: POST
require:
  responses:
    has: "201"  # Or use "2XX" for any 2xx response
message: "POST {{path}} should return 201 for successful creation"

Operators:

  • has: Single status code or wildcard (e.g., "201", "2XX")
  • hasAnyOf: At least one must exist (e.g., ["200", "201", "2XX"])
  • hasOneOf: Exactly one must exist - not zero, not multiple (e.g., ["200", "201"])
# Example: Operation must have at least one success response
require:
  responses:
    hasAnyOf: ["200", "201", "204"]

# Example: Operation must have exactly one of 200 or 201 (not both)
require:
  responses:
    hasOneOf: ["200", "201"]

require.headers

Check that a response includes specific headers:

slug: 201-has-location
name: 201 Must Have Location Header
description: 201 responses should include Location header
severity: error
target: responses
filter:
  status: "201"
require:
  headers:
    has: "Location"
message: "201 response at {{path}} should include Location header"

require.siblings

Check that a path has sibling operations (e.g., if you have POST, you should also have GET):

slug: post-needs-get
name: POST Paths Need GET
description: Paths with POST should also have GET for listing
severity: info
target: operations
filter:
  method: POST
require:
  siblings:
    - method: GET
message: "{{path}} has POST but no GET operation for listing resources"

require.parameters

Check that an operation has specific parameters:

slug: collection-needs-pagination
name: Collection Endpoints Need Pagination
description: GET collection endpoints should have pagination parameters
severity: warning
target: operations
filter:
  method: GET
when:
  - field: path
    matches: "^/[^{]+$"  # Paths without path params (likely collections)
require:
  parameters:
    has:
      name: "limit"
      in: query
message: "GET {{path}} should have pagination parameter 'limit'"

Conditional Checks (when)

The when block runs the check only if conditions are met:

slug: versioned-paths-need-header
name: Versioned APIs Need Accept Header
description: APIs with version in path should document Accept header
severity: info
target: operations
when:
  - field: path
    matches: "/v[0-9]+/"
require:
  parameters:
    has:
      name: "Accept"
      in: header
message: "{{method}} {{path}} is versioned but doesn't document Accept header"

Consistency Checks

Check for consistency across similar elements:

slug: consistent-error-schema
name: Consistent Error Responses
description: All error responses should use the same schema
severity: warning
target: responses
filter:
  status: "4XX"
consistent:
  group_by: [status]
  field: schema
message: "Error response schemas should be consistent"

More Examples

Enforce kebab-case in paths

slug: kebab-case-paths
name: Kebab Case Paths
description: Path segments must use kebab-case
severity: error
target: paths
check:
  field: segment
  matches: "^[a-z0-9]+(-[a-z0-9]+)*$|^\\{[a-zA-Z]+\\}$"
message: "Path segment '{{segment}}' should use kebab-case"

204 responses must be empty

slug: 204-no-content
name: 204 Must Be Empty
description: 204 No Content responses should not define content
severity: error
target: responses
filter:
  status: "204"
check:
  field: content
  empty: true
message: "204 response at {{path}} should not have content"

Require operation descriptions

slug: require-operation-description
name: Require Operation Description
description: All operations must have a description
severity: warning
target: operations
check:
  field: description
  exists: true
message: "{{method}} {{path}} is missing a description"

DELETE should return 204

slug: delete-returns-204
name: DELETE Should Return 204
description: DELETE operations should return 204 on success
severity: info
target: operations
filter:
  method: DELETE
require:
  responses:
    has: "204"
message: "DELETE {{path}} should return 204 for successful deletion"

Path parameters should be documented

slug: document-path-params
name: Document Path Parameters
description: Path parameters must have a description
severity: warning
target: parameters
filter:
  in: path
check:
  field: description
  exists: true
message: "Path parameter '{{name}}' is missing a description"

Tags must have descriptions

slug: tag-description-required
name: Tag Description Required
description: All tags must have a description
severity: warning
target: tags
check:
  field: description
  exists: true
message: "Tag '{{tag}}' is missing a description"

Path parameters should use camelCase

slug: path-param-camel-case
name: Path Parameters Must Be camelCase
description: Path parameter names should use camelCase
severity: error
target: paths
filter:
  isParameter: true  # Only check path parameter segments like {userId}
check:
  field: segment
  matches: "^\\{[a-z][a-zA-Z0-9]*\\}$"
message: "Path parameter '{{segment}}' should use camelCase"

Enum values must be unique

slug: unique-enum-values
name: Unique Enum Values
description: Enum arrays should not contain duplicate values
severity: error
target: schemas
check:
  field: enum
  unique: true
message: "Schema enum contains duplicate values"

API must have contact info

slug: api-contact-required
name: API Contact Required
description: API info must include contact information
severity: info
target: info
check:
  field: contact
  exists: true
message: "API info should include contact information"

API title must not be empty

slug: api-title-not-empty
name: API Title Required
description: API must have a non-empty title
severity: error
target: info
check:
  field: title
  empty: true
  negate: true  # Must NOT be empty
message: "API title must not be empty"

API version must follow semver

slug: api-version-semver
name: API Version Must Be Semver
description: API version must follow semantic versioning
severity: warning
target: info
check:
  field: version
  matches: "^[0-9]+\\.[0-9]+\\.[0-9]+(-[a-zA-Z0-9]+)?$"
message: "API version '{{version}}' must follow semantic versioning (e.g., 1.0.0)"

No deprecated operations allowed

slug: no-deprecated-operations
name: No Deprecated Operations
description: Deprecated operations should be removed
severity: warning
target: operations
check:
  field: deprecated
  equals: true
  negate: true  # deprecated must NOT be true
message: "{{method}} {{path}} is deprecated and should be removed"

Object schemas should have properties

slug: object-needs-properties
name: Object Schemas Need Properties
description: Object type schemas should define their properties
severity: warning
target: schemas
when:
  - field: type
    equals: object
check:
  field: properties
  exists: true
message: "Schema '{{segment}}' is type 'object' but has no properties defined"

JSON responses only

slug: json-responses-only
name: Responses Must Be JSON
description: All responses should use application/json content type
severity: error
target: responses
filter:
  content_type: "application/json"
check:
  field: content
  exists: true
message: "Response {{status}} at {{path}} should use application/json"

AI-Powered Rule Generation

REST Lens can generate custom rules from natural language descriptions using AI. This feature helps you quickly create rules without manually writing YAML.

How to Use

  1. Navigate to Custom Rules page
  2. Click New Rule
  3. Click Generate with AI
  4. Describe what you want to check in plain English
  5. Review and edit the generated YAML
  6. Save the rule

Example Prompts

  • "Check that all POST operations return a 201 status code"
  • "Ensure all path parameters have descriptions"
  • "Verify that DELETE operations return 204"
  • "Make sure error responses use consistent schema"

Rate Limits

AI rule generation is rate limited to 20 requests per minute per organization.

Notes

  • Generated rules may need minor adjustments
  • Always test generated rules before enabling organization-wide
  • The AI uses AWS Bedrock (Claude 3 Haiku) for generation

Importing Spectral Rulesets

REST Lens supports importing rules from Spectral rulesets. Click Import Spectral on the Custom Rules page and paste your Spectral YAML configuration.

Supported Spectral Features

  • Basic rule definitions
  • JSONPath targeting
  • Pattern matching functions
  • Common Spectral functions (truthy, falsy, pattern, etc.)

Translation Limitations

Not all Spectral rules can be fully translated to REST Lens format. Complex JSONPath expressions or custom functions may require manual adjustment. The import process will flag partially translated rules for review.

Example Import

rules:
  operation-description:
    description: Operations must have a description
    given: "$.paths[*][*]"
    severity: warn
    then:
      field: description
      function: truthy

Git Integration

REST Lens provides git access for managing custom rules, allowing you to use your favorite editor, version control, and CI/CD workflows.

Overview

Each organization has a virtual git repository for custom rules at:

https://restlens.com/git/{org-slug}/rules.git

You can clone this repository, edit rule files locally, and push changes back to sync with REST Lens.

Setting Up Git Access

  1. Navigate to Organization Settings
  2. Find the Git Access section
  3. Click Create Token to generate an access token
  4. Choose permissions:
    • Read & Write: Clone and push changes
    • Read Only: Clone only (for CI/CD verification)

Cloning the Repository

# Clone the rules repository
git clone https://restlens.com/git/{org-slug}/rules.git

# When prompted for credentials:
# Username: anything (e.g., "git")
# Password: your git access token

Repository Structure

rules/
├── my-rule.yaml         # filename becomes the rule slug
├── kebab-case-paths.yaml
└── require-description.yaml
README.md

Rule File Format

Each YAML file in the rules/ directory represents a custom rule:

slug: my-rule
name: My Custom Rule
description: What this rule checks for
severity: error  # or warning, info
enabled: true    # optional, defaults to true

target: operations

check:
  field: description
  exists: true

message: "{{method}} {{path}} is missing a description"

The slug field in the file must match the filename (without .yaml).

Pushing Changes

cd rules

# Create or edit a rule
vim rules/my-new-rule.yaml

# Commit and push
git add .
git commit -m "Add my-new-rule"
git push

Changes are applied immediately when pushed. The push will fail if:

  • YAML syntax is invalid
  • Rule validation fails (missing required fields, invalid operators)
  • Slug doesn't match filename

Git Workflow Benefits

  • Version Control: Track rule changes over time with full git history
  • Code Review: Use pull requests to review rule changes before deployment
  • CI/CD Integration: Automate rule validation and deployment
  • Team Collaboration: Multiple team members can work on rules simultaneously
  • Backup: Git repository serves as a backup of all custom rules
  • Offline Editing: Work on rules without internet, push when ready

Token Management

Tokens can be managed in Organization Settings:

  • Create Token: Generate new tokens with descriptive names
  • Revoke Token: Delete tokens that are no longer needed
  • Track Usage: See when each token was last used

Best practices:

  • Create separate tokens for different use cases (CI/CD, local dev)
  • Use read-only tokens for CI/CD verification jobs
  • Rotate tokens periodically
  • Revoke tokens when team members leave

Managing Rules

Enable/Disable Rules

Toggle rules on or off without deleting them. Disabled rules won't be evaluated.

Lock Settings

Organization admins can lock rule settings to prevent project-level overrides:

  • Enabled Lock: Projects cannot disable the rule
  • Severity Lock: Projects cannot change the severity level

Project Overrides

By default, projects inherit organization rule settings. Unless locked, project admins can:

  • Disable specific rules for their project
  • Change severity levels to match project requirements

Permissions & Limits

Who Can Create Rules

Only organization owners and admins can create, edit, or delete custom rules. Organization members have read-only access.

Rule Limits

Each organization can have up to 100 custom rules. This includes both manually created rules and imported Spectral rules.

Rate Limits

EndpointLimit
Create/Update/Delete5 requests per hour
Validate YAML30 requests per minute
Import Spectral5 requests per hour
Generate with AI20 requests per minute

Best Practices

  1. Start with warnings: Set new rules to warning severity initially to assess impact
  2. Use clear messages: Include actionable guidance in violation messages with placeholders
  3. Test incrementally: Enable rules one at a time to identify issues
  4. Document conventions: Add descriptions explaining why the rule exists
  5. Review before locking: Ensure rules work correctly before locking at org level
  6. Use filters wisely: Narrow down targets with filters to avoid false positives
  7. Keep rules focused: Each rule should check one specific concern
  8. Test with sample specs: Validate rules against sample specifications before enabling organization-wide
  9. Version control: Consider maintaining rule definitions in version control for team collaboration
  10. Monitor effectiveness: Review rule violations regularly to ensure rules remain relevant