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:

ActionWhat happens
reviewThe 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).
blockThe 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:

{
  "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

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

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.

On this page