---
title: "Evaluate rule sets against content, a CMS post, or raw text"
method: POST
path: "/rules/evaluate"
tags: ["Rules"]
---

# Evaluate rule sets against content, a CMS post, or raw text

`POST /rules/evaluate`

## Request

- Base URL: `https://next.frase.io/api/v1`
- URL: `https://next.frase.io/api/v1/rules/evaluate`
- Auth: API key in header `X-API-KEY`

Run content rules against exactly one subject: an existing Frase content row (`content_id`), a FraseCMS post (`post_id`), or raw text (`content`). Omit `rule_set_id` to evaluate every enabled rule set (org-wide plus the subject's site), matching the in-app agent. This is a **read:content** endpoint: results are never persisted (the `save_results` field is accepted for back-compat but ignored). Provide `target_keywords` (post/raw subjects) to compute real SEO/GEO/EEAT scores for score_threshold rules; without it those rules report `skipped` rather than failing.

## Request body

- object
  - `content_id` string, uuid: Subject: an existing Frase content row.
  - `post_id` string, uuid: Subject: a FraseCMS post.
  - `content` object: Subject: raw text (e.g. a pre-publish preflight).
    - `title` string
    - `body` string, required: Required. Sanitized exactly as a publish would before evaluation.
    - `seo_title` string
    - `seo_description` string
  - `rule_set_id` string, uuid: Optional. Omit to evaluate every enabled rule set in scope.
  - `site_id` string, uuid: Optional. Scopes site-scoped rule sets and score computation.
  - `target_keywords` string[]: Optional (post/raw subjects). Enables real score computation for score_threshold rules.
  - `save_results` boolean: Accepted for back-compat but IGNORED — this read-scope endpoint never persists.

## Response `200`

Evaluation result

- object
  - `data` object
    - `content_id` string, uuid: Echoed when the subject was a content row.
    - `post_id` string, uuid: Echoed when the subject was a FraseCMS post.
    - `overall_status` 'pass' | 'warning' | 'fail'
    - `no_rules_configured` boolean: true when no rule set applied — a 'pass' here means governance was never set up, not that anything was checked.
    - `site_scoped_sets_skipped` integer: Count of site-scoped rule sets not evaluated (no site in scope). A verdict with this > 0 is weaker than the publish gate.
    - `rule_set_results` object[]
      - `rule_set_id` string, uuid
      - `rule_set_name` string
      - `overall_status` 'pass' | 'warning' | 'fail'
      - `summary` unknown
      - `results` object[]
        - `rule_id` string, uuid
        - `rule_name` string
        - `condition_type` string
        - `severity` 'error' | 'warning' | 'info'
        - `passed` boolean
        - `skipped` boolean: The rule could not run (e.g. a score_threshold rule with no target_keywords). A skipped rule is neither a pass nor a failure — never read it as a genuine pass.
        - `value` unknown
        - `threshold` unknown
        - `message` string, nullable
        - `fix_hint` string, nullable
  - `request_id` string

## Other responses

- `400`: Validation failed
- `401`: Authentication failed
- `404`: Resource not found
- `429`: Rate limited, or the org's monthly brief limit blocked minting the refreshed brief (only the FIRST supersession of a brief is billing-exempt; repeats meter normally)

---

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