# Content rules

Source: https://postgale.com/docs/content-rules

Deterministic checks on what agents write. Ban words, limit links and mentions, require a disclosure or keep posts inside publishing hours, per organization, account, API key or connected app. A match holds the write for a person or refuses it.

Content rules are the plain, predictable part of Postgale's safety layer. A person writes them once in the dashboard
(**Content rules**), and every post, edit, reply, message or other text an agent sends is checked against them first. There is no
AI in the check: the same text always gets the same answer.

## Review or block

Each rule decides what happens when it matches:

| Action | What happens |
| --- | --- |
| `review` | The write is held in the approval queue for an owner or admin, even if approval is off for the organization, account and key. Posts become `pending_approval` (the `post.pending_approval` webhook carries `reason: "content_rule"` and the hits); other actions become a queued action (`202`). |
| `block` | The write is refused with `422 rule_violation`. Nothing is stored or sent. |

When several rules match, **block wins over review**.

A blocked write returns every matching rule and why, so the agent can fix its own output and try again:

```json
{
  "error": {
    "message": "Blocked by content rule \"No prices\": banned term \"gratis\"",
    "code": "rule_violation",
    "rules": [
      { "ruleId": "cm...", "name": "No prices", "kind": "banned_terms", "action": "block", "detail": "banned term \"gratis\"" }
    ]
  }
}
```

Over MCP the tool error lists the same rules in text.

## Scopes

A rule applies to one of:

- the **organization** (everything an agent writes),
- one **account** (anything written to that account),
- one **API key**,
- one **connected app** (an OAuth sign-in such as ChatGPT or Claude).

All rules in scope are evaluated together: an organization rule, the rules of the accounts a post targets and the rules of the
key or app that sends it. Disabled rules are skipped.

## Rule kinds

| Kind | Config | Matches when |
| --- | --- | --- |
| `banned_terms` | `{"terms": ["gratis", "free money"]}` | A term appears, case-insensitive. Single words match whole words only (`gratis` does not match `gratisfying`); phrases match as text. |
| `link_domains` | `{"allow": ["example.com"]}` | The text has a link (`http://`, `https://` or a bare `www.`) to a domain that is not listed or a subdomain of one. An empty `allow` makes any link match. |
| `mentions` | `{}` or `{"allow": ["ourbrand"]}` | The text has an `@handle` that is not in `allow`. |
| `topics` | `{"patterns": ["\\bguaranteed\\s+returns?\\b"]}` | A regular expression (case-insensitive) matches. Up to 20 patterns of 200 characters; nested repeats such as `(a+)+` and backreferences are rejected when you save, and only the first 10,000 characters are tested. |
| `schedule_window` | `{"timezone": "Europe/Madrid", "days": [1,2,3,4,5], "from": "09:00", "to": "18:00"}` | The time the post will publish (`scheduledFor`, or now) is outside the window. Days are 0 (Sunday) to 6; `from` later than `to` means overnight. |
| `required_text` | `{"text": "#ad", "when": "sponsored"}` | The text does not contain `text` (case-insensitive). With `when` (a regular expression), only texts that match it are checked. |

Configs are validated when saved: a bad pattern, an unknown time zone or a malformed list is rejected with a message saying why.

## Where rules are checked

- `POST /posts` and the MCP `create_post` and bulk uploads: the post text and its publish time.
- `PATCH /posts/{id}` and `update_post`: the new text. A `review` match sends a changed scheduled or failed post back to the queue.
- Agent actions that carry text: comment replies, direct messages, WhatsApp and Discord messages, review and mention replies, published-post edits, LinkedIn posts, comments and notes. A `review` match queues the action even when approval is off; see [Approval](https://postgale.com/docs/api/authentication#approval).

Rules apply to **agent writes only** (API keys and connected apps). People using the dashboard are not checked in this version.

## Reading the rules

Agents can read, but not change, the rules that apply to them: `GET /rules` and the MCP tool `list_content_rules`. Writing the
rules is a dashboard action for owners and admins; members can read them.
