# Rules

> The Rules operations of the Hub REST API (4 operations).

URL: https://docs.synap.live/docs/reference/hub-api/rules · Generated from Synap-core/backend@c31e261ab (main)

Paths are relative to `/api/hub` on a pod.

## POST /rules/classify

Classify a natural-language rule

Routes a STATEMENT to the shapes it implies (fact · behaviour · structure · schedule · notification · extraction · unknown), each with a confidence and the literal cues that fired. Also reports `oneShot` (the text reads as a one-off request, not a standing rule) and `needsClarification` when the routing is undecidable. READ-ONLY: no writes, no side effects, no I/O.

Request body (`application/json`): object

| Field | Type | Required | Description |
|---|---|---|---|
| `text` | string | yes |  |
| `context` | object |  |  |

Responses:

- `200`: Intent route
- `400`: Bad request
- `403`: Forbidden

## POST /rules/parse

Parse typed text into a rule sentence

Turns a typed rule into a structured WHEN / ONLY IF / THEN sentence built from the caller's own trigger, action and kind menus. The shared deterministic matcher runs first; the intelligence service is asked only for the halves it left open, and may only pick from those menus. The sentence is validated with the rule compiler (`validation`). Open slots come back in `unresolved`. NEVER saves — create with `POST /rules`. An unreachable assistant returns the matcher's reading with `aiUnavailable: true`.

Request body (`application/json`): object

| Field | Type | Required | Description |
|---|---|---|---|
| `text` | string | yes |  |
| `workspaceId` | string |  |  |

Responses:

- `200`: Parsed sentence
- `400`: Bad request
- `403`: Forbidden
- `429`: Rate limited
- `500`: Internal error

## GET /rules

List rules

Rules visible to the caller, newest first. Floored by the canonical `visibleSkillsWhere` predicate. Every row carries `status`: `"active"` for a materialized rule. With `includeProposed=true` the list ALSO contains rules that exist only as a pending proposal — `status:"proposed"` + the `proposalId` to review them (`synap open proposal <id>`) — floored by `proposals.list`'s own `userVisibleWhere` predicate plus the rule scope tiers. Default OFF, so a caller reading "what is in effect" is unaffected.

Parameters:

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `workspaceId` | query | string |  |  |
| `limit` | query | number |  |  |
| `offset` | query | number \| null |  |  |
| `includeProposed` | query | boolean \| "0" \| "1" \| "true" \| "false" |  |  |

Responses:

- `200`: Rules
- `400`: Bad request
- `403`: Forbidden
- `500`: Internal error

## POST /rules

Create a rule (governed)

Governed create. A `{status:"proposed", proposalId}` body is a SUCCESS (HTTP 200) — the write is queued for the owner's review, exactly as an agent-authored write should be. `{status:"denied"}` is a 403.

Request body (`application/json`): object

| Field | Type | Required | Description |
|---|---|---|---|
| `intent` | string | yes |  |
| `scope` | object | yes |  |
| `expiresAt` | string |  |  |
| `factSkillId` | string |  |  |
| `automationIds` | array<string> |  |  |
| `sentence` | object |  |  |
| `agentUserId` | string |  |  |
| `userId` | string |  |  |

Responses:

- `200`: Created or proposed
- `400`: Bad request
- `403`: Forbidden / denied
- `500`: Internal error
